diff --git a/Cargo.toml b/Cargo.toml index c9b32407..4fdfee8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,34 +1,6 @@ -[package] -name = "pin-project" -# NB: When modifying, also modify html_root_url in lib.rs -version = "0.3.4" -authors = ["Taiki Endo "] -edition = "2018" -license = "Apache-2.0/MIT" -description = "An attribute that creates a projection struct covering all the fields." -repository = "https://github.com/taiki-e/pin-project" -documentation = "https://docs.rs/pin-project/" -readme = "README.md" -keywords = ["pin", "macros", "attribute"] -categories = ["rust-patterns"] -exclude = ["/.travis.yml", "/bors.toml"] +[workspace] -[badges] -travis-ci = { repository = "taiki-e/pin-project" } - -[lib] -proc-macro = true - -[features] -# Default features. -default = ["project_attr"] -# Enable to use `project` attribute. -project_attr = ["syn/visit-mut"] - -[dependencies] -proc-macro2 = "0.4.13" -quote = "0.6.8" -syn = { version = "0.15.29", features = ["full"] } - -[dev-dependencies] -compiletest = { version = "0.3.21", package = "compiletest_rs", features = ["stable", "tmp"] } +members = [ + "pin-project", + "pin-project-internal", +] diff --git a/README.md b/README.md index d687fc0f..a690d22d 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ The current version of pin-project requires Rust 1.33 or later. ## Examples -[`unsafe_project`] attribute creates a projection struct covering all the fields. +[`pin_projectable`] attribute creates a projection struct covering all the fields. ```rust -use pin_project::unsafe_project; +use pin_project::pin_projectable; use std::pin::Pin; -#[unsafe_project(Unpin)] // `(Unpin)` is optional (create the appropriate conditional Unpin implementation) +#[pin_projectable] struct Foo { #[pin] future: T, @@ -61,7 +61,7 @@ impl Foo { [Code like this will be generated](doc/struct-example-1.md) -[`unsafe_project`]: https://docs.rs/pin-project/0.3/pin_project/attr.unsafe_project.html +[`pin_projectable`]: https://docs.rs/pin-project/0.3/pin_project/attr.pin_projectable.html ## License diff --git a/pin-project-internal/Cargo.toml b/pin-project-internal/Cargo.toml new file mode 100644 index 00000000..a899616c --- /dev/null +++ b/pin-project-internal/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "pin-project-internal" +# NB: When modifying, also modify html_root_url in lib.rs +version = "0.3.4" +authors = ["Taiki Endo "] +edition = "2018" +license = "Apache-2.0/MIT" +description = "An interal crate to support pin_project - do not use directly" +repository = "https://github.com/taiki-e/pin-project" +documentation = "https://docs.rs/pin-project/" +readme = "README.md" +keywords = ["pin", "macros", "attribute"] +categories = ["rust-patterns"] +exclude = ["/.travis.yml", "/bors.toml"] + +[lib] +proc-macro = true + +[features] +# Default features. +default = ["project_attr"] +# Enable to use `project` attribute. +project_attr = ["syn/visit-mut"] +# Enable to allow using the crate with a renamed 'pin-project' dependency +renamed = ["proc-macro-crate", "serde", "lazy_static"] + + +[dependencies] +proc-macro2 = "0.4.13" +quote = "0.6.8" +syn = { version = "0.15.29", features = ["full", "extra-traits"] } +proc-macro-crate = { version = "0.1.4", optional = true } +# Required until a new toml-rs release is made with https://github.com/alexcrichton/toml-rs/pull/311, +# and proc-macro-crate updates to that new version of toml-rs. +serde = { version = "1.0.97", optional = true } +lazy_static = { version = "1.3.0", optional = true } diff --git a/doc/enum-example-1.md b/pin-project-internal/doc/enum-example-1.md similarity index 100% rename from doc/enum-example-1.md rename to pin-project-internal/doc/enum-example-1.md diff --git a/doc/struct-example-1.md b/pin-project-internal/doc/struct-example-1.md similarity index 100% rename from doc/struct-example-1.md rename to pin-project-internal/doc/struct-example-1.md diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs new file mode 100644 index 00000000..de76a08f --- /dev/null +++ b/pin-project-internal/src/lib.rs @@ -0,0 +1,50 @@ +#![recursion_limit = "256"] +#![doc(html_root_url = "https://docs.rs/pin-project/0.3.3")] +#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn(unsafe_code)] +#![warn(rust_2018_idioms, unreachable_pub)] +#![warn(single_use_lifetimes)] +#![warn(clippy::all, clippy::pedantic)] +#![warn(clippy::nursery)] + +extern crate proc_macro; + +#[macro_use] +mod utils; + +mod pin_projectable; +#[cfg(feature = "project_attr")] +mod project; + +use proc_macro::TokenStream; + +#[cfg(feature = "project_attr")] +#[proc_macro_attribute] +pub fn project(args: TokenStream, input: TokenStream) -> TokenStream { + assert!(args.is_empty()); + TokenStream::from(project::attribute(input.into())) +} + +/// This is a doc comment from the defining crate! +#[proc_macro] +pub fn pin_project(input: TokenStream) -> TokenStream { + TokenStream::from( + pin_projectable::pin_project(input.into()).unwrap_or_else(|e| e.to_compile_error()), + ) +} + +#[proc_macro_attribute] +pub fn pin_projectable(args: TokenStream, input: TokenStream) -> TokenStream { + TokenStream::from( + pin_projectable::attribute(args.into(), input.into()) + .unwrap_or_else(|e| e.to_compile_error()), + ) +} + +#[cfg(feature = "renamed")] +lazy_static::lazy_static! { + pub(crate) static ref PIN_PROJECT_CRATE: String = { + proc_macro_crate::crate_name("pin-project") + .expect("pin-project-internal was used without pin-project!") + }; +} diff --git a/src/unsafe_project/enums.rs b/pin-project-internal/src/pin_projectable/enums.rs similarity index 95% rename from src/unsafe_project/enums.rs rename to pin-project-internal/src/pin_projectable/enums.rs index a03997f8..8413697a 100644 --- a/src/unsafe_project/enums.rs +++ b/pin-project-internal/src/pin_projectable/enums.rs @@ -6,7 +6,12 @@ use crate::utils::{proj_ident, VecExt}; use super::*; -pub(super) fn parse(args: TokenStream, mut item: ItemEnum) -> Result { +pub(super) fn parse( + args: TokenStream, + mut item: ItemEnum, + pinned_drop: Option, +) -> Result { + let impl_drop = ImplDrop::new(item.generics.clone(), pinned_drop)?; let mut impl_unpin = ImplUnpin::new(args, &item.generics)?; if item.variants.is_empty() { @@ -31,6 +36,7 @@ pub(super) fn parse(args: TokenStream, mut item: ItemEnum) -> Result, +} + +impl Parse for PinProject { + fn parse(input: ParseStream<'_>) -> Result { + let mut items = vec![]; + while !input.is_empty() { + items.push(input.parse()?); + } + Ok(Self { items }) + } +} + +fn handle_type(args: TokenStream, item: Item, pinned_drop: Option) -> Result { + match item { + Item::Struct(item) => { + ensure_not_packed(&item.attrs)?; + Ok(structs::parse(args, item, pinned_drop)?) + } + Item::Enum(item) => { + ensure_not_packed(&item.attrs)?; + Ok(enums::parse(args, item, pinned_drop)?) + } + _ => panic!("Unexpected item: {:?}", item), + } +} + +pub(super) fn pin_project(input: TokenStream) -> Result { + let span = span!(input); + let items: Vec = syn::parse2::(input)?.items; + + let mut found_type = None; + let mut found_pinned_drop = None; + + for mut item in items { + match &mut item { + Item::Struct(ItemStruct { attrs, .. }) | Item::Enum(ItemEnum { attrs, .. }) => { + if found_type.is_none() { + if let Some(pos) = attrs.iter().position(|a| a.path.is_ident("pin_projectable")) { + // Remove the 'pin_projectable' attribute, to prevent it from + // being parsed again + let attr = attrs.remove(pos); + let args = match attr.parse_meta()? { + Meta::List(l) => l.nested.into_token_stream(), + Meta::Word(_) => TokenStream::new(), + _ => return Err(error!(span!(attr), "invalid arguments")) + }; + + found_type = Some((item.clone(), args)); + } else { + return Err(error!(span, "type delcared in pin_project! must have pin_projectable attribute")) + } + } else { + return Err(error!(span, "cannot declare multiple types within pinned module")) + } + }, + Item::Fn(ref mut fn_) => { + if fn_.attrs.find_remove(PINNED_DROP) { + if found_pinned_drop.is_none() { + found_pinned_drop = Some(fn_.clone()); + } else { + return Err(error!(span, "cannot declare multiple functions within pinned module")); + } + } else { + return Err(error!(span, "only #[pinned_drop] functions cannot be declared within pinend module")); + } + }, + _ => return Err(error!(span, "pinned module may only contain a struct/enum with an option #[pinned_drop] function")) + } + } + + if found_type.is_none() { + return Err(error!(span, "pin_project must declare a struct or enum")); + } + + let (type_, args) = match found_type { + Some(t) => t, + None => return Err(error!(span, "No #[pin_projectable] type found!")), + }; + + handle_type(args, type_, found_pinned_drop) +} + +pub(super) fn attribute(args: TokenStream, input: TokenStream) -> Result { + let span = span!(input); + let item = syn::parse2(input)?; + match &item { + Item::Struct(_) | Item::Enum(_) => handle_type(args, item, None), + _ => Err(error!(span, "may only be used on structs or enums")), + } +} + +fn ensure_not_packed(attrs: &[Attribute]) -> Result<()> { + for attr in attrs { + if let Ok(meta) = attr.parse_meta() { + if let Meta::List(l) = meta { + if l.ident == "repr" { + for repr in l.nested.iter() { + if let NestedMeta::Meta(Meta::Word(w)) = repr { + if w == "packed" { + return Err(error!( + w, + "pin_projectable may not be used on #[repr(packed)] types" + )); + } + } + } + } + } + } + } + Ok(()) +} + +/// Makes the generics of projected type from the reference of the original generics. +fn proj_generics(generics: &Generics) -> Generics { + let mut generics = generics.clone(); + generics.params.insert(0, syn::parse_quote!('__a)); + generics +} + +struct ImplDrop { + generics: Generics, + pinned_drop: Option, +} + +impl ImplDrop { + /// Parses attribute arguments. + fn new(generics: Generics, pinned_drop: Option) -> Result { + Ok(Self { generics, pinned_drop }) + } + + fn build(self, ident: &Ident) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + if let Some(fn_) = self.pinned_drop { + let fn_name = fn_.ident.clone(); + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { + fn drop(&mut self) { + // Declare the #[pinned_drop] function *inside* our drop function + // This guarantees that it's impossible for any other user code + // to call it + #fn_ + // Safety - we're in 'drop', so we know that 'self' will + // never move again + let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) }; + // 'pinned_drop' is a free function - if it were part of a trait impl, + // it would be possible for user code to call it by directly invoking + // the trait. + // Therefore, we enforce a return type of '()' by explicitly + // assigning it to a temporary. + let _: () = #fn_name(pinned_self); + } + } + } + } else { + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { + fn drop(&mut self) { + // Do nothing. The precense of this Drop + // impl ensures that the user can't provide one of their own + } + } + } + } + } +} + +// ================================================================================================= +// conditional Unpin implementation + +struct ImplUnpin { + generics: Generics, + auto: bool, +} + +impl ImplUnpin { + /// Parses attribute arguments. + fn new(args: TokenStream, generics: &Generics) -> Result { + let mut generics = generics.clone(); + generics.make_where_clause(); + + match &*args.to_string() { + "" => Ok(Self { generics, auto: true }), + "unsafe_Unpin" => Ok(Self { generics, auto: false }), + _ => Err(error!(args, "an invalid argument was passed")), + } + } + + fn push(&mut self, ty: &Type) { + // We only add bounds for automatically generated impls + if self.auto { + self.generics + .make_where_clause() + .predicates + .push(syn::parse_quote!(#ty: ::core::marker::Unpin)); + } + } + + /// Creates `Unpin` implementation. + fn build(self, ident: &Ident) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + let mut where_clause = where_clause.unwrap().clone(); // Created in 'new' + + if self.auto { + quote! { + impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} + } + } else { + let pin_project_crate = pin_project_crate_path(); + where_clause.predicates.push(syn::parse_quote!(::#pin_project_crate::Wrapper<#ident #ty_generics>: ::#pin_project_crate::UnsafeUnpin)); + + quote! { + impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} + } + } + } +} + +/// If the 'renamed' feature is enabled, we detect +/// the actual name of the 'pin-project' crate in the consumer's Cargo.toml +#[cfg(feature = "renamed")] +fn pin_project_crate_path() -> Ident { + use crate::PIN_PROJECT_CRATE; + // This is fairly subtle. + // Normally, you would use `env!("CARGO_PKG_NAME")` to get the name of the package, + // since it's set at compile time. + // However, we're in a proc macro, which runs while *another* crate is being compiled. + // By retreiving the runtime value of `CARGO_PKG_NAME`, we can figure out the name + // of the crate that's calling us. + let cur_crate = std::env::var("CARGO_PKG_NAME") + .expect("Could not find CARGO_PKG_NAME environemnt variable"); + Ident::new( + if cur_crate == "pin-project" { "pin_project" } else { PIN_PROJECT_CRATE.as_str() }, + Span::call_site(), + ) +} + +/// If the 'renamed' feature is not enabled, we just +/// assume that the 'pin-project' dependency has not been renamed +#[cfg(not(feature = "renamed"))] +fn pin_project_crate_path() -> Ident { + Ident::new("pin_project", Span::call_site()) +} diff --git a/src/unsafe_project/structs.rs b/pin-project-internal/src/pin_projectable/structs.rs similarity index 93% rename from src/unsafe_project/structs.rs rename to pin-project-internal/src/pin_projectable/structs.rs index 041b4799..d401a414 100644 --- a/src/unsafe_project/structs.rs +++ b/pin-project-internal/src/pin_projectable/structs.rs @@ -6,7 +6,12 @@ use crate::utils::{proj_ident, VecExt}; use super::*; -pub(super) fn parse(args: TokenStream, mut item: ItemStruct) -> Result { +pub(super) fn parse( + args: TokenStream, + mut item: ItemStruct, + pinned_drop: Option, +) -> Result { + let impl_drop = ImplDrop::new(item.generics.clone(), pinned_drop)?; let mut impl_unpin = ImplUnpin::new(args, &item.generics)?; let (proj_item_body, proj_init_body) = match &mut item.fields { @@ -32,6 +37,7 @@ pub(super) fn parse(args: TokenStream, mut item: ItemStruct) -> Result { #[pin] field1: T, @@ -33,7 +33,7 @@ fn test_project_attr() { // tuple struct - #[unsafe_project(Unpin)] + #[pin_projectable] struct Bar(#[pin] T, U); let mut bar = Bar(1, 2); @@ -49,7 +49,7 @@ fn test_project_attr() { // enum - #[unsafe_project(Unpin)] + #[pin_projectable] enum Baz { Variant1(#[pin] A, B), Variant2 { diff --git a/tests/unsafe_project.rs b/pin-project-internal/tests/unsafe_project.rs similarity index 80% rename from tests/unsafe_project.rs rename to pin-project-internal/tests/unsafe_project.rs index f3313838..5e097ab0 100644 --- a/tests/unsafe_project.rs +++ b/pin-project-internal/tests/unsafe_project.rs @@ -5,13 +5,13 @@ #![allow(dead_code)] use core::pin::Pin; -use pin_project::unsafe_project; +use pin_project_internal::{pin_project, pin_projectable}; #[test] -fn test_unsafe_project() { +fn test_pin_projectable() { // struct - #[unsafe_project(Unpin)] + #[pin_projectable] struct Foo { #[pin] field1: T, @@ -30,7 +30,7 @@ fn test_unsafe_project() { // tuple struct - #[unsafe_project(Unpin)] + #[pin_projectable] struct Bar(#[pin] T, U); let mut bar = Bar(1, 2); @@ -45,7 +45,7 @@ fn test_unsafe_project() { // enum - #[unsafe_project(Unpin)] + #[pin_projectable] enum Baz { Variant1(#[pin] A, B), Variant2 { @@ -107,7 +107,7 @@ fn test_unsafe_project() { fn where_clause_and_associated_type_fields() { // struct - #[unsafe_project(Unpin)] + #[pin_projectable] struct Foo where I: Iterator, @@ -119,7 +119,7 @@ fn where_clause_and_associated_type_fields() { // enum - #[unsafe_project(Unpin)] + #[pin_projectable] enum Baz where I: Iterator, @@ -133,19 +133,39 @@ fn where_clause_and_associated_type_fields() { fn trait_bounds_on_type_generics() { // struct - #[unsafe_project(Unpin)] + #[pin_projectable] pub struct Foo<'a, T: ?Sized> { field: &'a mut T, } // tuple struct - #[unsafe_project(Unpin)] + #[pin_projectable] pub struct Bar<'a, T: ?Sized>(&'a mut T); // enum - #[unsafe_project(Unpin)] + #[pin_projectable] enum Baz<'a, T: ?Sized> { Variant(&'a mut T), } } + +pin_project! { + #[pin_projectable] + pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] field_2: u8 + } + + #[pinned_drop] + fn do_drop(foo: Pin<&mut Foo<'_>>) { + **foo.project().was_dropped = true; + } +} + +#[test] +fn safe_project() { + let mut was_dropped = false; + drop(Foo { was_dropped: &mut was_dropped, field_2: 42 }); + assert!(was_dropped); +} diff --git a/pin-project/Cargo.toml b/pin-project/Cargo.toml new file mode 100644 index 00000000..44507c19 --- /dev/null +++ b/pin-project/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "pin-project" +# NB: When modifying, also modify html_root_url in lib.rs +version = "0.3.4" +authors = ["Taiki Endo "] +edition = "2018" +license = "Apache-2.0/MIT" +description = "An attribute that creates a projection struct covering all the fields." +repository = "https://github.com/taiki-e/pin-project" +documentation = "https://docs.rs/pin-project/" +readme = "README.md" +keywords = ["pin", "macros", "attribute"] +categories = ["rust-patterns"] +exclude = ["/.travis.yml", "/bors.toml"] + +[badges] +travis-ci = { repository = "taiki-e/pin-project" } + +[features] +# Default features. +default = ["project_attr"] +# Enable to use `project` attribute. +project_attr = ["pin-project-internal/project_attr"] +# Enable to allow using this crate as a renamed dependency +renamed = ["pin-project-internal/renamed"] + +[dependencies] +pin-project-internal = { path = "../pin-project-internal" } + +[dev-dependencies] +compiletest = { version = "0.3.21", package = "compiletest_rs", features = ["stable", "tmp"] } diff --git a/pin-project/src/lib.rs b/pin-project/src/lib.rs new file mode 100644 index 00000000..657901f7 --- /dev/null +++ b/pin-project/src/lib.rs @@ -0,0 +1,628 @@ +//! An attribute that creates a projection struct covering all the fields. +//! +//! ## Examples +//! +//! [`pin_projectable`] attribute creates a projection struct covering all the fields. +//! +//! ```rust +//! use pin_project::pin_projectable; +//! use std::pin::Pin; +//! +//! #[pin_projectable] +//! struct Foo { +//! #[pin] +//! future: T, +//! field: U, +//! } +//! +//! impl Foo { +//! fn baz(self: Pin<&mut Self>) { +//! let this = self.project(); +//! let _: Pin<&mut T> = this.future; // Pinned reference to the field +//! let _: &mut U = this.field; // Normal reference to the field +//! } +//! } +//! +//! // Automatically create the appropriate conditional Unpin implementation (optional). +//! // impl Unpin for Foo {} +//! ``` +//! +//!
+//! Code like this will be generated: +//! +//! ```rust +//! struct Foo { +//! future: T, +//! field: U, +//! } +//! +//! struct __FooProjection<'__a, T, U> { +//! future: ::core::pin::Pin<&'__a mut T>, +//! field: &'__a mut U, +//! } +//! +//! impl Foo { +//! fn project<'__a>(self: ::core::pin::Pin<&'__a mut Self>) -> __FooProjection<'__a, T, U> { +//! unsafe { +//! let this = ::core::pin::Pin::get_unchecked_mut(self); +//! __FooProjection { +//! future: ::core::pin::Pin::new_unchecked(&mut this.future), +//! field: &mut this.field, +//! } +//! } +//! } +//! } +//! +//! // Automatically create the appropriate conditional Unpin implementation (optional). +//! impl Unpin for Foo where T: Unpin {} +//! ``` +//! +//!
+//! +//! [`pin_projectable`] also supports enums, but to use it ergonomically, you need +//! to use the [`project`] attribute. +//! +//! ```rust +//! # #[cfg(feature = "project_attr")] +//! use pin_project::{project, pin_projectable}; +//! # #[cfg(feature = "project_attr")] +//! use std::pin::Pin; +//! +//! # #[cfg(feature = "project_attr")] +//! #[pin_projectable] +//! enum Foo { +//! Future(#[pin] T), +//! Done(U), +//! } +//! +//! # #[cfg(feature = "project_attr")] +//! impl Foo { +//! #[project] // Nightly does not need a dummy attribute to the function. +//! fn baz(self: Pin<&mut Self>) { +//! #[project] +//! match self.project() { +//! Foo::Future(future) => { +//! let _: Pin<&mut T> = future; +//! } +//! Foo::Done(value) => { +//! let _: &mut U = value; +//! } +//! } +//! } +//! } +//! +//! // Automatically create the appropriate conditional Unpin implementation (optional). +//! // impl Unpin for Foo where T: Unpin {} +//! ``` +//! +//!
+//! Code like this will be generated: +//! +//! ```rust +//! enum Foo { +//! Future(T), +//! Done(U), +//! } +//! +//! enum __FooProjection<'__a, T, U> { +//! Future(::core::pin::Pin<&'__a mut T>), +//! Done(&'__a mut U), +//! } +//! +//! impl Foo { +//! fn project<'__a>(self: ::core::pin::Pin<&'__a mut Self>) -> __FooProjection<'__a, T, U> { +//! unsafe { +//! match ::core::pin::Pin::get_unchecked_mut(self) { +//! Foo::Future(_x0) => __FooProjection::Future(::core::pin::Pin::new_unchecked(_x0)), +//! Foo::Done(_x0) => __FooProjection::Done(_x0), +//! } +//! } +//! } +//! } +//! +//! // Automatically create the appropriate conditional Unpin implementation (optional). +//! impl Unpin for Foo where T: Unpin {} +//! ``` +//! +//!
+//! +//! See [`pin_projectable`] and [`project`] for more details. +//! +//! [`pin_projectable`]: ./attr.pin_projectable.html +//! [`project`]: ./attr.project.html +//! +#![recursion_limit = "256"] +#![doc(html_root_url = "https://docs.rs/pin-project/0.3.4")] +#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![warn(unsafe_code)] +#![warn(rust_2018_idioms, unreachable_pub)] +#![warn(single_use_lifetimes)] +#![warn(clippy::all, clippy::pedantic)] +#![warn(clippy::nursery)] + +/// An attribute to support pattern matching. +/// +/// *This attribute is available if pin-project is built with the +/// `"project_attr"` feature (it is enabled by default).* +/// +/// ## Examples +/// +/// ### `let` bindings +/// +/// ```rust +/// use pin_project::{pin_projectable, project}; +/// # use std::pin::Pin; +/// +/// #[pin_projectable] +/// struct Foo { +/// #[pin] +/// future: T, +/// field: U, +/// } +/// +/// impl Foo { +/// #[project] // Nightly does not need a dummy attribute to the function. +/// fn baz(self: Pin<&mut Self>) { +/// #[project] +/// let Foo { future, field } = self.project(); +/// +/// let _: Pin<&mut T> = future; +/// let _: &mut U = field; +/// } +/// } +/// ``` +/// +/// ### `match` expressions +/// +/// ```rust +/// use pin_project::{project, pin_projectable}; +/// # use std::pin::Pin; +/// +/// #[pin_projectable] +/// enum Foo { +/// Tuple(#[pin] A, B), +/// Struct { field: C }, +/// Unit, +/// } +/// +/// impl Foo { +/// #[project] // Nightly does not need a dummy attribute to the function. +/// fn baz(self: Pin<&mut Self>) { +/// #[project] +/// match self.project() { +/// Foo::Tuple(x, y) => { +/// let _: Pin<&mut A> = x; +/// let _: &mut B = y; +/// } +/// Foo::Struct { field } => { +/// let _: &mut C = field; +/// } +/// Foo::Unit => {} +/// } +/// } +/// } +/// ``` +/// +/// ### `if let` expressions +/// +/// When used against `if let` expressions, the `#[project]` attribute records +/// the name of the structure destructed with the first `if let`. Destructing +/// different structures in the after second times will not generate wrong code. +/// +/// ```rust +/// use pin_project::{project, pin_projectable}; +/// # use std::pin::Pin; +/// +/// #[pin_projectable] +/// enum Foo { +/// Tuple(#[pin] A, B), +/// Struct { field: C }, +/// Unit, +/// } +/// +/// impl Foo { +/// #[project] // Nightly does not need a dummy attribute to the function. +/// fn baz(self: Pin<&mut Self>) { +/// #[project] +/// { +/// if let Foo::Tuple(x, y) = self.project() { +/// let _: Pin<&mut A> = x; +/// let _: &mut B = y; +/// } +/// } +/// } +/// } +/// ``` +#[doc(inline)] +pub use pin_project_internal::project; + +/// An attribute that creates a projection struct covering all the fields. +/// +/// This attribute creates a projection struct according to the following rules: +/// +/// - For the field that uses `#[pin]` attribute, makes the pinned reference to +/// the field. +/// - For the other fields, makes the unpinned reference to the field. +/// +/// ## Safety +/// +/// This attribute is completely safe. In the absence of other `unsafe` code *that you write*, +/// it is impossible to cause undefined behavior with this attribute. +/// +/// This is accomplished by enforcing the four requirements for pin projection +/// stated in [the Rust documentation](https://doc.rust-lang.org/beta/std/pin/index.html#projections-and-structural-pinning): +/// +/// 1. The struct must only be Unpin if all the structural fields are Unpin +/// +/// To enforce this, this attribute will automatically generate an `Unpin` implementation +/// for you, which will require that all structurally pinned fields be `Unpin` +/// If you wish to provide an manual `Unpin` impl, you can do so via the +/// `unsafe_Unpin` argument. +/// +/// 2. The destructor of the struct must not move structural fields out of its argument. +/// +/// To enforce this, this attribute will automatically generate a `Drop` impl. +/// If you wish to provide a custom `Drop` impl, you can annotate a function +/// with `#[pinned_drop]`. This function takes a pinned version of your struct - +/// that is, `Pin<&mut MyStruct>` where `MyStruct` is the type of your struct. +/// +/// You can call `project()` on this type as usual, along with any other +/// methods you have defined. Because your code is never provided with +/// a `&mut MyStruct`, it is impossible to move out of pin-projectable +/// fields in safe code in your destructor. +/// +/// 3. You must make sure that you uphold the Drop guarantee: once your struct is pinned, +/// the memory that contains the content is not overwritten or deallocated without calling the content's destructors +/// +/// Safe code doesn't need to worry about this - the only wait to violate this requirement +/// is to manually deallocate memory (which is `unsafe`), or to overwite a field with something else. +/// Becauese your custom destructor takes `Pin<&mut MyStruct`, it's impossible to obtain +/// a mutable reference to a pin-projected field in safe code. +/// +/// 4. You must not offer any other operations that could lead to data being moved out of the structural fields when your type is pinned. +/// +/// As with requirement 3, it is impossible for safe code to violate this. This crate ensures that safe code can never +/// obtain a mutable reference to `#[pin]` fields, which prevents you from ever moving out of them in safe code. +/// +/// Pin projections are also incompatible with `#[repr(packed)]` structs. Attempting to use this attribute +/// on a `#[repr(packed)]` struct results in a compile-time error. +/// +/// +/// ## Examples +/// +/// Using `#[pin_projectable]` will automatically create the appropriate +/// conditional [`Unpin`] implementation: +/// +/// ```rust +/// use pin_project::pin_projectable; +/// use std::pin::Pin; +/// +/// #[pin_projectable] +/// struct Foo { +/// #[pin] +/// future: T, +/// field: U, +/// } +/// +/// impl Foo { +/// fn baz(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.future; // Pinned reference to the field +/// let _: &mut U = this.field; // Normal reference to the field +/// } +/// } +/// +/// // Automatically create the appropriate conditional Unpin implementation. +/// // impl Unpin for Foo where T: Unpin {} +/// ``` +/// +/// If you want to implement [`Unpin`] manually, +/// you msut use thw `unsafe_Unpin` argument to +/// `#[pin_projectable]`. +/// +/// ```rust +/// use pin_project::{pin_projectable, UnsafeUnpin}; +/// use std::pin::Pin; +/// +/// #[pin_projectable(unsafe_Unpin)] +/// struct Foo { +/// #[pin] +/// future: T, +/// field: U, +/// } +/// +/// impl Foo { +/// fn baz(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.future; // Pinned reference to the field +/// let _: &mut U = this.field; // Normal reference to the field +/// } +/// } +/// +/// unsafe impl UnsafeUnpin for Foo {} // Conditional Unpin impl +/// ``` +/// +/// Note the usage of the unsafe `UnsafeUnpin` trait, instead of the usual +/// `Unpin` trait. `UnsafeUnpin` behaves exactly like `Unpin`, except that is +/// unsafe to implement. This unsafety comes from the fact that pin projections +/// are being used. If you implement `UnsafeUnpin`, you must ensure that it is +/// only implemented when all pin-projected fields implement `Unpin`. +/// +/// Note that borrowing the field where `#[pin]` attribute is used multiple +/// times requires using `.as_mut()` to avoid consuming the `Pin`. +/// +/// ## Supported Items +/// +/// The current version of pin-project supports the following types of items. +/// +/// ### Structs (structs with named fields): +/// +/// ```rust +/// # use pin_project::pin_projectable; +/// # use std::pin::Pin; +/// #[pin_projectable] +/// struct Foo { +/// #[pin] +/// future: T, +/// field: U, +/// } +/// +/// impl Foo { +/// fn baz(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.future; +/// let _: &mut U = this.field; +/// } +/// } +/// ``` +/// +/// ### Tuple structs (structs with unnamed fields): +/// +/// ```rust +/// # use pin_project::pin_projectable; +/// # use std::pin::Pin; +/// #[pin_projectable] +/// struct Foo(#[pin] T, U); +/// +/// impl Foo { +/// fn baz(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.0; +/// let _: &mut U = this.1; +/// } +/// } +/// ``` +/// +/// Structs without fields (unit-like struct and zero fields struct) are not +/// supported. +/// +/// ### Enums +/// +/// `pin_projectable` also supports enums, but to use it ergonomically, you need +/// to use the [`project`] attribute. +/// +/// ```rust +/// # #[cfg(feature = "project_attr")] +/// use pin_project::{project, pin_projectable}; +/// # #[cfg(feature = "project_attr")] +/// # use std::pin::Pin; +/// +/// # #[cfg(feature = "project_attr")] +/// #[pin_projectable] +/// enum Foo { +/// Tuple(#[pin] A, B), +/// Struct { field: C }, +/// Unit, +/// } +/// +/// # #[cfg(feature = "project_attr")] +/// impl Foo { +/// #[project] // Nightly does not need a dummy attribute to the function. +/// fn baz(self: Pin<&mut Self>) { +/// #[project] +/// match self.project() { +/// Foo::Tuple(x, y) => { +/// let _: Pin<&mut A> = x; +/// let _: &mut B = y; +/// } +/// Foo::Struct { field } => { +/// let _: &mut C = field; +/// } +/// Foo::Unit => {} +/// } +/// } +/// } +/// ``` +/// +/// Enums without variants (zero-variant enums) are not supported. +/// +/// ### `#[pinned_drop]` +/// +/// In order to correctly implement pin projections, a type's Drop impl must +/// not move out of any stucturally pinned fields. Unfortunately, `Drop::drop` takes +/// `&mut Self`, not `Pin<&mut Self>`. +/// +/// To ensure that this requirement is upheld, the `pin_projectable` attribute will +/// provide a `Drop` impl for you. This `Drop` impl will delegate to a function +/// annotated with `#[pinned_drop]`, if present in the same `pin_project!` block. +/// This function acts just like a normal `drop` impl, except for the fact that it +/// takes `Pin<&mut Self>`. In particular, it will never be called more than once, +/// just like `Drop::drop`. +/// +/// For example: +/// +/// ```rust +/// use std::fmt::Debug; +/// use pin_project::pin_project; +/// use std::pin::Pin; +/// +/// pin_project! { +/// #[pin_projectable] +/// pub struct Foo { +/// #[pin] pinned_field: T, +/// unpin_field: U +/// } +/// +/// #[pinned_drop] +/// fn my_drop_fn(foo: Pin<&mut Foo>) { +/// let foo = foo.project(); +/// println!("Dropping pinned field: {:?}", foo.pinned_field); +/// println!("Dropping unpin field: {:?}", foo.unpin_field); +/// } +/// } +/// +/// fn main() { +/// Foo { pinned_field: true, unpin_field: 40 }; +/// } +/// ``` +/// +/// +/// +/// Also see [`project`] attribute. +/// +/// [`Unpin`]: core::marker::Unpin +/// [`drop`]: Drop::drop +/// [`project`]: ./attr.project.html +#[doc(inline)] +pub use pin_project_internal::pin_projectable; + +/// A helper macro for working with `pin_projectable`. +/// +/// This macro is only needed when you wish to provide a `Drop` +/// impl for your type. You may include a single `#[pin_projectable]` +/// type, and (optionally) one `#[pinned_drop]` function. Writing +/// anything else within the `pin_projectable` block is an error. +/// +/// Example: +/// +/// ```rust +/// +/// use std::pin::Pin; +/// use pin_project::pin_project; +/// +/// pin_project! { +/// +/// #[pin_projectable] +/// struct Foo { +/// #[pin] field: u8 +/// } +/// +/// #[pinned_drop] +/// fn my_drop(foo: Pin<&mut Foo>) { +/// println!("Dropping: {}", foo.field); +/// } +/// } +/// +/// fn main() { +/// Foo { field: 50 }; +/// } +///``` +#[doc(inline)] +pub use pin_project_internal::pin_project; + +/// A trait used for custom implementations of [`Unpin`]. +/// This trait is used in conjunction with the `unsafe_Unpin` +/// argument to [`pin_projectable`] +/// +/// The Rust [`Unpin`] trait is safe to implement - by itself, +/// implementing it cannot lead to undefined behavior. Undefined +/// behavior can only occur when other unsafe code is used. +/// +/// It turns out that using pin projections, which requires unsafe code, +/// imposes additional requirements on an `Unpin` impl. Normally, all of this +/// unsafety is contained within this crate, ensuring that it's impossible for +/// you to violate any of the guarnatees required by pin projection. +/// +/// However, things change if you want to provide a custom `Unpin` impl +/// for your `#[pin_projectable]` type. As stated in [the Rust +/// documentation](https://doc.rust-lang.org/beta/std/pin/index.html#projections-and-structural-pinning), +/// you must be sure to only implement `Unpin` when all of your `#[pin]` fields (i.e. struturally +/// pinend fields) are also `Unpin`. +/// +/// To help highlight this unsafety, the `UnsafeUnpin` trait is provided. +/// Implementing this trait is logically equivalent to implemnting `Unpin` - +/// this crate will generate an `Unpin` impl for your type that 'forwards' to +/// your `UnsafeUnpin` impl. However, this trait is `unsafe` - since your type +/// uses structural pinning (otherwise, you wouldn't be using this crate!), +/// you must be sure that your `UnsafeUnpinned` impls follows all of +/// the requirements for an `Unpin` impl of a structurally-pinned type. +/// +/// Note that if you specify `#[pin_projectable(unsafe_Unpin)]`, but do *not* +/// provide an impl of `UnsafeUnpin`, your type will never implement `Unpin`. +/// This is effectly the same thing as adding a `PhantomUnpin` to your type +/// +/// Since this trait is `unsafe`, impls of it will be detected by the `unsafe_code` lint, +/// and by tools like `cargo geiger`. +/// +/// ## Examples +/// +/// An `UnsafeUnpin` impl which, in addition to requiring that structually pinned +/// fields be `Unpin`, imposes an additional requirement: +/// +/// ```rust +/// use pin_project::{pin_projectable, UnsafeUnpin}; +/// +/// #[pin_projectable(unsafe_Unpin)] +/// struct Foo { +/// #[pin] +/// field_1: K, +/// field_2: V +/// } +/// +/// unsafe impl UnsafeUnpin for Foo where K: Unpin + Clone {} +/// ``` +/// +/// [`pin_projectable`]: ./attr.pin_projectable.html +#[allow(unsafe_code)] +pub unsafe trait UnsafeUnpin {} + +#[doc(hidden)] +// This is an internal helper struct used by `pin-project-internal`. +// This allows us to force an error if the user tries to provide +// a regular `Unpin` impl when they specify the `unsafe_Unpin` argument +// This is why we need Wrapper: +// +// Supposed we have the following code: +// +// #[pin_projectable(unsafe_Unpin)] +// struct MyStruct { +// #[pin] field: T +// } +// +// impl Unpin for MyStruct where MyStruct: UnsafeUnpinned {} // generated by pin-project-internal +// impl Unpin for MyStruct where T: Copy // written by the user +// +// We want this code to be rejected - the user is completely bypassing unsafe_Unpin, +// and providing an unsound Unpin impl in safe code! +// +// Unfortunately, the Rust compiler will accept the above code. +// Because MyStruct is declared in the same crate as the user-provided impl, +// the compiler will notice that 'MyStruct: UnsafeUnpinned' never holds. +// +// The solution is to introduce the 'Wrapper' struct, which is defined +// in the 'pin-project' crate. +// +// We now have code that looks like this: +// +// impl Unpin for MyStruct where Wrapper>: UnsafeUnpinned {} // generated by pin-project-internal +// impl Unpin for MyStruct where T: Copy // written by the user +// +// We also have 'unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {}' in the +// 'pin-project' crate. +// +// Now, our generated impl has a bound involving a type defined in another crate - Wrapper. +// This will cause rust to conservatively assume that 'Wrapper>: UnsafeUnpinned' +// holds, in the interest of preserving forwards compatibility (in case such an impl is added +// for Wrapper in a new version of the crate). +// +// This will cause rust to reject any other Unpin impls for MyStruct, since it will +// assume that our generated impl could potentially apply in any situation. +// +// This acheives the desired effect - when the user writes `#[pin_projectable(unsafe_Unpin)]`, +// the user must either provide no impl of `UnsafeUnpinned` (which is equivalent +// to making the type never implement Unpin), or provide an impl of `UnsafeUnpin`. +// It is impossible for them to provide an impl of `Unpin` +pub struct Wrapper(T); + +#[allow(unsafe_code)] +unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {} diff --git a/tests/compile-test.rs b/pin-project/tests/compile-test.rs similarity index 100% rename from tests/compile-test.rs rename to pin-project/tests/compile-test.rs diff --git a/pin-project/tests/ui/unsafe_project/packed.rs b/pin-project/tests/ui/unsafe_project/packed.rs new file mode 100644 index 00000000..56e45015 --- /dev/null +++ b/pin-project/tests/ui/unsafe_project/packed.rs @@ -0,0 +1,34 @@ +// compile-fail +#![allow(dead_code)] + +use pin_project::pin_projectable; + +#[pin_projectable] +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type +struct Foo { + #[pin] + field: u8 +} + +// Test putting 'repr' before the 'pin_projectable' attribute +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type +#[pin_projectable] +struct Foo2 { + #[pin] + field: u8 +} + +#[pin_projectable] +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type +enum Blah { + Tuple(#[pin] u8) +} + +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type +#[pin_projectable] +enum Blah2 { + Tuple(#[pin] u8) +} + + +fn main() {} diff --git a/pin-project/tests/ui/unsafe_project/packed.stderr b/pin-project/tests/ui/unsafe_project/packed.stderr new file mode 100644 index 00000000..3c5790f9 --- /dev/null +++ b/pin-project/tests/ui/unsafe_project/packed.stderr @@ -0,0 +1,26 @@ +error: pin_projectable may not be used on #[repr(packed)] types + --> $DIR/packed.rs:7:8 + | +7 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type + | ^^^^^^ + +error: pin_projectable may not be used on #[repr(packed)] types + --> $DIR/packed.rs:14:8 + | +14 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type + | ^^^^^^ + +error: pin_projectable may not be used on #[repr(packed)] types + --> $DIR/packed.rs:22:8 + | +22 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type + | ^^^^^^ + +error: pin_projectable may not be used on #[repr(packed)] types + --> $DIR/packed.rs:27:8 + | +27 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type + | ^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/unsafe_project/unpin_conflict.rs b/pin-project/tests/ui/unsafe_project/unpin_conflict.rs similarity index 89% rename from tests/ui/unsafe_project/unpin_conflict.rs rename to pin-project/tests/ui/unsafe_project/unpin_conflict.rs index fa7d73c9..d7923483 100644 --- a/tests/ui/unsafe_project/unpin_conflict.rs +++ b/pin-project/tests/ui/unsafe_project/unpin_conflict.rs @@ -2,12 +2,12 @@ #![deny(warnings)] -use pin_project::unsafe_project; +use pin_project::pin_projectable; use std::pin::Pin; // The same implementation. -#[unsafe_project(Unpin)] //~ ERROR E0119 +#[pin_projectable] //~ ERROR E0119 struct Foo { #[pin] future: T, @@ -27,7 +27,7 @@ impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl // The implementation that under different conditions. -#[unsafe_project(Unpin)] //~ ERROR E0119 +#[pin_projectable] //~ ERROR E0119 struct Bar { #[pin] future: T, @@ -45,7 +45,7 @@ impl Bar { // conflicting implementations impl Unpin for Bar {} // Non-conditional Unpin impl -#[unsafe_project(Unpin)] //~ ERROR E0119 +#[pin_projectable] //~ ERROR E0119 struct Baz { #[pin] future: T, diff --git a/tests/ui/unsafe_project/unpin_conflict.stderr b/pin-project/tests/ui/unsafe_project/unpin_conflict.stderr similarity index 73% rename from tests/ui/unsafe_project/unpin_conflict.stderr rename to pin-project/tests/ui/unsafe_project/unpin_conflict.stderr index c8c37d38..7baa4bef 100644 --- a/tests/ui/unsafe_project/unpin_conflict.stderr +++ b/pin-project/tests/ui/unsafe_project/unpin_conflict.stderr @@ -1,8 +1,8 @@ error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: --> $DIR/unpin_conflict.rs:10:1 | -10 | #[unsafe_project(Unpin)] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +10 | #[pin_projectable] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` ... 26 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl | --------------------------------------------- first implementation here @@ -10,8 +10,8 @@ error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: --> $DIR/unpin_conflict.rs:30:1 | -30 | #[unsafe_project(Unpin)] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +30 | #[pin_projectable] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` ... 46 | impl Unpin for Bar {} // Non-conditional Unpin impl | ------------------------------ first implementation here @@ -19,8 +19,8 @@ error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: --> $DIR/unpin_conflict.rs:48:1 | -48 | #[unsafe_project(Unpin)] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +48 | #[pin_projectable] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` ... 64 | impl Unpin for Baz {} // Conditional Unpin impl | -------------------------------------------- first implementation here diff --git a/tests/ui/unsafe_project/unsupported.rs b/pin-project/tests/ui/unsafe_project/unsupported.rs similarity index 78% rename from tests/ui/unsafe_project/unsupported.rs rename to pin-project/tests/ui/unsafe_project/unsupported.rs index 7473f8af..79955d08 100644 --- a/tests/ui/unsafe_project/unsupported.rs +++ b/pin-project/tests/ui/unsafe_project/unsupported.rs @@ -2,27 +2,27 @@ #![deny(warnings)] -use pin_project::unsafe_project; +use pin_project::pin_projectable; -#[unsafe_project] +#[pin_projectable] struct Struct1 {} //~ ERROR cannot be implemented for structs with zero fields -#[unsafe_project] +#[pin_projectable] struct Struct2(); //~ ERROR cannot be implemented for structs with zero fields -#[unsafe_project] +#[pin_projectable] struct Struct3; //~ ERROR cannot be implemented for structs with units -#[unsafe_project] +#[pin_projectable] enum Enum1 {} //~ ERROR cannot be implemented for enums without variants -#[unsafe_project] +#[pin_projectable] enum Enum2 { A = 2, //~ ERROR cannot be implemented for enums with discriminants } /* FIXME: cannot be implemented for enums that has no field. -#[unsafe_project] +#[pin_projectable] enum Enum1 { A, } diff --git a/tests/ui/unsafe_project/unsupported.stderr b/pin-project/tests/ui/unsafe_project/unsupported.stderr similarity index 100% rename from tests/ui/unsafe_project/unsupported.stderr rename to pin-project/tests/ui/unsafe_project/unsupported.stderr diff --git a/tests/ui/update-all-references.sh b/pin-project/tests/ui/update-all-references.sh similarity index 100% rename from tests/ui/update-all-references.sh rename to pin-project/tests/ui/update-all-references.sh diff --git a/tests/ui/update-references.sh b/pin-project/tests/ui/update-references.sh old mode 100644 new mode 100755 similarity index 100% rename from tests/ui/update-references.sh rename to pin-project/tests/ui/update-references.sh diff --git a/pin-project/tests/unsafe_unpin.rs b/pin-project/tests/unsafe_unpin.rs new file mode 100644 index 00000000..5b1c3192 --- /dev/null +++ b/pin-project/tests/unsafe_unpin.rs @@ -0,0 +1,17 @@ +#![recursion_limit = "128"] +#![no_std] +#![warn(unsafe_code)] +#![warn(rust_2018_idioms)] +#![allow(dead_code)] + +use pin_project::pin_projectable; + +#[test] +fn unsafe_unpin() { + #[pin_projectable(unsafe_Unpin)] + pub struct Blah { + field_1: u8, + #[pin] + field_2: Option, + } +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 268ea993..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,427 +0,0 @@ -//! An attribute that creates a projection struct covering all the fields. -//! -//! ## Examples -//! -//! [`unsafe_project`] attribute creates a projection struct covering all the fields. -//! -//! ```rust -//! use pin_project::unsafe_project; -//! use std::pin::Pin; -//! -//! #[unsafe_project(Unpin)] // `(Unpin)` is optional (create the appropriate conditional Unpin implementation) -//! struct Foo { -//! #[pin] -//! future: T, -//! field: U, -//! } -//! -//! impl Foo { -//! fn baz(self: Pin<&mut Self>) { -//! let this = self.project(); -//! let _: Pin<&mut T> = this.future; // Pinned reference to the field -//! let _: &mut U = this.field; // Normal reference to the field -//! } -//! } -//! -//! // Automatically create the appropriate conditional Unpin implementation (optional). -//! // impl Unpin for Foo {} -//! ``` -//! -//!
-//! Code like this will be generated: -//! -//! ```rust -//! struct Foo { -//! future: T, -//! field: U, -//! } -//! -//! struct __FooProjection<'__a, T, U> { -//! future: ::core::pin::Pin<&'__a mut T>, -//! field: &'__a mut U, -//! } -//! -//! impl Foo { -//! fn project<'__a>(self: ::core::pin::Pin<&'__a mut Self>) -> __FooProjection<'__a, T, U> { -//! unsafe { -//! let this = ::core::pin::Pin::get_unchecked_mut(self); -//! __FooProjection { -//! future: ::core::pin::Pin::new_unchecked(&mut this.future), -//! field: &mut this.field, -//! } -//! } -//! } -//! } -//! -//! // Automatically create the appropriate conditional Unpin implementation (optional). -//! impl Unpin for Foo where T: Unpin {} -//! ``` -//! -//!
-//! -//! [`unsafe_project`] also supports enums, but to use it ergonomically, you need -//! to use the [`project`] attribute. -//! -//! ```rust -//! # #[cfg(feature = "project_attr")] -//! use pin_project::{project, unsafe_project}; -//! # #[cfg(feature = "project_attr")] -//! use std::pin::Pin; -//! -//! # #[cfg(feature = "project_attr")] -//! #[unsafe_project(Unpin)] // `(Unpin)` is optional (create the appropriate conditional Unpin implementation) -//! enum Foo { -//! Future(#[pin] T), -//! Done(U), -//! } -//! -//! # #[cfg(feature = "project_attr")] -//! impl Foo { -//! #[project] // Nightly does not need a dummy attribute to the function. -//! fn baz(self: Pin<&mut Self>) { -//! #[project] -//! match self.project() { -//! Foo::Future(future) => { -//! let _: Pin<&mut T> = future; -//! } -//! Foo::Done(value) => { -//! let _: &mut U = value; -//! } -//! } -//! } -//! } -//! -//! // Automatically create the appropriate conditional Unpin implementation (optional). -//! // impl Unpin for Foo where T: Unpin {} -//! ``` -//! -//!
-//! Code like this will be generated: -//! -//! ```rust -//! enum Foo { -//! Future(T), -//! Done(U), -//! } -//! -//! enum __FooProjection<'__a, T, U> { -//! Future(::core::pin::Pin<&'__a mut T>), -//! Done(&'__a mut U), -//! } -//! -//! impl Foo { -//! fn project<'__a>(self: ::core::pin::Pin<&'__a mut Self>) -> __FooProjection<'__a, T, U> { -//! unsafe { -//! match ::core::pin::Pin::get_unchecked_mut(self) { -//! Foo::Future(_x0) => __FooProjection::Future(::core::pin::Pin::new_unchecked(_x0)), -//! Foo::Done(_x0) => __FooProjection::Done(_x0), -//! } -//! } -//! } -//! } -//! -//! // Automatically create the appropriate conditional Unpin implementation (optional). -//! impl Unpin for Foo where T: Unpin {} -//! ``` -//! -//!
-//! -//! See [`unsafe_project`] and [`project`] for more details. -//! -//! [`unsafe_project`]: ./attr.unsafe_project.html -//! [`project`]: ./attr.project.html -//! - -#![recursion_limit = "256"] -#![doc(html_root_url = "https://docs.rs/pin-project/0.3.4")] -#![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] -#![warn(unsafe_code)] -#![warn(rust_2018_idioms, unreachable_pub)] -#![warn(single_use_lifetimes)] -#![warn(clippy::all, clippy::pedantic)] -#![warn(clippy::nursery)] - -extern crate proc_macro; - -#[macro_use] -mod utils; - -#[cfg(feature = "project_attr")] -mod project; -mod unsafe_project; - -use proc_macro::TokenStream; - -/// An attribute that creates a projection struct covering all the fields. -/// -/// This attribute creates a projection struct according to the following rules: -/// -/// - For the field that uses `#[pin]` attribute, makes the pinned reference to -/// the field. -/// - For the other fields, makes the unpinned reference to the field. -/// -/// ## Safety -/// -/// For the field that uses `#[pin]` attribute, three things need to be ensured: -/// -/// - If the struct implements [`Drop`], the [`drop`] method is not allowed to -/// move the value of the field. -/// - If the struct wants to implement [`Unpin`], it has to do so conditionally: -/// The struct can only implement [`Unpin`] if the field's type is [`Unpin`]. -/// If you use `#[unsafe_project(Unpin)]`, you do not need to ensure this -/// because an appropriate conditional [`Unpin`] implementation will be -/// generated. -/// - The struct must not be `#[repr(packed)]`. -/// -/// For the other fields, need to be ensured that the contained value not pinned -/// in the current context. -/// -/// ## Examples -/// -/// Using `#[unsafe_project(Unpin)]` will automatically create the appropriate -/// conditional [`Unpin`] implementation: -/// -/// ```rust -/// use pin_project::unsafe_project; -/// use std::pin::Pin; -/// -/// #[unsafe_project(Unpin)] -/// struct Foo { -/// #[pin] -/// future: T, -/// field: U, -/// } -/// -/// impl Foo { -/// fn baz(self: Pin<&mut Self>) { -/// let this = self.project(); -/// let _: Pin<&mut T> = this.future; // Pinned reference to the field -/// let _: &mut U = this.field; // Normal reference to the field -/// } -/// } -/// -/// // Automatically create the appropriate conditional Unpin implementation. -/// // impl Unpin for Foo where T: Unpin {} -/// ``` -/// -/// If you want to implement [`Unpin`] manually: -/// -/// ```rust -/// use pin_project::unsafe_project; -/// use std::pin::Pin; -/// -/// #[unsafe_project] -/// struct Foo { -/// #[pin] -/// future: T, -/// field: U, -/// } -/// -/// impl Foo { -/// fn baz(self: Pin<&mut Self>) { -/// let this = self.project(); -/// let _: Pin<&mut T> = this.future; // Pinned reference to the field -/// let _: &mut U = this.field; // Normal reference to the field -/// } -/// } -/// -/// impl Unpin for Foo {} // Conditional Unpin impl -/// ``` -/// -/// Note that borrowing the field where `#[pin]` attribute is used multiple -/// times requires using `.as_mut()` to avoid consuming the `Pin`. -/// -/// ## Supported Items -/// -/// The current version of pin-project supports the following types of items. -/// -/// ### Structs (structs with named fields): -/// -/// ```rust -/// # use pin_project::unsafe_project; -/// # use std::pin::Pin; -/// #[unsafe_project(Unpin)] -/// struct Foo { -/// #[pin] -/// future: T, -/// field: U, -/// } -/// -/// impl Foo { -/// fn baz(self: Pin<&mut Self>) { -/// let this = self.project(); -/// let _: Pin<&mut T> = this.future; -/// let _: &mut U = this.field; -/// } -/// } -/// ``` -/// -/// ### Tuple structs (structs with unnamed fields): -/// -/// ```rust -/// # use pin_project::unsafe_project; -/// # use std::pin::Pin; -/// #[unsafe_project(Unpin)] -/// struct Foo(#[pin] T, U); -/// -/// impl Foo { -/// fn baz(self: Pin<&mut Self>) { -/// let this = self.project(); -/// let _: Pin<&mut T> = this.0; -/// let _: &mut U = this.1; -/// } -/// } -/// ``` -/// -/// Structs without fields (unit-like struct and zero fields struct) are not -/// supported. -/// -/// ### Enums -/// -/// `unsafe_project` also supports enums, but to use it ergonomically, you need -/// to use the [`project`] attribute. -/// -/// ```rust -/// # #[cfg(feature = "project_attr")] -/// use pin_project::{project, unsafe_project}; -/// # #[cfg(feature = "project_attr")] -/// # use std::pin::Pin; -/// -/// # #[cfg(feature = "project_attr")] -/// #[unsafe_project(Unpin)] -/// enum Foo { -/// Tuple(#[pin] A, B), -/// Struct { field: C }, -/// Unit, -/// } -/// -/// # #[cfg(feature = "project_attr")] -/// impl Foo { -/// #[project] // Nightly does not need a dummy attribute to the function. -/// fn baz(self: Pin<&mut Self>) { -/// #[project] -/// match self.project() { -/// Foo::Tuple(x, y) => { -/// let _: Pin<&mut A> = x; -/// let _: &mut B = y; -/// } -/// Foo::Struct { field } => { -/// let _: &mut C = field; -/// } -/// Foo::Unit => {} -/// } -/// } -/// } -/// ``` -/// -/// Enums without variants (zero-variant enums) are not supported. -/// -/// Also see [`project`] attribute. -/// -/// [`Unpin`]: core::marker::Unpin -/// [`drop`]: Drop::drop -/// [`project`]: ./attr.project.html -#[proc_macro_attribute] -pub fn unsafe_project(args: TokenStream, input: TokenStream) -> TokenStream { - TokenStream::from(unsafe_project::attribute(args.into(), input.into())) -} - -/// An attribute to support pattern matching. -/// -/// *This attribute is available if pin-project is built with the -/// `"project_attr"` feature (it is enabled by default).* -/// -/// ## Examples -/// -/// ### `let` bindings -/// -/// ```rust -/// use pin_project::{project, unsafe_project}; -/// # use std::pin::Pin; -/// -/// #[unsafe_project(Unpin)] -/// struct Foo { -/// #[pin] -/// future: T, -/// field: U, -/// } -/// -/// impl Foo { -/// #[project] // Nightly does not need a dummy attribute to the function. -/// fn baz(self: Pin<&mut Self>) { -/// #[project] -/// let Foo { future, field } = self.project(); -/// -/// let _: Pin<&mut T> = future; -/// let _: &mut U = field; -/// } -/// } -/// ``` -/// -/// ### `match` expressions -/// -/// ```rust -/// use pin_project::{project, unsafe_project}; -/// # use std::pin::Pin; -/// -/// #[unsafe_project(Unpin)] -/// enum Foo { -/// Tuple(#[pin] A, B), -/// Struct { field: C }, -/// Unit, -/// } -/// -/// impl Foo { -/// #[project] // Nightly does not need a dummy attribute to the function. -/// fn baz(self: Pin<&mut Self>) { -/// #[project] -/// match self.project() { -/// Foo::Tuple(x, y) => { -/// let _: Pin<&mut A> = x; -/// let _: &mut B = y; -/// } -/// Foo::Struct { field } => { -/// let _: &mut C = field; -/// } -/// Foo::Unit => {} -/// } -/// } -/// } -/// ``` -/// -/// ### `if let` expressions -/// -/// When used against `if let` expressions, the `#[project]` attribute records -/// the name of the structure destructed with the first `if let`. Destructing -/// different structures in the after second times will not generate wrong code. -/// -/// ```rust -/// use pin_project::{project, unsafe_project}; -/// # use std::pin::Pin; -/// -/// #[unsafe_project(Unpin)] -/// enum Foo { -/// Tuple(#[pin] A, B), -/// Struct { field: C }, -/// Unit, -/// } -/// -/// impl Foo { -/// #[project] // Nightly does not need a dummy attribute to the function. -/// fn baz(self: Pin<&mut Self>) { -/// #[project] -/// { -/// if let Foo::Tuple(x, y) = self.project() { -/// let _: Pin<&mut A> = x; -/// let _: &mut B = y; -/// } -/// } -/// } -/// } -/// ``` -#[cfg(feature = "project_attr")] -#[proc_macro_attribute] -pub fn project(args: TokenStream, input: TokenStream) -> TokenStream { - assert!(args.is_empty()); - TokenStream::from(project::attribute(input.into())) -} diff --git a/src/unsafe_project/mod.rs b/src/unsafe_project/mod.rs deleted file mode 100644 index 57d447ca..00000000 --- a/src/unsafe_project/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -use proc_macro2::{Ident, TokenStream}; -use quote::quote; -use syn::{Generics, Item, Result, Type}; - -mod enums; -mod structs; - -/// The annotation for pinned type. -const PIN: &str = "pin"; - -pub(super) fn attribute(args: TokenStream, input: TokenStream) -> TokenStream { - let span = span!(input); - match syn::parse2(input) { - Ok(Item::Struct(item)) => structs::parse(args, item), - Ok(Item::Enum(item)) => enums::parse(args, item), - _ => Err(error!(span, "may only be used on structs or enums")), - } - .unwrap_or_else(|e| e.to_compile_error()) -} - -/// Makes the generics of projected type from the reference of the original generics. -fn proj_generics(generics: &Generics) -> Generics { - let mut generics = generics.clone(); - generics.params.insert(0, syn::parse_quote!('__a)); - generics -} - -// ================================================================================================= -// conditional Unpin implementation - -#[derive(Default)] -struct ImplUnpin(Option); - -impl ImplUnpin { - /// Parses attribute arguments. - fn new(args: TokenStream, generics: &Generics) -> Result { - match &*args.to_string() { - "" => Ok(Self::default()), - "Unpin" => Ok(Self(Some(generics.clone()))), - _ => Err(error!(args, "an invalid argument was passed")), - } - } - - fn push(&mut self, ty: &Type) { - if let Some(generics) = &mut self.0 { - generics - .make_where_clause() - .predicates - .push(syn::parse_quote!(#ty: ::core::marker::Unpin)); - } - } - - /// Creates `Unpin` implementation. - fn build(self, ident: &Ident) -> TokenStream { - self.0 - .map(|generics| { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - quote! { - impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} - } - }) - .unwrap_or_default() - } -}