From 37419c284dac57e0add43c6909c68d06a0bc0f12 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 18 Jul 2019 22:06:32 -0400 Subject: [PATCH 01/14] Replace #[unsafe_project] with #[pin_projectable] See https://github.com/taiki-e/pin-project/pull/18 for more details --- Cargo.toml | 38 +- README.md | 8 +- pin-project-internal/Cargo.toml | 34 + .../doc}/enum-example-1.md | 0 .../doc}/struct-example-1.md | 0 pin-project-internal/src/lib.rs | 49 ++ .../src/pin_projectable}/enums.rs | 4 +- .../src/pin_projectable/mod.rs | 253 +++++++ .../src/pin_projectable}/structs.rs | 4 +- {src => pin-project-internal/src}/project.rs | 0 {src => pin-project-internal/src}/utils.rs | 0 .../tests}/compile-test.rs | 0 .../tests}/project.rs | 8 +- .../tests/ui/unsafe_project/packed.rs | 20 + .../tests/ui/unsafe_project/packed.stderr | 14 + .../ui/unsafe_project/unpin_conflict.rs | 8 +- .../ui/unsafe_project/unpin_conflict.stderr | 0 .../tests}/ui/unsafe_project/unsupported.rs | 14 +- .../ui/unsafe_project/unsupported.stderr | 0 .../tests}/ui/update-all-references.sh | 0 .../tests}/ui/update-references.sh | 0 .../tests}/unsafe_project.rs | 41 +- pin-project/Cargo.toml | 26 + pin-project/src/lib.rs | 629 ++++++++++++++++++ pin-project/tests/unsafe_unpin.rs | 18 + src/lib.rs | 427 ------------ src/unsafe_project/mod.rs | 64 -- tests/ui/unsafe_project/unpin_conflict.stderr | 30 - 28 files changed, 1104 insertions(+), 585 deletions(-) create mode 100644 pin-project-internal/Cargo.toml rename {doc => pin-project-internal/doc}/enum-example-1.md (100%) rename {doc => pin-project-internal/doc}/struct-example-1.md (100%) create mode 100644 pin-project-internal/src/lib.rs rename {src/unsafe_project => pin-project-internal/src/pin_projectable}/enums.rs (95%) create mode 100644 pin-project-internal/src/pin_projectable/mod.rs rename {src/unsafe_project => pin-project-internal/src/pin_projectable}/structs.rs (93%) rename {src => pin-project-internal/src}/project.rs (100%) rename {src => pin-project-internal/src}/utils.rs (100%) rename {tests => pin-project-internal/tests}/compile-test.rs (100%) rename {tests => pin-project-internal/tests}/project.rs (93%) create mode 100644 pin-project-internal/tests/ui/unsafe_project/packed.rs create mode 100644 pin-project-internal/tests/ui/unsafe_project/packed.stderr rename {tests => pin-project-internal/tests}/ui/unsafe_project/unpin_conflict.rs (89%) create mode 100644 pin-project-internal/tests/ui/unsafe_project/unpin_conflict.stderr rename {tests => pin-project-internal/tests}/ui/unsafe_project/unsupported.rs (78%) rename {tests => pin-project-internal/tests}/ui/unsafe_project/unsupported.stderr (100%) rename {tests => pin-project-internal/tests}/ui/update-all-references.sh (100%) rename {tests => pin-project-internal/tests}/ui/update-references.sh (100%) mode change 100644 => 100755 rename {tests => pin-project-internal/tests}/unsafe_project.rs (80%) create mode 100644 pin-project/Cargo.toml create mode 100644 pin-project/src/lib.rs create mode 100644 pin-project/tests/unsafe_unpin.rs delete mode 100644 src/lib.rs delete mode 100644 src/unsafe_project/mod.rs delete mode 100644 tests/ui/unsafe_project/unpin_conflict.stderr 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 cb17c320..127ff2f3 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,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(Unpin)] // `(Unpin)` is optional (create the appropriate conditional Unpin implementation) struct Foo { #[pin] future: T, @@ -50,7 +50,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..f5c62f7a --- /dev/null +++ b/pin-project-internal/Cargo.toml @@ -0,0 +1,34 @@ +[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"] + + +[dependencies] +proc-macro2 = "0.4.13" +quote = "0.6.8" +syn = { version = "0.15.29", features = ["full", "extra-traits"] } +proc-macro-crate = "0.1.4" +lazy_static = "1.3.0" + +[dev-dependencies] +compiletest = { version = "0.3.21", package = "compiletest_rs", features = ["stable", "tmp"] } 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..ad96ccff --- /dev/null +++ b/pin-project-internal/src/lib.rs @@ -0,0 +1,49 @@ +#![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)] +#![feature(proc_macro_hygiene)] + +extern crate proc_macro; + +#[macro_use] +mod utils; + +#[cfg(feature = "project_attr")] +mod project; +mod pin_projectable; + +use proc_macro::TokenStream; +use lazy_static::lazy_static; + + +#[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())) +} + +lazy_static! { + pub(crate) static ref PIN_PROJECT_CRATE: String = { + let crate_name = proc_macro_crate::crate_name("pin-projectable") + .expect("pin-project-internal was used without pin-project!"); + crate_name + }; +} 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..e7dbc027 100644 --- a/src/unsafe_project/enums.rs +++ b/pin-project-internal/src/pin_projectable/enums.rs @@ -6,7 +6,8 @@ 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 +32,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(PinProject { 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!")) + }; + + let res = handle_type(args, type_, found_pinned_drop.clone()); + res +} + +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")) + } + } + } + } + } + } + } + return 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(ImplDrop { generics, pinned_drop }) + } + + fn build(self, ident: &Ident)-> TokenStream { + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + match self.pinned_drop { + Some(fn_) => { + 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); + } + } + } + }, + None => { + 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: generics.clone(), auto: true }), + "unsafe_Unpin" => Ok(Self { generics: generics.clone(), 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' + + let res = if self.auto { + quote! { + impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} + } + } else { + // 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"); + let pin_project_crate = Ident::new(if cur_crate == "pin-project" { + "pin_project" + } else { + PIN_PROJECT_CRATE.as_str() + }, Span::call_site()); + + 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 {} + } + + + }; + res + } +} 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..db0d7574 100644 --- a/src/unsafe_project/structs.rs +++ b/pin-project-internal/src/pin_projectable/structs.rs @@ -6,7 +6,8 @@ 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 +33,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/pin-project-internal/tests/ui/unsafe_project/packed.rs b/pin-project-internal/tests/ui/unsafe_project/packed.rs new file mode 100644 index 00000000..3db4e46a --- /dev/null +++ b/pin-project-internal/tests/ui/unsafe_project/packed.rs @@ -0,0 +1,20 @@ +// 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 +} + +#[pin_projectable] +#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type +enum Blah { + Tuple(#[pin] u8) +} + + +fn main() {} diff --git a/pin-project-internal/tests/ui/unsafe_project/packed.stderr b/pin-project-internal/tests/ui/unsafe_project/packed.stderr new file mode 100644 index 00000000..74d73bf3 --- /dev/null +++ b/pin-project-internal/tests/ui/unsafe_project/packed.stderr @@ -0,0 +1,14 @@ +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: aborting due to 2 previous errors + diff --git a/tests/ui/unsafe_project/unpin_conflict.rs b/pin-project-internal/tests/ui/unsafe_project/unpin_conflict.rs similarity index 89% rename from tests/ui/unsafe_project/unpin_conflict.rs rename to pin-project-internal/tests/ui/unsafe_project/unpin_conflict.rs index fa7d73c9..d7923483 100644 --- a/tests/ui/unsafe_project/unpin_conflict.rs +++ b/pin-project-internal/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/pin-project-internal/tests/ui/unsafe_project/unpin_conflict.stderr b/pin-project-internal/tests/ui/unsafe_project/unpin_conflict.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/ui/unsafe_project/unsupported.rs b/pin-project-internal/tests/ui/unsafe_project/unsupported.rs similarity index 78% rename from tests/ui/unsafe_project/unsupported.rs rename to pin-project-internal/tests/ui/unsafe_project/unsupported.rs index 7473f8af..79955d08 100644 --- a/tests/ui/unsafe_project/unsupported.rs +++ b/pin-project-internal/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-internal/tests/ui/unsafe_project/unsupported.stderr similarity index 100% rename from tests/ui/unsafe_project/unsupported.stderr rename to pin-project-internal/tests/ui/unsafe_project/unsupported.stderr diff --git a/tests/ui/update-all-references.sh b/pin-project-internal/tests/ui/update-all-references.sh similarity index 100% rename from tests/ui/update-all-references.sh rename to pin-project-internal/tests/ui/update-all-references.sh diff --git a/tests/ui/update-references.sh b/pin-project-internal/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-internal/tests/ui/update-references.sh 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..e59035d3 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_projectable, pin_project}; #[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,40 @@ 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..19cb784f --- /dev/null +++ b/pin-project/Cargo.toml @@ -0,0 +1,26 @@ +[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"] + +[dependencies] +pin-project-internal = { path = "../pin-project-internal" } diff --git a/pin-project/src/lib.rs b/pin-project/src/lib.rs new file mode 100644 index 00000000..b3ddd58c --- /dev/null +++ b/pin-project/src/lib.rs @@ -0,0 +1,629 @@ +//! 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 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 conservatible assume that 'Wrapper>: UnsafeUnpinned' +// holds, in the interest of preserving fowrads 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 `#[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/pin-project/tests/unsafe_unpin.rs b/pin-project/tests/unsafe_unpin.rs new file mode 100644 index 00000000..8b580ded --- /dev/null +++ b/pin-project/tests/unsafe_unpin.rs @@ -0,0 +1,18 @@ +#![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() - } -} diff --git a/tests/ui/unsafe_project/unpin_conflict.stderr b/tests/ui/unsafe_project/unpin_conflict.stderr deleted file mode 100644 index c8c37d38..00000000 --- a/tests/ui/unsafe_project/unpin_conflict.stderr +++ /dev/null @@ -1,30 +0,0 @@ -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<_, _>` -... -26 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl - | --------------------------------------------- first implementation here - -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<_, _>` -... -46 | impl Unpin for Bar {} // Non-conditional Unpin impl - | ------------------------------ first implementation here - -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<_, _>` -... -64 | impl Unpin for Baz {} // Conditional Unpin impl - | -------------------------------------------- first implementation here - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0119`. From 1db86f16eb6e154071420ea5b1a44ef3fb60e775 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 20:20:40 -0400 Subject: [PATCH 02/14] Remove unnecessary nightly feature --- pin-project-internal/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index ad96ccff..5effafb4 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -6,7 +6,6 @@ #![warn(single_use_lifetimes)] #![warn(clippy::all, clippy::pedantic)] #![warn(clippy::nursery)] -#![feature(proc_macro_hygiene)] extern crate proc_macro; From e82696fa2a378bae9c65b806e7d036e25135e64e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 20:21:12 -0400 Subject: [PATCH 03/14] Run 'cargo fmt' --- pin-project-internal/src/lib.rs | 15 +++--- .../src/pin_projectable/enums.rs | 6 ++- .../src/pin_projectable/mod.rs | 46 ++++++++++--------- .../src/pin_projectable/structs.rs | 6 ++- pin-project-internal/tests/project.rs | 2 +- pin-project-internal/tests/unsafe_project.rs | 3 +- pin-project/src/lib.rs | 15 +++--- pin-project/tests/unsafe_unpin.rs | 7 ++- 8 files changed, 55 insertions(+), 45 deletions(-) diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index 5effafb4..a7215dbf 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -12,13 +12,12 @@ extern crate proc_macro; #[macro_use] mod utils; +mod pin_projectable; #[cfg(feature = "project_attr")] mod project; -mod pin_projectable; -use proc_macro::TokenStream; use lazy_static::lazy_static; - +use proc_macro::TokenStream; #[cfg(feature = "project_attr")] #[proc_macro_attribute] @@ -27,16 +26,20 @@ pub fn project(args: TokenStream, input: TokenStream) -> TokenStream { 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())) + 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())) + TokenStream::from( + pin_projectable::attribute(args.into(), input.into()) + .unwrap_or_else(|e| e.to_compile_error()), + ) } lazy_static! { diff --git a/pin-project-internal/src/pin_projectable/enums.rs b/pin-project-internal/src/pin_projectable/enums.rs index e7dbc027..8413697a 100644 --- a/pin-project-internal/src/pin_projectable/enums.rs +++ b/pin-project-internal/src/pin_projectable/enums.rs @@ -6,7 +6,11 @@ use crate::utils::{proj_ident, VecExt}; use super::*; -pub(super) fn parse(args: TokenStream, mut item: ItemEnum, pinned_drop: Option) -> 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)?; diff --git a/pin-project-internal/src/pin_projectable/mod.rs b/pin-project-internal/src/pin_projectable/mod.rs index 7248728a..dc2e1c29 100644 --- a/pin-project-internal/src/pin_projectable/mod.rs +++ b/pin-project-internal/src/pin_projectable/mod.rs @@ -1,8 +1,9 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; -use syn::{Generics, Item, Result, Type, Attribute, Meta, NestedMeta, ItemFn, ItemStruct, ItemEnum}; use syn::parse::{Parse, ParseStream}; - +use syn::{ + Attribute, Generics, Item, ItemEnum, ItemFn, ItemStruct, Meta, NestedMeta, Result, Type, +}; use crate::utils::VecExt; use crate::PIN_PROJECT_CRATE; @@ -16,7 +17,7 @@ const PIN: &str = "pin"; const PINNED_DROP: &str = "pinned_drop"; struct PinProject { - items: Vec + items: Vec, } impl Parse for PinProject { @@ -29,7 +30,7 @@ impl Parse for PinProject { } } -fn handle_type(args: TokenStream, item: Item, pinned_drop: Option) -> Result { +fn handle_type(args: TokenStream, item: Item, pinned_drop: Option) -> Result { match item { Item::Struct(item) => { ensure_not_packed(&item.attrs)?; @@ -38,8 +39,8 @@ fn handle_type(args: TokenStream, item: Item, pinned_drop: Option) -> Re Item::Enum(item) => { ensure_not_packed(&item.attrs)?; Ok(enums::parse(args, item, pinned_drop)?) - }, - _ => panic!("Unexpected item: {:?}", item) + } + _ => panic!("Unexpected item: {:?}", item), } } @@ -88,12 +89,12 @@ pub(super) fn pin_project(input: TokenStream) -> Result { } if found_type.is_none() { - return Err(error!(span, "pin_project must declare a struct or enum")) + 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!")) + None => return Err(error!(span, "No #[pin_projectable] type found!")), }; let res = handle_type(args, type_, found_pinned_drop.clone()); @@ -117,7 +118,10 @@ fn ensure_not_packed(attrs: &[Attribute]) -> Result<()> { 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")) + return Err(error!( + w, + "pin_projectable may not be used on #[repr(packed)] types" + )); } } } @@ -125,7 +129,7 @@ fn ensure_not_packed(attrs: &[Attribute]) -> Result<()> { } } } - return Ok(()) + return Ok(()); } /// Makes the generics of projected type from the reference of the original generics. @@ -137,7 +141,7 @@ fn proj_generics(generics: &Generics) -> Generics { struct ImplDrop { generics: Generics, - pinned_drop: Option + pinned_drop: Option, } impl ImplDrop { @@ -146,7 +150,7 @@ impl ImplDrop { Ok(ImplDrop { generics, pinned_drop }) } - fn build(self, ident: &Ident)-> TokenStream { + fn build(self, ident: &Ident) -> TokenStream { let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); match self.pinned_drop { @@ -171,7 +175,7 @@ impl ImplDrop { } } } - }, + } None => { quote! { impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { @@ -191,7 +195,7 @@ impl ImplDrop { struct ImplUnpin { generics: Generics, - auto: bool + auto: bool, } impl ImplUnpin { @@ -233,20 +237,18 @@ impl ImplUnpin { // 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"); - let pin_project_crate = Ident::new(if cur_crate == "pin-project" { - "pin_project" - } else { - PIN_PROJECT_CRATE.as_str() - }, Span::call_site()); + let cur_crate = std::env::var("CARGO_PKG_NAME") + .expect("Could not find CARGO_PKG_NAME environemnt variable"); + let pin_project_crate = Ident::new( + if cur_crate == "pin-project" { "pin_project" } else { PIN_PROJECT_CRATE.as_str() }, + Span::call_site(), + ); 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 {} } - - }; res } diff --git a/pin-project-internal/src/pin_projectable/structs.rs b/pin-project-internal/src/pin_projectable/structs.rs index db0d7574..d401a414 100644 --- a/pin-project-internal/src/pin_projectable/structs.rs +++ b/pin-project-internal/src/pin_projectable/structs.rs @@ -6,7 +6,11 @@ use crate::utils::{proj_ident, VecExt}; use super::*; -pub(super) fn parse(args: TokenStream, mut item: ItemStruct, pinned_drop: Option) -> 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)?; diff --git a/pin-project-internal/tests/project.rs b/pin-project-internal/tests/project.rs index 7dc9c41f..adc0caca 100644 --- a/pin-project-internal/tests/project.rs +++ b/pin-project-internal/tests/project.rs @@ -6,7 +6,7 @@ #![cfg(feature = "project_attr")] use core::pin::Pin; -use pin_project_internal::{project, pin_projectable}; +use pin_project_internal::{pin_projectable, project}; #[project] // Nightly does not need a dummy attribute to the function. #[test] diff --git a/pin-project-internal/tests/unsafe_project.rs b/pin-project-internal/tests/unsafe_project.rs index e59035d3..5e097ab0 100644 --- a/pin-project-internal/tests/unsafe_project.rs +++ b/pin-project-internal/tests/unsafe_project.rs @@ -5,7 +5,7 @@ #![allow(dead_code)] use core::pin::Pin; -use pin_project_internal::{pin_projectable, pin_project}; +use pin_project_internal::{pin_project, pin_projectable}; #[test] fn test_pin_projectable() { @@ -150,7 +150,6 @@ fn trait_bounds_on_type_generics() { } } - pin_project! { #[pin_projectable] pub struct Foo<'a> { diff --git a/pin-project/src/lib.rs b/pin-project/src/lib.rs index b3ddd58c..913a25af 100644 --- a/pin-project/src/lib.rs +++ b/pin-project/src/lib.rs @@ -140,7 +140,6 @@ #![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 @@ -246,21 +245,21 @@ pub use pin_project_internal::project; /// - 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 +/// 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. +/// 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 @@ -524,7 +523,7 @@ 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. @@ -541,7 +540,7 @@ pub use pin_project_internal::pin_project; /// pinend fields) are also `Unpin`. /// /// To help highlight this unsafety, the `UnsafeUnpin` trait is provided. -/// Implementing this trait is logically equivalent to implemnting `Unpin` - +/// 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!), @@ -553,7 +552,7 @@ pub use pin_project_internal::pin_project; /// 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`. +/// and by tools like `cargo geiger`. /// /// ## Examples /// @@ -589,7 +588,7 @@ pub unsafe trait UnsafeUnpin {} // 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 // diff --git a/pin-project/tests/unsafe_unpin.rs b/pin-project/tests/unsafe_unpin.rs index 8b580ded..5b1c3192 100644 --- a/pin-project/tests/unsafe_unpin.rs +++ b/pin-project/tests/unsafe_unpin.rs @@ -4,15 +4,14 @@ #![warn(rust_2018_idioms)] #![allow(dead_code)] - -use pin_project::{pin_projectable}; +use pin_project::pin_projectable; #[test] fn unsafe_unpin() { #[pin_projectable(unsafe_Unpin)] pub struct Blah { field_1: u8, - #[pin] field_2: Option + #[pin] + field_2: Option, } } - From 500d43f2aa6936edc69c0257b9e3d6735f2fce4c Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 20:47:53 -0400 Subject: [PATCH 04/14] Explicitly depend on newer Serde --- pin-project-internal/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pin-project-internal/Cargo.toml b/pin-project-internal/Cargo.toml index f5c62f7a..95568352 100644 --- a/pin-project-internal/Cargo.toml +++ b/pin-project-internal/Cargo.toml @@ -28,6 +28,9 @@ proc-macro2 = "0.4.13" quote = "0.6.8" syn = { version = "0.15.29", features = ["full", "extra-traits"] } proc-macro-crate = "0.1.4" +# Required until https://github.com/alexcrichton/toml-rs/pull/311 is merged, +# and proc-macro-crate updates to a new version of toml-rs +serde = "1.0.97" lazy_static = "1.3.0" [dev-dependencies] From 01bac2d3027c4cbcb5e905b5ff48c19447719abd Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 20:55:07 -0400 Subject: [PATCH 05/14] Move UI tests into pin-project crate --- pin-project-internal/Cargo.toml | 3 -- .../ui/unsafe_project/unpin_conflict.stderr | 0 pin-project/Cargo.toml | 3 ++ .../tests/compile-test.rs | 0 .../tests/ui/unsafe_project/packed.rs | 0 .../tests/ui/unsafe_project/packed.stderr | 0 .../tests/ui/unsafe_project/unpin_conflict.rs | 0 .../ui/unsafe_project/unpin_conflict.stderr | 30 +++++++++++++++++++ .../tests/ui/unsafe_project/unsupported.rs | 0 .../ui/unsafe_project/unsupported.stderr | 0 .../tests/ui/update-all-references.sh | 0 .../tests/ui/update-references.sh | 0 12 files changed, 33 insertions(+), 3 deletions(-) delete mode 100644 pin-project-internal/tests/ui/unsafe_project/unpin_conflict.stderr rename {pin-project-internal => pin-project}/tests/compile-test.rs (100%) rename {pin-project-internal => pin-project}/tests/ui/unsafe_project/packed.rs (100%) rename {pin-project-internal => pin-project}/tests/ui/unsafe_project/packed.stderr (100%) rename {pin-project-internal => pin-project}/tests/ui/unsafe_project/unpin_conflict.rs (100%) create mode 100644 pin-project/tests/ui/unsafe_project/unpin_conflict.stderr rename {pin-project-internal => pin-project}/tests/ui/unsafe_project/unsupported.rs (100%) rename {pin-project-internal => pin-project}/tests/ui/unsafe_project/unsupported.stderr (100%) rename {pin-project-internal => pin-project}/tests/ui/update-all-references.sh (100%) rename {pin-project-internal => pin-project}/tests/ui/update-references.sh (100%) diff --git a/pin-project-internal/Cargo.toml b/pin-project-internal/Cargo.toml index 95568352..fedd1dbe 100644 --- a/pin-project-internal/Cargo.toml +++ b/pin-project-internal/Cargo.toml @@ -32,6 +32,3 @@ proc-macro-crate = "0.1.4" # and proc-macro-crate updates to a new version of toml-rs serde = "1.0.97" lazy_static = "1.3.0" - -[dev-dependencies] -compiletest = { version = "0.3.21", package = "compiletest_rs", features = ["stable", "tmp"] } diff --git a/pin-project-internal/tests/ui/unsafe_project/unpin_conflict.stderr b/pin-project-internal/tests/ui/unsafe_project/unpin_conflict.stderr deleted file mode 100644 index e69de29b..00000000 diff --git a/pin-project/Cargo.toml b/pin-project/Cargo.toml index 19cb784f..f9bd0cf7 100644 --- a/pin-project/Cargo.toml +++ b/pin-project/Cargo.toml @@ -24,3 +24,6 @@ project_attr = ["pin-project-internal/project_attr"] [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-internal/tests/compile-test.rs b/pin-project/tests/compile-test.rs similarity index 100% rename from pin-project-internal/tests/compile-test.rs rename to pin-project/tests/compile-test.rs diff --git a/pin-project-internal/tests/ui/unsafe_project/packed.rs b/pin-project/tests/ui/unsafe_project/packed.rs similarity index 100% rename from pin-project-internal/tests/ui/unsafe_project/packed.rs rename to pin-project/tests/ui/unsafe_project/packed.rs diff --git a/pin-project-internal/tests/ui/unsafe_project/packed.stderr b/pin-project/tests/ui/unsafe_project/packed.stderr similarity index 100% rename from pin-project-internal/tests/ui/unsafe_project/packed.stderr rename to pin-project/tests/ui/unsafe_project/packed.stderr diff --git a/pin-project-internal/tests/ui/unsafe_project/unpin_conflict.rs b/pin-project/tests/ui/unsafe_project/unpin_conflict.rs similarity index 100% rename from pin-project-internal/tests/ui/unsafe_project/unpin_conflict.rs rename to pin-project/tests/ui/unsafe_project/unpin_conflict.rs diff --git a/pin-project/tests/ui/unsafe_project/unpin_conflict.stderr b/pin-project/tests/ui/unsafe_project/unpin_conflict.stderr new file mode 100644 index 00000000..7baa4bef --- /dev/null +++ b/pin-project/tests/ui/unsafe_project/unpin_conflict.stderr @@ -0,0 +1,30 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: + --> $DIR/unpin_conflict.rs:10:1 + | +10 | #[pin_projectable] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +26 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl + | --------------------------------------------- first implementation here + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: + --> $DIR/unpin_conflict.rs:30:1 + | +30 | #[pin_projectable] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +46 | impl Unpin for Bar {} // Non-conditional Unpin impl + | ------------------------------ first implementation here + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: + --> $DIR/unpin_conflict.rs:48:1 + | +48 | #[pin_projectable] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +64 | impl Unpin for Baz {} // Conditional Unpin impl + | -------------------------------------------- first implementation here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0119`. diff --git a/pin-project-internal/tests/ui/unsafe_project/unsupported.rs b/pin-project/tests/ui/unsafe_project/unsupported.rs similarity index 100% rename from pin-project-internal/tests/ui/unsafe_project/unsupported.rs rename to pin-project/tests/ui/unsafe_project/unsupported.rs diff --git a/pin-project-internal/tests/ui/unsafe_project/unsupported.stderr b/pin-project/tests/ui/unsafe_project/unsupported.stderr similarity index 100% rename from pin-project-internal/tests/ui/unsafe_project/unsupported.stderr rename to pin-project/tests/ui/unsafe_project/unsupported.stderr diff --git a/pin-project-internal/tests/ui/update-all-references.sh b/pin-project/tests/ui/update-all-references.sh similarity index 100% rename from pin-project-internal/tests/ui/update-all-references.sh rename to pin-project/tests/ui/update-all-references.sh diff --git a/pin-project-internal/tests/ui/update-references.sh b/pin-project/tests/ui/update-references.sh similarity index 100% rename from pin-project-internal/tests/ui/update-references.sh rename to pin-project/tests/ui/update-references.sh From 1d81d65114b24a707c6abaf468fa9c1a9e87fa58 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 21:12:58 -0400 Subject: [PATCH 06/14] Fix crate name --- pin-project-internal/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index a7215dbf..e72ac872 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -44,7 +44,7 @@ pub fn pin_projectable(args: TokenStream, input: TokenStream) -> TokenStream { lazy_static! { pub(crate) static ref PIN_PROJECT_CRATE: String = { - let crate_name = proc_macro_crate::crate_name("pin-projectable") + let crate_name = proc_macro_crate::crate_name("pin-project") .expect("pin-project-internal was used without pin-project!"); crate_name }; From 23f954635474effaf207395b6320460f6c28d3d3 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 27 Jul 2019 21:16:32 -0400 Subject: [PATCH 07/14] Fix typos --- pin-project/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pin-project/src/lib.rs b/pin-project/src/lib.rs index 913a25af..657901f7 100644 --- a/pin-project/src/lib.rs +++ b/pin-project/src/lib.rs @@ -602,7 +602,7 @@ pub unsafe trait UnsafeUnpin {} // The solution is to introduce the 'Wrapper' struct, which is defined // in the 'pin-project' crate. // -// We now have that looks like this: +// 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 @@ -611,14 +611,14 @@ pub unsafe trait UnsafeUnpin {} // 'pin-project' crate. // // Now, our generated impl has a bound involving a type defined in another crate - Wrapper. -// This will cause rust to conservatible assume that 'Wrapper>: UnsafeUnpinned' -// holds, in the interest of preserving fowrads compatibility (in case such an impl is added +// 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 `#[pin_projectable(unsafe_Unpin)]`, +// 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` From 46779b31a9376114dab7c7998519f7f97651b396 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 28 Jul 2019 13:22:26 -0400 Subject: [PATCH 08/14] Move renamed dependency support behind a feature --- pin-project-internal/Cargo.toml | 8 ++-- pin-project-internal/src/lib.rs | 4 +- .../src/pin_projectable/mod.rs | 42 ++++++++++++------- pin-project/Cargo.toml | 2 + 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/pin-project-internal/Cargo.toml b/pin-project-internal/Cargo.toml index fedd1dbe..f744793c 100644 --- a/pin-project-internal/Cargo.toml +++ b/pin-project-internal/Cargo.toml @@ -21,14 +21,16 @@ proc-macro = true 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"] [dependencies] proc-macro2 = "0.4.13" quote = "0.6.8" syn = { version = "0.15.29", features = ["full", "extra-traits"] } -proc-macro-crate = "0.1.4" -# Required until https://github.com/alexcrichton/toml-rs/pull/311 is merged, -# and proc-macro-crate updates to a new version of toml-rs +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 = "1.0.97" lazy_static = "1.3.0" diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index e72ac872..bae52b97 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -16,7 +16,6 @@ mod pin_projectable; #[cfg(feature = "project_attr")] mod project; -use lazy_static::lazy_static; use proc_macro::TokenStream; #[cfg(feature = "project_attr")] @@ -42,7 +41,8 @@ pub fn pin_projectable(args: TokenStream, input: TokenStream) -> TokenStream { ) } -lazy_static! { +#[cfg(feature = "renamed")] +lazy_static::lazy_static! { pub(crate) static ref PIN_PROJECT_CRATE: String = { let crate_name = proc_macro_crate::crate_name("pin-project") .expect("pin-project-internal was used without pin-project!"); diff --git a/pin-project-internal/src/pin_projectable/mod.rs b/pin-project-internal/src/pin_projectable/mod.rs index dc2e1c29..2d74924c 100644 --- a/pin-project-internal/src/pin_projectable/mod.rs +++ b/pin-project-internal/src/pin_projectable/mod.rs @@ -6,7 +6,6 @@ use syn::{ }; use crate::utils::VecExt; -use crate::PIN_PROJECT_CRATE; mod enums; mod structs; @@ -231,19 +230,7 @@ impl ImplUnpin { impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} } } else { - // 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"); - let pin_project_crate = Ident::new( - if cur_crate == "pin-project" { "pin_project" } else { PIN_PROJECT_CRATE.as_str() }, - Span::call_site(), - ); - + 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! { @@ -253,3 +240,30 @@ impl ImplUnpin { res } } + +/// 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/pin-project/Cargo.toml b/pin-project/Cargo.toml index f9bd0cf7..44507c19 100644 --- a/pin-project/Cargo.toml +++ b/pin-project/Cargo.toml @@ -21,6 +21,8 @@ travis-ci = { repository = "taiki-e/pin-project" } 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" } From 8942555b8c75e760ca1c90e7e9cabfc574086414 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 28 Jul 2019 13:22:42 -0400 Subject: [PATCH 09/14] Run 'cargo fmt' --- pin-project-internal/src/pin_projectable/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pin-project-internal/src/pin_projectable/mod.rs b/pin-project-internal/src/pin_projectable/mod.rs index 2d74924c..55eb833e 100644 --- a/pin-project-internal/src/pin_projectable/mod.rs +++ b/pin-project-internal/src/pin_projectable/mod.rs @@ -266,4 +266,3 @@ fn pin_project_crate_path() -> Ident { fn pin_project_crate_path() -> Ident { Ident::new("pin_project", Span::call_site()) } - From f445dfd6b5e7f7ebf1853497bf46544683cc78e1 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 28 Jul 2019 13:24:31 -0400 Subject: [PATCH 10/14] Remove lingering 'Unpin' parameter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 127ff2f3..e9aef746 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ The current version of pin-project requires Rust 1.33 or later. use pin_project::pin_projectable; use std::pin::Pin; -#[pin_projectable(Unpin)] // `(Unpin)` is optional (create the appropriate conditional Unpin implementation) +#[pin_projectable] struct Foo { #[pin] future: T, From 87668a9f00c97569417ed225a2f11aa0d7877590 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 28 Jul 2019 13:28:43 -0400 Subject: [PATCH 11/14] Test that the relative ordering of `#[pin_projectable]` and `#[repr(packed)]` doesn't matter --- pin-project/tests/ui/unsafe_project/packed.rs | 14 ++++++++++++++ pin-project/tests/ui/unsafe_project/packed.stderr | 14 +++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/pin-project/tests/ui/unsafe_project/packed.rs b/pin-project/tests/ui/unsafe_project/packed.rs index 3db4e46a..56e45015 100644 --- a/pin-project/tests/ui/unsafe_project/packed.rs +++ b/pin-project/tests/ui/unsafe_project/packed.rs @@ -10,11 +10,25 @@ struct Foo { 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 index 74d73bf3..3c5790f9 100644 --- a/pin-project/tests/ui/unsafe_project/packed.stderr +++ b/pin-project/tests/ui/unsafe_project/packed.stderr @@ -10,5 +10,17 @@ error: pin_projectable may not be used on #[repr(packed)] types 14 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ^^^^^^ -error: aborting due to 2 previous errors +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 From 94faf6d9af32cdc892e4ea4534cc4264c2c57c0d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 28 Jul 2019 14:05:22 -0400 Subject: [PATCH 12/14] Move 'serde' and 'lazy_static' behind the 'renamed' feature --- pin-project-internal/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pin-project-internal/Cargo.toml b/pin-project-internal/Cargo.toml index f744793c..a899616c 100644 --- a/pin-project-internal/Cargo.toml +++ b/pin-project-internal/Cargo.toml @@ -22,7 +22,7 @@ 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"] +renamed = ["proc-macro-crate", "serde", "lazy_static"] [dependencies] @@ -32,5 +32,5 @@ 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 = "1.0.97" -lazy_static = "1.3.0" +serde = { version = "1.0.97", optional = true } +lazy_static = { version = "1.3.0", optional = true } From 2433d778e040b9251c0c2320c9b4aabf00bc72ba Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 4 Aug 2019 23:01:56 -0400 Subject: [PATCH 13/14] Fix clippy lints --- .../src/pin_projectable/mod.rs | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/pin-project-internal/src/pin_projectable/mod.rs b/pin-project-internal/src/pin_projectable/mod.rs index 55eb833e..131f2985 100644 --- a/pin-project-internal/src/pin_projectable/mod.rs +++ b/pin-project-internal/src/pin_projectable/mod.rs @@ -25,7 +25,7 @@ impl Parse for PinProject { while !input.is_empty() { items.push(input.parse()?); } - Ok(PinProject { items }) + Ok(Self { items }) } } @@ -96,8 +96,7 @@ pub(super) fn pin_project(input: TokenStream) -> Result { None => return Err(error!(span, "No #[pin_projectable] type found!")), }; - let res = handle_type(args, type_, found_pinned_drop.clone()); - res + handle_type(args, type_, found_pinned_drop) } pub(super) fn attribute(args: TokenStream, input: TokenStream) -> Result { @@ -128,7 +127,7 @@ fn ensure_not_packed(attrs: &[Attribute]) -> Result<()> { } } } - return Ok(()); + Ok(()) } /// Makes the generics of projected type from the reference of the original generics. @@ -146,42 +145,39 @@ struct ImplDrop { impl ImplDrop { /// Parses attribute arguments. fn new(generics: Generics, pinned_drop: Option) -> Result { - Ok(ImplDrop { generics, pinned_drop }) + Ok(Self { generics, pinned_drop }) } fn build(self, ident: &Ident) -> TokenStream { let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); - match self.pinned_drop { - Some(fn_) => { - 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); - } + 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); } } } - None => { - 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 - } + } 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 } } } @@ -204,8 +200,8 @@ impl ImplUnpin { generics.make_where_clause(); match &*args.to_string() { - "" => Ok(Self { generics: generics.clone(), auto: true }), - "unsafe_Unpin" => Ok(Self { generics: generics.clone(), auto: false }), + "" => Ok(Self { generics, auto: true }), + "unsafe_Unpin" => Ok(Self { generics, auto: false }), _ => Err(error!(args, "an invalid argument was passed")), } } @@ -225,7 +221,7 @@ impl ImplUnpin { let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); let mut where_clause = where_clause.unwrap().clone(); // Created in 'new' - let res = if self.auto { + if self.auto { quote! { impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} } @@ -236,8 +232,7 @@ impl ImplUnpin { quote! { impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} } - }; - res + } } } From 60e4242e3ba21c399c6431e38c580179f4598f7a Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 4 Aug 2019 23:06:51 -0400 Subject: [PATCH 14/14] Fix missed clippy lint --- pin-project-internal/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index bae52b97..de76a08f 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -44,8 +44,7 @@ pub fn pin_projectable(args: TokenStream, input: TokenStream) -> TokenStream { #[cfg(feature = "renamed")] lazy_static::lazy_static! { pub(crate) static ref PIN_PROJECT_CRATE: String = { - let crate_name = proc_macro_crate::crate_name("pin-project") - .expect("pin-project-internal was used without pin-project!"); - crate_name + proc_macro_crate::crate_name("pin-project") + .expect("pin-project-internal was used without pin-project!") }; }