From 2746521c959642da4285338795d4adaea8c9e4b5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 7 Aug 2019 03:42:32 +0900 Subject: [PATCH] Remove pin_project! function-like macro and add #[pinned_drop] attribute --- README.md | 8 +- pin-project-internal/src/lib.rs | 19 +- .../{pin_projectable => pin_project}/enums.rs | 10 +- pin-project-internal/src/pin_project/mod.rs | 185 +++++++++++++ .../structs.rs | 10 +- .../src/pin_projectable/mod.rs | 259 ------------------ pin-project-internal/src/pinned_drop.rs | 68 +++++ pin-project-internal/src/utils.rs | 27 +- src/lib.rs | 239 ++++++++-------- tests/{pin_projectable.rs => pin_project.rs} | 40 +-- tests/pinned_drop.rs | 27 ++ tests/project.rs | 8 +- tests/ui/pin_project/drop_conflict.rs | 19 ++ tests/ui/pin_project/drop_conflict.stderr | 12 + tests/ui/pin_project/forget-unsafe-unpin.rs | 20 ++ .../ui/pin_project/forget-unsafe-unpin.stderr | 0 .../invalid.rs | 12 +- .../invalid.stderr | 0 .../packed.rs | 14 +- .../packed.stderr | 8 +- tests/ui/pin_project/unpin_conflict.rs | 42 +++ .../unpin_conflict.stderr | 22 +- .../unsupported.rs | 16 +- .../unsupported.stderr | 0 tests/ui/pin_projectable/unpin_conflict.rs | 66 ----- tests/ui/pinned_drop/forget-pinned-drop.rs | 15 + .../ui/pinned_drop/forget-pinned-drop.stderr | 11 + tests/ui/pinned_drop/invalid.rs | 29 +- tests/ui/pinned_drop/invalid.stderr | 14 +- tests/ui/pinned_drop/return-type.rs | 46 ++-- tests/ui/pinned_drop/return-type.stderr | 6 +- tests/ui/project/invalid.rs | 6 +- tests/unsafe_unpin.rs | 7 +- 33 files changed, 679 insertions(+), 586 deletions(-) rename pin-project-internal/src/{pin_projectable => pin_project}/enums.rs (95%) create mode 100644 pin-project-internal/src/pin_project/mod.rs rename pin-project-internal/src/{pin_projectable => pin_project}/structs.rs (93%) delete mode 100644 pin-project-internal/src/pin_projectable/mod.rs create mode 100644 pin-project-internal/src/pinned_drop.rs rename tests/{pin_projectable.rs => pin_project.rs} (80%) create mode 100644 tests/pinned_drop.rs create mode 100644 tests/ui/pin_project/drop_conflict.rs create mode 100644 tests/ui/pin_project/drop_conflict.stderr create mode 100644 tests/ui/pin_project/forget-unsafe-unpin.rs create mode 100644 tests/ui/pin_project/forget-unsafe-unpin.stderr rename tests/ui/{pin_projectable => pin_project}/invalid.rs (71%) rename tests/ui/{pin_projectable => pin_project}/invalid.stderr (100%) rename tests/ui/{pin_projectable => pin_project}/packed.rs (71%) rename tests/ui/{pin_projectable => pin_project}/packed.stderr (67%) create mode 100644 tests/ui/pin_project/unpin_conflict.rs rename tests/ui/{pin_projectable => pin_project}/unpin_conflict.stderr (54%) rename tests/ui/{pin_projectable => pin_project}/unsupported.rs (75%) rename tests/ui/{pin_projectable => pin_project}/unsupported.stderr (100%) delete mode 100644 tests/ui/pin_projectable/unpin_conflict.rs create mode 100644 tests/ui/pinned_drop/forget-pinned-drop.rs create mode 100644 tests/ui/pinned_drop/forget-pinned-drop.stderr diff --git a/README.md b/README.md index 644cb61c..982a0ccc 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ The current version of pin-project requires Rust 1.33 or later. ## Examples -[`pin_projectable`] attribute creates a projection struct covering all the fields. +[`pin_project`] attribute creates a projection struct covering all the fields. ```rust -use pin_project::pin_projectable; +use pin_project::pin_project; use std::pin::Pin; -#[pin_projectable] +#[pin_project] struct Foo { #[pin] future: T, @@ -58,7 +58,7 @@ impl Foo { [Code like this will be generated](doc/struct-example-1.md) -[`pin_projectable`]: https://docs.rs/pin-project/0.3/pin_project/attr.pin_projectable.html +[`pin_project`]: https://docs.rs/pin-project/0.3/pin_project/attr.pin_project.html ## License diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index dc405081..178dad8a 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -11,13 +11,14 @@ extern crate proc_macro; #[macro_use] mod utils; -mod pin_projectable; +mod pin_project; +mod pinned_drop; #[cfg(feature = "project_attr")] mod project; use proc_macro::TokenStream; -use crate::utils::Nothing; +use utils::Nothing; #[cfg(feature = "project_attr")] #[proc_macro_attribute] @@ -26,17 +27,15 @@ pub fn project(args: TokenStream, input: TokenStream) -> TokenStream { project::attribute(input.into()).into() } -/// This is a doc comment from the defining crate! -#[proc_macro] -pub fn pin_project(input: TokenStream) -> TokenStream { - pin_projectable::pin_project(input.into()).unwrap_or_else(|e| e.to_compile_error()).into() +#[proc_macro_attribute] +pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { + pin_project::attribute(args.into(), input.into()).into() } #[proc_macro_attribute] -pub fn pin_projectable(args: TokenStream, input: TokenStream) -> TokenStream { - pin_projectable::attribute(args.into(), input.into()) - .unwrap_or_else(|e| e.to_compile_error()) - .into() +pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { + let _: Nothing = syn::parse_macro_input!(args); + pinned_drop::attribute(input.into()).into() } #[cfg(feature = "renamed")] diff --git a/pin-project-internal/src/pin_projectable/enums.rs b/pin-project-internal/src/pin_project/enums.rs similarity index 95% rename from pin-project-internal/src/pin_projectable/enums.rs rename to pin-project-internal/src/pin_project/enums.rs index ef1967f0..b98ad50b 100644 --- a/pin-project-internal/src/pin_projectable/enums.rs +++ b/pin-project-internal/src/pin_project/enums.rs @@ -6,13 +6,8 @@ use crate::utils::{proj_ident, VecExt}; use super::*; -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)?; +pub(super) fn parse(args: Args, mut item: ItemEnum) -> Result { + let mut impl_unpin = args.impl_unpin(&item.generics); if item.variants.is_empty() { return Err(error!(item, "cannot be implemented for enums without variants")); @@ -34,6 +29,7 @@ pub(super) fn parse( let (proj_item_body, proj_arms) = variants(&mut item, &proj_ident, &mut impl_unpin)?; let ident = &item.ident; + let impl_drop = args.impl_drop(&item.generics); let proj_generics = proj_generics(&item.generics); let proj_ty_generics = proj_generics.split_for_impl().1; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); diff --git a/pin-project-internal/src/pin_project/mod.rs b/pin-project-internal/src/pin_project/mod.rs new file mode 100644 index 00000000..f8f1d6c3 --- /dev/null +++ b/pin-project-internal/src/pin_project/mod.rs @@ -0,0 +1,185 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, quote_spanned}; +use syn::{ + parse::{Parse, ParseStream}, + Attribute, Generics, Item, Meta, NestedMeta, Result, Type, +}; + +use crate::utils::{crate_path, Nothing}; + +mod enums; +mod structs; + +/// The annotation for pinned type. +const PIN: &str = "pin"; + +pub(super) fn attribute(args: TokenStream, input: TokenStream) -> TokenStream { + parse(args, input).unwrap_or_else(|e| e.to_compile_error()) +} + +#[derive(Clone, Copy)] +struct Args { + pinned_drop: Option, + unsafe_unpin: Option, +} + +impl Args { + fn impl_drop(self, generics: &Generics) -> ImplDrop<'_> { + ImplDrop::new(generics, self.pinned_drop) + } + + fn impl_unpin(self, generics: &Generics) -> ImplUnpin { + ImplUnpin::new(generics, self.unsafe_unpin) + } +} + +impl Parse for Args { + fn parse(input: ParseStream<'_>) -> Result { + let mut pinned_drop = None; + let mut unsafe_unpin = None; + while !input.is_empty() { + let i = input.parse::()?; + match &*i.to_string() { + "PinnedDrop" => pinned_drop = Some(i.span()), + "unsafe_Unpin" => unsafe_unpin = Some(i.span()), + _ => return Err(error!(i, "an invalid argument was passed")), + } + } + Ok(Self { pinned_drop, unsafe_unpin }) + } +} + +fn parse(args: TokenStream, input: TokenStream) -> Result { + let args = syn::parse2(args)?; + match syn::parse2(input)? { + Item::Struct(item) => { + ensure_not_packed(&item.attrs)?; + structs::parse(args, item) + } + Item::Enum(item) => { + ensure_not_packed(&item.attrs)?; + enums::parse(args, item) + } + item => Err(error!(item, "may only be used on structs or enums")), + } +} + +fn ensure_not_packed(attrs: &[Attribute]) -> Result<()> { + for meta in attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + 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_project may not be used on #[repr(packed)] types" + )); + } + } + } + } + } + } + Ok(()) +} + +/// Makes the generics of projected type from the reference of the original generics. +fn proj_generics(generics: &Generics) -> Generics { + let mut generics = generics.clone(); + generics.params.insert(0, syn::parse_quote!('__a)); + generics +} + +// ================================================================================================= +// Drop implementation + +struct ImplDrop<'a> { + generics: &'a Generics, + pinned_drop: Option, +} + +impl<'a> ImplDrop<'a> { + fn new(generics: &'a Generics, pinned_drop: Option) -> Self { + Self { generics, pinned_drop } + } + + fn build(self, ident: &Ident) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + if let Some(pinned_drop) = self.pinned_drop { + let crate_path = crate_path(); + let call = quote_spanned! { pinned_drop => + ::#crate_path::__private::UnsafePinnedDrop::pinned_drop(pinned_self) + }; + + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { + fn drop(&mut self) { + // Safety - we're in 'drop', so we know that 'self' will + // never move again + let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) }; + // We call `pinned_drop` only once. Since `UnsafePinnedDrop::pinned_drop` + // is an unsafe function and a private API, it is never called again in safe + // code *unless the user uses a maliciously crafted macro*. + unsafe { + #call; + } + } + } + } + } else { + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { + fn drop(&mut self) { + // Do nothing. The precense of this Drop + // impl ensures that the user can't provide one of their own + } + } + } + } + } +} + +// ================================================================================================= +// conditional Unpin implementation + +struct ImplUnpin { + generics: Generics, + unsafe_unpin: bool, +} + +impl ImplUnpin { + fn new(generics: &Generics, unsafe_unpin: Option) -> Self { + let mut generics = generics.clone(); + if let Some(unsafe_unpin) = unsafe_unpin { + let crate_path = crate_path(); + generics.make_where_clause().predicates.push( + syn::parse2(quote_spanned! { unsafe_unpin => + ::#crate_path::__private::Wrapper: ::#crate_path::UnsafeUnpin + }) + .unwrap(), + ); + } + + Self { generics, unsafe_unpin: unsafe_unpin.is_some() } + } + + fn push(&mut self, ty: &Type) { + // We only add bounds for automatically generated impls + if !self.unsafe_unpin { + 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(); + quote! { + impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} + } + } +} diff --git a/pin-project-internal/src/pin_projectable/structs.rs b/pin-project-internal/src/pin_project/structs.rs similarity index 93% rename from pin-project-internal/src/pin_projectable/structs.rs rename to pin-project-internal/src/pin_project/structs.rs index 41220f5c..8a695beb 100644 --- a/pin-project-internal/src/pin_projectable/structs.rs +++ b/pin-project-internal/src/pin_project/structs.rs @@ -6,13 +6,8 @@ use crate::utils::{proj_ident, Nothing, VecExt}; use super::*; -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)?; +pub(super) fn parse(args: Args, mut item: ItemStruct) -> Result { + let mut impl_unpin = args.impl_unpin(&item.generics); let (proj_item_body, proj_init_body) = match &mut item.fields { Fields::Named(FieldsNamed { named: fields, .. }) @@ -28,6 +23,7 @@ pub(super) fn parse( }; let ident = &item.ident; + let impl_drop = args.impl_drop(&item.generics); let proj_ident = proj_ident(&item.ident); let proj_generics = proj_generics(&item.generics); let proj_ty_generics = proj_generics.split_for_impl().1; diff --git a/pin-project-internal/src/pin_projectable/mod.rs b/pin-project-internal/src/pin_projectable/mod.rs deleted file mode 100644 index e2954f47..00000000 --- a/pin-project-internal/src/pin_projectable/mod.rs +++ /dev/null @@ -1,259 +0,0 @@ -use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, ToTokens}; -use syn::parse::{Parse, ParseStream}; -use syn::{ - Attribute, Generics, Item, ItemEnum, ItemFn, ItemStruct, Meta, NestedMeta, Result, ReturnType, - Type, TypeTuple, -}; - -use crate::utils::{Nothing, VecExt}; - -mod enums; -mod structs; - -/// The annotation for pinned type. -const PIN: &str = "pin"; - -const PINNED_DROP: &str = "pinned_drop"; - -struct PinProject { - items: Vec, -} - -impl Parse for PinProject { - fn parse(input: ParseStream<'_>) -> Result { - let mut items = vec![]; - while !input.is_empty() { - items.push(input.parse()?); - } - Ok(Self { items }) - } -} - -fn handle_type(args: TokenStream, item: Item, pinned_drop: Option) -> Result { - match item { - Item::Struct(item) => { - ensure_not_packed(&item.attrs)?; - structs::parse(args, item, pinned_drop) - } - Item::Enum(item) => { - ensure_not_packed(&item.attrs)?; - enums::parse(args, item, pinned_drop) - } - _ => unreachable!(), - } -} - -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(attr) = attrs.find_remove("pin_projectable") { - 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!(item, "type declared in pin_project! must have pin_projectable attribute")) - } - } else { - return Err(error!(item, "cannot declare multiple types within pinned module")) - } - }, - Item::Fn(fn_) => { - if let Some(attr)= fn_.attrs.find_remove(PINNED_DROP) { - let _: Nothing = syn::parse2(attr.tts)?; - if found_pinned_drop.is_none() { - if let ReturnType::Type(_, ty) = &fn_.decl.output { - match &**ty { - Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => {} - _ => return Err(error!(ty, "#[pinned_drop] function must return the unit type")), - } - } - found_pinned_drop = Some(fn_.clone()); - } else { - return Err(error!(fn_, "cannot declare multiple functions within pinned module")); - } - } else { - return Err(error!(fn_, "only #[pinned_drop] functions can be declared within a pin_project! block")); - } - }, - _ => return Err(error!(item, "pin_project! block may only contain a struct/enum with an optional #[pinned_drop] function")) - } - } - - let (type_, args) = match found_type { - Some(t) => t, - None => return Err(error!(span, "No #[pin_projectable] type found!")), - }; - - handle_type(args, type_, found_pinned_drop) -} - -pub(super) fn attribute(args: TokenStream, input: TokenStream) -> Result { - let item = syn::parse2(input)?; - match &item { - Item::Struct(_) | Item::Enum(_) => handle_type(args, item, None), - _ => Err(error!(item, "may only be used on structs or enums")), - } -} - -fn ensure_not_packed(attrs: &[Attribute]) -> Result<()> { - for meta in attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - if let Meta::List(l) = meta { - if l.ident == "repr" { - for repr in l.nested.iter() { - if let NestedMeta::Meta(Meta::Word(w)) = repr { - if w == "packed" { - return Err(error!( - w, - "pin_projectable may not be used on #[repr(packed)] types" - )); - } - } - } - } - } - } - Ok(()) -} - -/// Makes the generics of projected type from the reference of the original generics. -fn proj_generics(generics: &Generics) -> Generics { - let mut generics = generics.clone(); - generics.params.insert(0, syn::parse_quote!('__a)); - generics -} - -struct ImplDrop { - generics: Generics, - pinned_drop: Option, -} - -impl ImplDrop { - /// Parses attribute arguments. - fn new(generics: Generics, pinned_drop: Option) -> Result { - Ok(Self { generics, pinned_drop }) - } - - fn build(self, ident: &Ident) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); - - if let Some(fn_) = self.pinned_drop { - let fn_name = fn_.ident.clone(); - quote! { - impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { - fn drop(&mut self) { - // Declare the #[pinned_drop] function *inside* our drop function - // This guarantees that it's impossible for any other user code - // to call it - #fn_ - // Safety - we're in 'drop', so we know that 'self' will - // never move again - let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) }; - // 'pinned_drop' is a free function - if it were part of a trait impl, - // it would be possible for user code to call it by directly invoking - // the trait. - #fn_name(pinned_self); - } - } - } - } else { - quote! { - impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { - fn drop(&mut self) { - // Do nothing. The precense of this Drop - // impl ensures that the user can't provide one of their own - } - } - } - } - } -} - -// ================================================================================================= -// conditional Unpin implementation - -struct ImplUnpin { - generics: Generics, - auto: bool, -} - -impl ImplUnpin { - /// Parses attribute arguments. - fn new(args: TokenStream, generics: &Generics) -> Result { - let mut generics = generics.clone(); - generics.make_where_clause(); - - match &*args.to_string() { - "" => Ok(Self { generics, auto: true }), - "unsafe_Unpin" => Ok(Self { generics, auto: false }), - _ => Err(error!(args, "an invalid argument was passed")), - } - } - - fn push(&mut self, ty: &Type) { - // We only add bounds for automatically generated impls - if self.auto { - self.generics - .make_where_clause() - .predicates - .push(syn::parse_quote!(#ty: ::core::marker::Unpin)); - } - } - - /// Creates `Unpin` implementation. - fn build(self, ident: &Ident) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); - let mut where_clause = where_clause.unwrap().clone(); // Created in 'new' - - if self.auto { - quote! { - impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} - } - } else { - let pin_project_crate = pin_project_crate_path(); - where_clause.predicates.push(syn::parse_quote!(::#pin_project_crate::Wrapper<#ident #ty_generics>: ::#pin_project_crate::UnsafeUnpin)); - - quote! { - impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} - } - } - } -} - -/// If the 'renamed' feature is enabled, we detect -/// the actual name of the 'pin-project' crate in the consumer's Cargo.toml -#[cfg(feature = "renamed")] -fn pin_project_crate_path() -> Ident { - use crate::PIN_PROJECT_CRATE; - // This is fairly subtle. - // Normally, you would use `env!("CARGO_PKG_NAME")` to get the name of the package, - // since it's set at compile time. - // However, we're in a proc macro, which runs while *another* crate is being compiled. - // By retreiving the runtime value of `CARGO_PKG_NAME`, we can figure out the name - // of the crate that's calling us. - let cur_crate = std::env::var("CARGO_PKG_NAME") - .expect("Could not find CARGO_PKG_NAME environemnt variable"); - Ident::new( - if cur_crate == "pin-project" { "pin_project" } else { PIN_PROJECT_CRATE.as_str() }, - Span::call_site(), - ) -} - -/// If the 'renamed' feature is not enabled, we just -/// assume that the 'pin-project' dependency has not been renamed -#[cfg(not(feature = "renamed"))] -fn pin_project_crate_path() -> Ident { - Ident::new("pin_project", Span::call_site()) -} diff --git a/pin-project-internal/src/pinned_drop.rs b/pin-project-internal/src/pinned_drop.rs new file mode 100644 index 00000000..59f80d7b --- /dev/null +++ b/pin-project-internal/src/pinned_drop.rs @@ -0,0 +1,68 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Result, *}; + +use crate::utils::crate_path; + +pub(super) fn attribute(input: TokenStream) -> TokenStream { + parse(input).unwrap_or_else(|e| e.to_compile_error()) +} + +fn parse_arg(arg: &FnArg) -> Result<&Type> { + if let FnArg::Captured(ArgCaptured { ty: Type::Path(TypePath { qself: None, path }), .. }) = + &arg + { + let ty = &path.segments[path.segments.len() - 1]; + if let PathArguments::AngleBracketed(args) = &ty.arguments { + if args.args.len() == 1 && ty.ident == "Pin" { + // &mut + if let GenericArgument::Type(Type::Reference(TypeReference { + mutability: Some(_), + elem, + .. + })) = &args.args[0] + { + return Ok(&**elem); + } + } + } + } + + Err(error!(arg.clone(), "#[pinned_drop] function must take a argument `Pin<&mut Type>`")) +} + +fn parse(input: TokenStream) -> Result { + let item: ItemFn = syn::parse2(input)?; + if let ReturnType::Type(_, ty) = &item.decl.output { + match &**ty { + Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => {} + _ => return Err(error!(ty, "#[pinned_drop] function must return the unit type")), + } + } + if item.decl.inputs.len() != 1 { + return Err(error!( + item.decl.inputs, + "#[pinned_drop] function must take exactly one argument" + )); + } + + let crate_path = crate_path(); + let type_ = parse_arg(&item.decl.inputs[0])?; + let fn_name = &item.ident; + let (impl_generics, _, where_clause) = item.decl.generics.split_for_impl(); + + Ok(quote! { + unsafe impl #impl_generics ::#crate_path::__private::UnsafePinnedDrop for #type_ #where_clause { + unsafe fn pinned_drop(self: ::core::pin::Pin<&mut Self>) { + // Declare the #[pinned_drop] function *inside* our pinned_drop function + // This guarantees that it's impossible for any other user code + // to call it + #item + // #[pinned_drop] function 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. + #fn_name(self); + } + } + }) +} diff --git a/pin-project-internal/src/utils.rs b/pin-project-internal/src/utils.rs index fca284e1..fa35ab21 100644 --- a/pin-project-internal/src/utils.rs +++ b/pin-project-internal/src/utils.rs @@ -29,10 +29,29 @@ impl Parse for Nothing { } } -macro_rules! span { - ($expr:expr) => { - $expr.clone() - }; +/// 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")] +pub(crate) fn crate_path() -> Ident { + // 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 { crate::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"))] +pub(crate) fn crate_path() -> Ident { + Ident::new("pin_project", Span::call_site()) } macro_rules! error { diff --git a/src/lib.rs b/src/lib.rs index 55f02880..23c5c074 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,13 +2,13 @@ //! //! ## Examples //! -//! [`pin_projectable`] attribute creates a projection struct covering all the fields. +//! [`pin_project`] attribute creates a projection struct covering all the fields. //! //! ```rust -//! use pin_project::pin_projectable; +//! use pin_project::pin_project; //! use std::pin::Pin; //! -//! #[pin_projectable] +//! #[pin_project] //! struct Foo { //! #[pin] //! future: T, @@ -64,17 +64,17 @@ //! //! //! -//! [`pin_projectable`] also supports enums, but to use it ergonomically, you need +//! [`pin_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, pin_projectable}; +//! use pin_project::{project, pin_project}; //! # #[cfg(feature = "project_attr")] //! use std::pin::Pin; //! //! # #[cfg(feature = "project_attr")] -//! #[pin_projectable] +//! #[pin_project] //! enum Foo { //! Future(#[pin] T), //! Done(U), @@ -136,14 +136,15 @@ //! //! //! -//! See [`pin_projectable`] and [`project`] for more details. +//! See [`pin_project`] and [`project`] for more details. //! -//! [`pin_projectable`]: ./attr.pin_projectable.html +//! [`pin_project`]: ./attr.pin_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))))] +#![no_std] #![warn(unsafe_code)] #![warn(rust_2018_idioms, unreachable_pub)] #![warn(single_use_lifetimes)] @@ -159,10 +160,10 @@ /// ### `let` bindings /// /// ```rust -/// use pin_project::{pin_projectable, project}; +/// use pin_project::{pin_project, project}; /// # use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// struct Foo { /// #[pin] /// future: T, @@ -184,10 +185,10 @@ /// ### `match` expressions /// /// ```rust -/// use pin_project::{project, pin_projectable}; +/// use pin_project::{project, pin_project}; /// # use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// enum Foo { /// Tuple(#[pin] A, B), /// Struct { field: C }, @@ -219,10 +220,10 @@ /// different structures in the after second times will not generate wrong code. /// /// ```rust -/// use pin_project::{project, pin_projectable}; +/// use pin_project::{project, pin_project}; /// # use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// enum Foo { /// Tuple(#[pin] A, B), /// Struct { field: C }, @@ -300,14 +301,14 @@ pub use pin_project_internal::project; /// /// ## Examples /// -/// Using `#[pin_projectable]` will automatically create the appropriate +/// Using `#[pin_project]` will automatically create the appropriate /// conditional [`Unpin`] implementation: /// /// ```rust -/// use pin_project::pin_projectable; +/// use pin_project::pin_project; /// use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// struct Foo { /// #[pin] /// future: T, @@ -325,13 +326,13 @@ pub use pin_project_internal::project; /// /// If you want to implement [`Unpin`] manually, /// you must use the `unsafe_Unpin` argument to -/// `#[pin_projectable]`. +/// `#[pin_project]`. /// /// ```rust -/// use pin_project::{pin_projectable, UnsafeUnpin}; +/// use pin_project::{pin_project, UnsafeUnpin}; /// use std::pin::Pin; /// -/// #[pin_projectable(unsafe_Unpin)] +/// #[pin_project(unsafe_Unpin)] /// struct Foo { /// #[pin] /// future: T, @@ -365,9 +366,9 @@ pub use pin_project_internal::project; /// ### Structs (structs with named fields): /// /// ```rust -/// # use pin_project::pin_projectable; +/// # use pin_project::pin_project; /// # use std::pin::Pin; -/// #[pin_projectable] +/// #[pin_project] /// struct Foo { /// #[pin] /// future: T, @@ -386,9 +387,9 @@ pub use pin_project_internal::project; /// ### Tuple structs (structs with unnamed fields): /// /// ```rust -/// # use pin_project::pin_projectable; +/// # use pin_project::pin_project; /// # use std::pin::Pin; -/// #[pin_projectable] +/// #[pin_project] /// struct Foo(#[pin] T, U); /// /// impl Foo { @@ -405,17 +406,17 @@ pub use pin_project_internal::project; /// /// ### Enums /// -/// `pin_projectable` also supports enums, but to use it ergonomically, you need +/// `pin_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, pin_projectable}; +/// use pin_project::{project, pin_project}; /// # #[cfg(feature = "project_attr")] /// # use std::pin::Pin; /// /// # #[cfg(feature = "project_attr")] -/// #[pin_projectable] +/// #[pin_project] /// enum Foo { /// Tuple(#[pin] A, B), /// Struct { field: C }, @@ -443,13 +444,15 @@ pub use pin_project_internal::project; /// /// Enums without variants (zero-variant enums) are not supported. /// +/// See also [`project`] attribute. +/// /// ### `#[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 +/// To ensure that this requirement is upheld, the `pin_project` 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 @@ -460,22 +463,20 @@ pub use pin_project_internal::project; /// /// ```rust /// use std::fmt::Debug; -/// use pin_project::pin_project; +/// use pin_project::{pin_project, pinned_drop}; /// use std::pin::Pin; /// -/// pin_project! { -/// #[pin_projectable] -/// pub struct Foo { -/// #[pin] pinned_field: T, -/// unpin_field: U -/// } +/// #[pin_project(PinnedDrop)] +/// 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); -/// } +/// #[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() { @@ -483,41 +484,37 @@ pub use pin_project_internal::project; /// } /// ``` /// -/// -/// -/// Also see [`project`] attribute. +/// See also [`pinned_drop`] attribute. /// /// [`Unpin`]: core::marker::Unpin /// [`drop`]: Drop::drop /// [`project`]: ./attr.project.html +/// [`pinned_drop`]: ./attr.pinned_drop.html #[doc(inline)] -pub use pin_project_internal::pin_projectable; +pub use pin_project_internal::pin_project; -/// A helper macro for working with `pin_projectable`. +/// A helper macro for working with `pin_project`. /// /// This macro is only needed when you wish to provide a `Drop` -/// impl for your type. You may include a single `#[pin_projectable]` +/// impl for your type. You may include a single `#[pin_project]` /// type, and (optionally) one `#[pinned_drop]` function. Writing -/// anything else within the `pin_projectable` block is an error. +/// anything else within the `pin_project` block is an error. /// /// Example: /// /// ```rust /// /// use std::pin::Pin; -/// use pin_project::pin_project; -/// -/// pin_project! { +/// use pin_project::{pin_project, pinned_drop}; /// -/// #[pin_projectable] -/// struct Foo { -/// #[pin] field: u8 -/// } +/// #[pin_project(PinnedDrop)] +/// struct Foo { +/// #[pin] field: u8 +/// } /// -/// #[pinned_drop] -/// fn my_drop(foo: Pin<&mut Foo>) { -/// println!("Dropping: {}", foo.field); -/// } +/// #[pinned_drop] +/// fn my_drop(foo: Pin<&mut Foo>) { +/// println!("Dropping: {}", foo.field); /// } /// /// fn main() { @@ -525,11 +522,11 @@ pub use pin_project_internal::pin_projectable; /// } ///``` #[doc(inline)] -pub use pin_project_internal::pin_project; +pub use pin_project_internal::pinned_drop; /// A trait used for custom implementations of [`Unpin`]. /// This trait is used in conjunction with the `unsafe_Unpin` -/// argument to [`pin_projectable`] +/// argument to [`pin_project`] /// /// The Rust [`Unpin`] trait is safe to implement - by itself, /// implementing it cannot lead to undefined behavior. Undefined @@ -541,7 +538,7 @@ pub use pin_project_internal::pin_project; /// 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 +/// for your `#[pin_project]` 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`. @@ -554,7 +551,7 @@ pub use pin_project_internal::pin_project; /// 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* +/// Note that if you specify `#[pin_project(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 /// @@ -567,9 +564,9 @@ pub use pin_project_internal::pin_project; /// fields be `Unpin`, imposes an additional requirement: /// /// ```rust -/// use pin_project::{pin_projectable, UnsafeUnpin}; +/// use pin_project::{pin_project, UnsafeUnpin}; /// -/// #[pin_projectable(unsafe_Unpin)] +/// #[pin_project(unsafe_Unpin)] /// struct Foo { /// #[pin] /// field_1: K, @@ -579,57 +576,71 @@ pub use pin_project_internal::pin_project; /// unsafe impl UnsafeUnpin for Foo where K: Unpin + Clone {} /// ``` /// -/// [`pin_projectable`]: ./attr.pin_projectable.html +/// [`pin_project`]: ./attr.pin_project.html #[allow(unsafe_code)] pub unsafe trait UnsafeUnpin {} -// This is an internal helper struct used by `pin-project-internal`. -// This allows us to force an error if the user tries to provide -// a regular `Unpin` impl when they specify the `unsafe_Unpin` argument -// This is why we need Wrapper: -// -// Supposed we have the following code: -// -// #[pin_projectable(unsafe_Unpin)] -// struct MyStruct { -// #[pin] field: T -// } -// -// impl Unpin for MyStruct where MyStruct: UnsafeUnpinned {} // generated by pin-project-internal -// impl Unpin for MyStruct where T: Copy // written by the user -// -// We want this code to be rejected - the user is completely bypassing unsafe_Unpin, -// and providing an unsound Unpin impl in safe code! -// -// Unfortunately, the Rust compiler will accept the above code. -// Because MyStruct is declared in the same crate as the user-provided impl, -// the compiler will notice that 'MyStruct: UnsafeUnpinned' never holds. -// -// The solution is to introduce the 'Wrapper' struct, which is defined -// in the 'pin-project' crate. -// -// We now have code that looks like this: -// -// impl Unpin for MyStruct where Wrapper>: UnsafeUnpinned {} // generated by pin-project-internal -// impl Unpin for MyStruct where T: Copy // written by the user -// -// We also have 'unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {}' in the -// 'pin-project' crate. -// -// Now, our generated impl has a bound involving a type defined in another crate - Wrapper. -// This will cause rust to conservatively assume that 'Wrapper>: UnsafeUnpinned' -// holds, in the interest of preserving forwards compatibility (in case such an impl is added -// for Wrapper in a new version of the crate). -// -// This will cause rust to reject any other Unpin impls for MyStruct, since it will -// assume that our generated impl could potentially apply in any situation. -// -// This acheives the desired effect - when the user writes `#[pin_projectable(unsafe_Unpin)]`, -// the user must either provide no impl of `UnsafeUnpinned` (which is equivalent -// to making the type never implement Unpin), or provide an impl of `UnsafeUnpin`. -// It is impossible for them to provide an impl of `Unpin` #[doc(hidden)] -pub struct Wrapper(T); +pub mod __private { + use super::UnsafeUnpin; + use core::pin::Pin; -#[allow(unsafe_code)] -unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {} + // do not call or define it directly. + #[allow(unsafe_code)] + #[doc(hidden)] + pub unsafe trait UnsafePinnedDrop { + #[doc(hidden)] + unsafe fn pinned_drop(self: Pin<&mut Self>); + } + + // 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_project(unsafe_Unpin)] + // struct MyStruct { + // #[pin] field: T + // } + // + // impl Unpin for MyStruct where MyStruct: UnsafeUnpinned {} // generated by pin-project-internal + // impl Unpin for MyStruct where T: Copy // written by the user + // + // We want this code to be rejected - the user is completely bypassing unsafe_Unpin, + // and providing an unsound Unpin impl in safe code! + // + // Unfortunately, the Rust compiler will accept the above code. + // Because MyStruct is declared in the same crate as the user-provided impl, + // the compiler will notice that 'MyStruct: UnsafeUnpinned' never holds. + // + // The solution is to introduce the 'Wrapper' struct, which is defined + // in the 'pin-project' crate. + // + // We now have code that looks like this: + // + // impl Unpin for MyStruct where Wrapper>: UnsafeUnpinned {} // generated by pin-project-internal + // impl Unpin for MyStruct where T: Copy // written by the user + // + // We also have 'unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {}' in the + // 'pin-project' crate. + // + // Now, our generated impl has a bound involving a type defined in another crate - Wrapper. + // This will cause rust to conservatively assume that 'Wrapper>: UnsafeUnpinned' + // holds, in the interest of preserving forwards compatibility (in case such an impl is added + // for Wrapper in a new version of the crate). + // + // This will cause rust to reject any other Unpin impls for MyStruct, since it will + // assume that our generated impl could potentially apply in any situation. + // + // This acheives the desired effect - when the user writes `#[pin_project(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` + #[doc(hidden)] + pub struct Wrapper(T); + + #[allow(unsafe_code)] + unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {} +} diff --git a/tests/pin_projectable.rs b/tests/pin_project.rs similarity index 80% rename from tests/pin_projectable.rs rename to tests/pin_project.rs index 5e097ab0..4ca66646 100644 --- a/tests/pin_projectable.rs +++ b/tests/pin_project.rs @@ -5,13 +5,13 @@ #![allow(dead_code)] use core::pin::Pin; -use pin_project_internal::{pin_project, pin_projectable}; +use pin_project::pin_project; #[test] -fn test_pin_projectable() { +fn test_pin_project() { // struct - #[pin_projectable] + #[pin_project] struct Foo { #[pin] field1: T, @@ -30,7 +30,7 @@ fn test_pin_projectable() { // tuple struct - #[pin_projectable] + #[pin_project] struct Bar(#[pin] T, U); let mut bar = Bar(1, 2); @@ -45,7 +45,7 @@ fn test_pin_projectable() { // enum - #[pin_projectable] + #[pin_project] enum Baz { Variant1(#[pin] A, B), Variant2 { @@ -107,7 +107,7 @@ fn test_pin_projectable() { fn where_clause_and_associated_type_fields() { // struct - #[pin_projectable] + #[pin_project] struct Foo where I: Iterator, @@ -119,7 +119,7 @@ fn where_clause_and_associated_type_fields() { // enum - #[pin_projectable] + #[pin_project] enum Baz where I: Iterator, @@ -133,39 +133,19 @@ fn where_clause_and_associated_type_fields() { fn trait_bounds_on_type_generics() { // struct - #[pin_projectable] + #[pin_project] pub struct Foo<'a, T: ?Sized> { field: &'a mut T, } // tuple struct - #[pin_projectable] + #[pin_project] pub struct Bar<'a, T: ?Sized>(&'a mut T); // enum - #[pin_projectable] + #[pin_project] 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/tests/pinned_drop.rs b/tests/pinned_drop.rs new file mode 100644 index 00000000..6c870abd --- /dev/null +++ b/tests/pinned_drop.rs @@ -0,0 +1,27 @@ +#![recursion_limit = "128"] +#![no_std] +#![warn(unsafe_code)] +#![warn(rust_2018_idioms)] +#![allow(dead_code)] + +use core::pin::Pin; +use pin_project::{pin_project, pinned_drop}; + +#[pin_project(PinnedDrop)] +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/tests/project.rs b/tests/project.rs index adc0caca..e8ccc989 100644 --- a/tests/project.rs +++ b/tests/project.rs @@ -6,14 +6,14 @@ #![cfg(feature = "project_attr")] use core::pin::Pin; -use pin_project_internal::{pin_projectable, project}; +use pin_project::{pin_project, project}; #[project] // Nightly does not need a dummy attribute to the function. #[test] fn test_project_attr() { // struct - #[pin_projectable] + #[pin_project] struct Foo { #[pin] field1: T, @@ -33,7 +33,7 @@ fn test_project_attr() { // tuple struct - #[pin_projectable] + #[pin_project] struct Bar(#[pin] T, U); let mut bar = Bar(1, 2); @@ -49,7 +49,7 @@ fn test_project_attr() { // enum - #[pin_projectable] + #[pin_project] enum Baz { Variant1(#[pin] A, B), Variant2 { diff --git a/tests/ui/pin_project/drop_conflict.rs b/tests/ui/pin_project/drop_conflict.rs new file mode 100644 index 00000000..5b4fda51 --- /dev/null +++ b/tests/ui/pin_project/drop_conflict.rs @@ -0,0 +1,19 @@ +// compile-fail + +#![deny(warnings, unsafe_code)] + +use pin_project::pin_project; +use std::pin::Pin; + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Drop for Foo { + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/pin_project/drop_conflict.stderr b/tests/ui/pin_project/drop_conflict.stderr new file mode 100644 index 00000000..3f5fbc09 --- /dev/null +++ b/tests/ui/pin_project/drop_conflict.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `Foo<_, _>`: + --> $DIR/drop_conflict.rs:8:1 + | +8 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +15 | impl Drop for Foo { + | ----------------------------- first implementation here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/pin_project/forget-unsafe-unpin.rs b/tests/ui/pin_project/forget-unsafe-unpin.rs new file mode 100644 index 00000000..3d9c62ac --- /dev/null +++ b/tests/ui/pin_project/forget-unsafe-unpin.rs @@ -0,0 +1,20 @@ +// compile-pass + +#![deny(warnings, unsafe_code)] + +use pin_project::{pin_project, UnsafeUnpin}; + +#[test] +fn unsafe_unpin() { + #[pin_project(unsafe_Unpin)] + pub struct Blah { + field_1: u8, + #[pin] + field_2: Option, + } + + #[allow(unsafe_code)] + unsafe impl UnsafeUnpin for Blah {} +} + +fn main() {} diff --git a/tests/ui/pin_project/forget-unsafe-unpin.stderr b/tests/ui/pin_project/forget-unsafe-unpin.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/ui/pin_projectable/invalid.rs b/tests/ui/pin_project/invalid.rs similarity index 71% rename from tests/ui/pin_projectable/invalid.rs rename to tests/ui/pin_project/invalid.rs index cb5ff845..ac7c0a56 100644 --- a/tests/ui/pin_projectable/invalid.rs +++ b/tests/ui/pin_project/invalid.rs @@ -1,24 +1,24 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_projectable; +use pin_project::pin_project; -#[pin_projectable] +#[pin_project] struct A { #[pin()] //~ ERROR unexpected token future: T, } -#[pin_projectable] +#[pin_project] struct B(#[pin(foo)] T); //~ ERROR unexpected token -#[pin_projectable] +#[pin_project] enum C { A(#[pin(foo)] T), //~ ERROR unexpected token } -#[pin_projectable] +#[pin_project] enum D { A { #[pin(foo)] //~ ERROR unexpected token diff --git a/tests/ui/pin_projectable/invalid.stderr b/tests/ui/pin_project/invalid.stderr similarity index 100% rename from tests/ui/pin_projectable/invalid.stderr rename to tests/ui/pin_project/invalid.stderr diff --git a/tests/ui/pin_projectable/packed.rs b/tests/ui/pin_project/packed.rs similarity index 71% rename from tests/ui/pin_projectable/packed.rs rename to tests/ui/pin_project/packed.rs index 13c98458..b0f3c571 100644 --- a/tests/ui/pin_projectable/packed.rs +++ b/tests/ui/pin_project/packed.rs @@ -1,32 +1,32 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_projectable; +use pin_project::pin_project; -#[pin_projectable] +#[pin_project] #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type struct Foo { #[pin] field: u8, } -// Test putting 'repr' before the 'pin_projectable' attribute +// Test putting 'repr' before the 'pin_project' attribute #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type -#[pin_projectable] +#[pin_project] struct Foo2 { #[pin] field: u8, } -#[pin_projectable] +#[pin_project] #[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] +#[pin_project] enum Blah2 { Tuple(#[pin] u8), } diff --git a/tests/ui/pin_projectable/packed.stderr b/tests/ui/pin_project/packed.stderr similarity index 67% rename from tests/ui/pin_projectable/packed.stderr rename to tests/ui/pin_project/packed.stderr index d41dba8b..cda8979f 100644 --- a/tests/ui/pin_projectable/packed.stderr +++ b/tests/ui/pin_project/packed.stderr @@ -1,22 +1,22 @@ -error: pin_projectable may not be used on #[repr(packed)] types +error: pin_project may not be used on #[repr(packed)] types --> $DIR/packed.rs:8:8 | 8 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ^^^^^^ -error: pin_projectable may not be used on #[repr(packed)] types +error: pin_project may not be used on #[repr(packed)] types --> $DIR/packed.rs:15:8 | 15 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ^^^^^^ -error: pin_projectable may not be used on #[repr(packed)] types +error: pin_project may not be used on #[repr(packed)] types --> $DIR/packed.rs:23:8 | 23 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ^^^^^^ -error: pin_projectable may not be used on #[repr(packed)] types +error: pin_project may not be used on #[repr(packed)] types --> $DIR/packed.rs:28:8 | 28 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type diff --git a/tests/ui/pin_project/unpin_conflict.rs b/tests/ui/pin_project/unpin_conflict.rs new file mode 100644 index 00000000..4f21f35a --- /dev/null +++ b/tests/ui/pin_project/unpin_conflict.rs @@ -0,0 +1,42 @@ +// compile-fail + +#![deny(warnings, unsafe_code)] + +use pin_project::pin_project; +use std::pin::Pin; + +// The same implementation. + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl + +// The implementation that under different conditions. + +#[pin_project] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Bar {} // Non-conditional Unpin impl + +#[pin_project] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Baz {} // Conditional Unpin impl + +fn main() {} diff --git a/tests/ui/pin_projectable/unpin_conflict.stderr b/tests/ui/pin_project/unpin_conflict.stderr similarity index 54% rename from tests/ui/pin_projectable/unpin_conflict.stderr rename to tests/ui/pin_project/unpin_conflict.stderr index 7baa4bef..8a6df905 100644 --- a/tests/ui/pin_projectable/unpin_conflict.stderr +++ b/tests/ui/pin_project/unpin_conflict.stderr @@ -1,28 +1,28 @@ 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<_, _>` +10 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` ... -26 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl +18 | 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 + --> $DIR/unpin_conflict.rs:22:1 | -30 | #[pin_projectable] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +22 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` ... -46 | impl Unpin for Bar {} // Non-conditional Unpin impl +30 | 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 + --> $DIR/unpin_conflict.rs:32:1 | -48 | #[pin_projectable] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +32 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` ... -64 | impl Unpin for Baz {} // Conditional Unpin impl +40 | impl Unpin for Baz {} // Conditional Unpin impl | -------------------------------------------- first implementation here error: aborting due to 3 previous errors diff --git a/tests/ui/pin_projectable/unsupported.rs b/tests/ui/pin_project/unsupported.rs similarity index 75% rename from tests/ui/pin_projectable/unsupported.rs rename to tests/ui/pin_project/unsupported.rs index 777f4fc0..b5b4a17e 100644 --- a/tests/ui/pin_projectable/unsupported.rs +++ b/tests/ui/pin_project/unsupported.rs @@ -1,27 +1,27 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_projectable; +use pin_project::pin_project; -#[pin_projectable] +#[pin_project] struct Struct1 {} //~ ERROR cannot be implemented for structs with zero fields -#[pin_projectable] +#[pin_project] struct Struct2(); //~ ERROR cannot be implemented for structs with zero fields -#[pin_projectable] +#[pin_project] struct Struct3; //~ ERROR cannot be implemented for structs with units -#[pin_projectable] +#[pin_project] enum Enum1 {} //~ ERROR cannot be implemented for enums without variants -#[pin_projectable] +#[pin_project] enum Enum2 { A = 2, //~ ERROR cannot be implemented for enums with discriminants } -#[pin_projectable] +#[pin_project] enum Enum1 { A, //~ ERROR cannot be implemented for enums that have no field B, diff --git a/tests/ui/pin_projectable/unsupported.stderr b/tests/ui/pin_project/unsupported.stderr similarity index 100% rename from tests/ui/pin_projectable/unsupported.stderr rename to tests/ui/pin_project/unsupported.stderr diff --git a/tests/ui/pin_projectable/unpin_conflict.rs b/tests/ui/pin_projectable/unpin_conflict.rs deleted file mode 100644 index d7923483..00000000 --- a/tests/ui/pin_projectable/unpin_conflict.rs +++ /dev/null @@ -1,66 +0,0 @@ -// compile-fail - -#![deny(warnings)] - -use pin_project::pin_projectable; -use std::pin::Pin; - -// The same implementation. - -#[pin_projectable] //~ ERROR E0119 -struct Foo { - #[pin] - future: T, - field: U, -} - -impl Foo { - fn baz(mut 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 - } -} - -// conflicting implementations -impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl - -// The implementation that under different conditions. - -#[pin_projectable] //~ ERROR E0119 -struct Bar { - #[pin] - future: T, - field: U, -} - -impl Bar { - fn baz(mut 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 - } -} - -// conflicting implementations -impl Unpin for Bar {} // Non-conditional Unpin impl - -#[pin_projectable] //~ ERROR E0119 -struct Baz { - #[pin] - future: T, - field: U, -} - -impl Baz { - fn baz(mut 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 - } -} - -// conflicting implementations -impl Unpin for Baz {} // Conditional Unpin impl - -fn main() {} diff --git a/tests/ui/pinned_drop/forget-pinned-drop.rs b/tests/ui/pinned_drop/forget-pinned-drop.rs new file mode 100644 index 00000000..d6a86cb7 --- /dev/null +++ b/tests/ui/pinned_drop/forget-pinned-drop.rs @@ -0,0 +1,15 @@ +// compile-fail + +#![deny(warnings, unsafe_code)] + +use core::pin::Pin; +use pin_project::{pin_project, pinned_drop}; + +#[pin_project(PinnedDrop)] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} + +fn main() {} diff --git a/tests/ui/pinned_drop/forget-pinned-drop.stderr b/tests/ui/pinned_drop/forget-pinned-drop.stderr new file mode 100644 index 00000000..8c894a46 --- /dev/null +++ b/tests/ui/pinned_drop/forget-pinned-drop.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `Foo<'a>: pin_project::__private::UnsafePinnedDrop` is not satisfied + --> $DIR/forget-pinned-drop.rs:8:15 + | +8 | #[pin_project(PinnedDrop)] + | ^^^^^^^^^^ the trait `pin_project::__private::UnsafePinnedDrop` is not implemented for `Foo<'a>` + | + = note: required by `pin_project::__private::UnsafePinnedDrop::pinned_drop` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pinned_drop/invalid.rs b/tests/ui/pinned_drop/invalid.rs index 41c6aa1f..d7ec7a92 100644 --- a/tests/ui/pinned_drop/invalid.rs +++ b/tests/ui/pinned_drop/invalid.rs @@ -1,19 +1,28 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_project; +use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; -pin_project! { - #[pin_projectable] - pub struct Foo<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } +#[pin_project] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} + +#[pinned_drop(foo)] //~ ERROR unexpected token +fn do_drop(_foo: Pin<&mut Foo<'_>>) {} - #[pinned_drop(foo)] //~ ERROR unexpected token - fn do_drop(_foo: Pin<&mut Foo<'_>>) {} +#[pin_project] +pub struct Bar<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, } +#[pinned_drop] +fn do_drop(_bar: &mut Bar<'_>) {} //~ ERROR #[pinned_drop] function must take a argument `Pin<&mut Type>` + fn main() {} diff --git a/tests/ui/pinned_drop/invalid.stderr b/tests/ui/pinned_drop/invalid.stderr index 2fef4391..212c044d 100644 --- a/tests/ui/pinned_drop/invalid.stderr +++ b/tests/ui/pinned_drop/invalid.stderr @@ -1,8 +1,14 @@ error: unexpected token - --> $DIR/invalid.rs:15:18 + --> $DIR/invalid.rs:15:15 | -15 | #[pinned_drop(foo)] //~ ERROR unexpected token - | ^^^^^ +15 | #[pinned_drop(foo)] //~ ERROR unexpected token + | ^^^ -error: aborting due to previous error +error: #[pinned_drop] function must take a argument `Pin<&mut Type>` + --> $DIR/invalid.rs:26:12 + | +26 | fn do_drop(_bar: &mut Bar<'_>) {} //~ ERROR #[pinned_drop] function must take a argument `Pin<&mut Type>` + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/tests/ui/pinned_drop/return-type.rs b/tests/ui/pinned_drop/return-type.rs index 5d259d66..0bb41acc 100644 --- a/tests/ui/pinned_drop/return-type.rs +++ b/tests/ui/pinned_drop/return-type.rs @@ -1,35 +1,35 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_project; +use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; -pin_project! { - #[pin_projectable] - pub struct Foo<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } +#[pin_project] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} - #[pinned_drop] - fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { //~ ERROR #[pinned_drop] function must return the unit type - **foo.project().was_dropped = true; - true - } +#[pinned_drop] +fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { + //~ ERROR #[pinned_drop] function must return the unit type + **foo.project().was_dropped = true; + true } -pin_project! { - #[pin_projectable] - pub struct Bar<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } +#[pin_project] +pub struct Bar<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} - #[pinned_drop] - fn do_drop(foo: Pin<&mut Bar<'_>>) -> () { // OK - **foo.project().was_dropped = true; - } +// OK +#[pinned_drop] +fn do_drop(foo: Pin<&mut Bar<'_>>) -> () { + **foo.project().was_dropped = true; } fn main() {} diff --git a/tests/ui/pinned_drop/return-type.stderr b/tests/ui/pinned_drop/return-type.stderr index b7d3c45a..8a44eb5b 100644 --- a/tests/ui/pinned_drop/return-type.stderr +++ b/tests/ui/pinned_drop/return-type.stderr @@ -1,8 +1,8 @@ error: #[pinned_drop] function must return the unit type - --> $DIR/return-type.rs:16:43 + --> $DIR/return-type.rs:16:39 | -16 | fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { //~ ERROR #[pinned_drop] function must return the unit type - | ^^^^ +16 | fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { + | ^^^^ error: aborting due to previous error diff --git a/tests/ui/project/invalid.rs b/tests/ui/project/invalid.rs index def05443..ea20a55d 100644 --- a/tests/ui/project/invalid.rs +++ b/tests/ui/project/invalid.rs @@ -1,10 +1,10 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::{pin_projectable, project}; +use pin_project::{pin_project, project}; -#[pin_projectable] +#[pin_project] struct A { #[pin] future: T, diff --git a/tests/unsafe_unpin.rs b/tests/unsafe_unpin.rs index 5b1c3192..142e1c52 100644 --- a/tests/unsafe_unpin.rs +++ b/tests/unsafe_unpin.rs @@ -4,14 +4,17 @@ #![warn(rust_2018_idioms)] #![allow(dead_code)] -use pin_project::pin_projectable; +use pin_project::{pin_project, UnsafeUnpin}; #[test] fn unsafe_unpin() { - #[pin_projectable(unsafe_Unpin)] + #[pin_project(unsafe_Unpin)] pub struct Blah { field_1: u8, #[pin] field_2: Option, } + + #[allow(unsafe_code)] + unsafe impl UnsafeUnpin for Blah {} }