diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed45927a..9e763bf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,8 +27,8 @@ jobs: # This is the minimum supported Rust version of this crate. # When updating this, the reminder to update the minimum supported # Rust version in README.md. - - build: 1.33.0 - rust: 1.33.0 + - build: 1.34.0 + rust: 1.34.0 - build: 1.36.0 rust: 1.36.0 - build: 1.37.0 diff --git a/README.md b/README.md index a8bc573d..840d1faf 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ [docs-url]: https://docs.rs/pin-project [license-badge]: https://img.shields.io/crates/l/pin-project.svg [license]: #license -[rustc-badge]: https://img.shields.io/badge/rustc-1.33+-lightgray.svg -[rustc-url]: https://blog.rust-lang.org/2019/02/28/Rust-1.33.0.html +[rustc-badge]: https://img.shields.io/badge/rustc-1.34+-lightgray.svg +[rustc-url]: https://blog.rust-lang.org/2019/04/11/Rust-1.34.0.html A crate for safe and ergonomic pin-projection. @@ -29,7 +29,7 @@ Add this to your `Cargo.toml`: pin-project = "0.4" ``` -The current pin-project requires Rust 1.33 or later. +The current pin-project requires Rust 1.34 or later. ## Examples diff --git a/ci.sh b/ci.sh index 900118c2..df0131ef 100644 --- a/ci.sh +++ b/ci.sh @@ -12,7 +12,7 @@ echo "Running 'cargo fmt -- --check'" cargo +nightly fmt --all -- --check echo "Running 'cargo clippy'" -cargo +nightly clippy --all --all-features +cargo +nightly clippy --all --all-features --all-targets echo "Running 'cargo test'" cargo +nightly test --all --all-features diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index 95bc25c4..1090a44e 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -28,15 +28,14 @@ use proc_macro::TokenStream; use crate::utils::{Immutable, Mutable, Owned}; -/// An attribute that creates a projection struct covering all the fields. +/// An attribute that creates a projection type covering all the fields of struct or enum. /// -/// This attribute creates a projection struct according to the following rules: +/// This attribute creates a projection type 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. +/// * 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. /// -/// The following methods are implemented on the original `#[pin_project]` type: +/// And the following methods are implemented on the original `#[pin_project]` type: /// /// ``` /// # #[rustversion::since(1.36)] @@ -51,27 +50,23 @@ use crate::utils::{Immutable, Mutable, Owned}; /// # } /// ``` /// -/// The visibility of the projected type and projection method is based on the -/// original type. However, if the visibility of the original type is `pub`, -/// the visibility of the projected type and the projection method is `pub(crate)`. +/// The visibility of the projected type and projection method is based on the original type. +/// However, if the visibility of the original type is `pub`, the visibility of the projected type +/// and the projection method is downgraded to `pub(crate)`. /// -/// If you want to call the `project` method multiple times or later use the -/// original Pin type, it needs to use [`.as_mut()`][`Pin::as_mut`] to avoid -/// consuming the `Pin`. -/// -/// ## Safety +/// # 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. +/// it is impossible to cause [undefined behavior][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/nightly/std/pin/index.html#projections-and-structural-pinning): +/// stated in [the Rust documentation][pin-projection]: /// -/// 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 +/// 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 /// `UnsafeUnpin` argument. /// /// 2. The destructor of the struct must not move structural fields out of its argument. @@ -85,25 +80,25 @@ use crate::utils::{Immutable, Mutable, Owned}; /// impl MyStructMustNotImplDrop for MyStruct {} /// ``` /// -/// If you attempt to provide an Drop impl, the blanket impl will +/// If you attempt to provide an [`Drop`] impl, the blanket impl will /// then apply to your type, causing a compile-time error due to /// the conflict with the second 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. +/// If you wish to provide a custom [`Drop`] impl, you can annotate a function +/// with [`#[pinned_drop]`][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, +/// 3. You must make sure that you uphold the [`Drop` guarantee][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 overwrite a field with something else. -/// Because your custom destructor takes `Pin<&mut MyStruct`, it's impossible to obtain +/// Because 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. @@ -111,11 +106,10 @@ use crate::utils::{Immutable, Mutable, Owned}; /// 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. -/// +/// Pin projections are also incompatible with [`#[repr(packed)]`][repr-packed] structs. Attempting to use this attribute +/// on a [`#[repr(packed)]`][repr-packed] struct results in a compile-time error. /// -/// ## Examples +/// # Examples /// /// Using `#[pin_project]` will automatically create the appropriate /// conditional [`Unpin`] implementation: @@ -125,48 +119,157 @@ use crate::utils::{Immutable, Mutable, Owned}; /// use std::pin::Pin; /// /// #[pin_project] -/// struct Foo { +/// struct Struct { /// #[pin] -/// future: T, -/// field: U, +/// pinned: T, +/// unpinned: U, /// } /// -/// impl Foo { -/// fn baz(self: Pin<&mut Self>) { +/// impl Struct { +/// fn method(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 +/// let _: Pin<&mut T> = this.pinned; // Pinned reference to the field +/// let _: &mut U = this.unpinned; // Normal reference to the field /// } /// } /// ``` /// -/// Note that borrowing the field where `#[pin]` attribute is used multiple -/// times requires using [`.as_mut()`][`Pin::as_mut`] to avoid -/// consuming the `Pin`. +/// If you want to call the `project()` method multiple times or later use the +/// original [`Pin`] type, it needs to use [`.as_mut()`][`Pin::as_mut`] to avoid +/// consuming the [`Pin`]. /// -/// If you want to implement [`Unpin`] manually, you must use the `UnsafeUnpin` -/// argument to `#[pin_project]`. +/// ## Supported Items +/// +/// `#[pin_project]` can be used on structs and enums. +/// +/// [Structs](https://doc.rust-lang.org/reference/items/structs.html): /// /// ```rust -/// use pin_project::{pin_project, UnsafeUnpin}; +/// use pin_project::pin_project; /// use std::pin::Pin; /// -/// #[pin_project(UnsafeUnpin)] -/// struct Foo { +/// #[pin_project] +/// struct Struct { /// #[pin] -/// future: T, -/// field: U, +/// pinned: T, +/// unpinned: U, /// } /// -/// impl Foo { -/// fn baz(self: Pin<&mut Self>) { +/// impl Struct { +/// fn method(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 +/// let _: Pin<&mut T> = this.pinned; +/// let _: &mut U = this.unpinned; /// } /// } +/// ``` +/// +/// [Tuple structs](https://doc.rust-lang.org/reference/items/structs.html): +/// +/// ```rust +/// use pin_project::pin_project; +/// use std::pin::Pin; +/// +/// #[pin_project] +/// struct TupleStruct(#[pin] T, U); /// -/// unsafe impl UnsafeUnpin for Foo {} // Conditional Unpin impl +/// impl TupleStruct { +/// fn method(self: Pin<&mut Self>) { +/// let this = self.project(); +/// let _: Pin<&mut T> = this.0; +/// let _: &mut U = this.1; +/// } +/// } +/// ``` +/// +/// [Enums](https://doc.rust-lang.org/reference/items/enumerations.html): +/// +/// `#[pin_project]` supports enums, but to use it, you need to use with the +/// [`project`] attribute. +/// +/// The attribute at the expression position is not stable, so you need to use +/// a dummy [`project`] attribute for the function. +/// +/// ```rust +/// use pin_project::{pin_project, project}; +/// use std::pin::Pin; +/// +/// #[pin_project] +/// enum Enum { +/// Tuple(#[pin] T), +/// Struct { field: U }, +/// Unit, +/// } +/// +/// impl Enum { +/// #[project] // Nightly does not need a dummy attribute to the function. +/// fn method(self: Pin<&mut Self>) { +/// #[project] +/// match self.project() { +/// Enum::Tuple(x) => { +/// let _: Pin<&mut T> = x; +/// } +/// Enum::Struct { field } => { +/// let _: &mut U = field; +/// } +/// Enum::Unit => {} +/// } +/// } +/// } +/// ``` +/// +/// See also [`project`] and [`project_ref`] attributes. +/// +/// ## `!Unpin` +/// +/// If you want to ensure that [`Unpin`] is not implemented, use the `!Unpin` +/// argument to `#[pin_project]`. +/// +/// ```rust +/// use pin_project::pin_project; +/// +/// #[pin_project(!Unpin)] +/// struct Struct { +/// #[pin] +/// pinned: T, +/// unpinned: U, +/// } +/// ``` +/// +/// You can also ensure `!Unpin` by using `#[pin]` attribute for [`PhantomPinned`] field. +/// +/// ```rust +/// use pin_project::pin_project; +/// use std::marker::PhantomPinned; +/// +/// #[pin_project] +/// struct Struct { +/// #[pin] +/// pinned: T, +/// unpinned: U, +/// #[pin] +/// _pin: PhantomPinned, +/// } +/// ``` +/// +/// Note that using [`PhantomPinned`] without `#[pin]` attribute has no effect. +/// +/// ## `UnsafeUnpin` +/// +/// If you want to implement [`Unpin`] manually, you must use the `UnsafeUnpin` +/// argument to `#[pin_project]`. +/// +/// ```rust +/// use pin_project::{pin_project, UnsafeUnpin}; +/// +/// #[pin_project(UnsafeUnpin)] +/// struct Struct { +/// #[pin] +/// pinned: T, +/// unpinned: U, +/// } +/// +/// unsafe impl UnsafeUnpin for Struct {} /// ``` /// /// Note the usage of the unsafe [`UnsafeUnpin`] trait, instead of the usual @@ -177,21 +280,21 @@ use crate::utils::{Immutable, Mutable, Owned}; /// /// See [`UnsafeUnpin`] trait for more details. /// -/// ### `#[pinned_drop]` +/// ## `#[pinned_drop]` /// -/// In order to correctly implement pin projections, a type's `Drop` impl must +/// In order to correctly implement pin projections, a type's [`Drop`] impl must /// not move out of any structurally pinned fields. Unfortunately, [`Drop::drop`] -/// takes `&mut Self`, not `Pin<&mut Self>`. +/// takes `&mut Self`, not [`Pin`]`<&mut Self>`. /// /// To ensure that this requirement is upheld, the `#[pin_project]` attribute will -/// provide a [`Drop`] impl for you. This `Drop` impl will delegate to an impl +/// provide a [`Drop`] impl for you. This [`Drop`] impl will delegate to an impl /// block annotated with `#[pinned_drop]` if you use the `PinnedDrop` argument /// to `#[pin_project]`. /// /// This impl block acts just like a normal [`Drop`] impl, /// except for the following two: /// -/// * `drop` method takes `Pin<&mut Self>` +/// * `drop` method takes [`Pin`]`<&mut Self>` /// * Name of the trait is `PinnedDrop`. /// /// ```rust @@ -214,14 +317,14 @@ use crate::utils::{Immutable, Mutable, Owned}; /// use std::{fmt::Debug, pin::Pin}; /// /// #[pin_project(PinnedDrop)] -/// pub struct Foo { +/// struct Struct { /// #[pin] /// pinned_field: T, /// unpin_field: U, /// } /// /// #[pinned_drop] -/// impl PinnedDrop for Foo { +/// impl PinnedDrop for Struct { /// fn drop(self: Pin<&mut Self>) { /// println!("Dropping pinned field: {:?}", self.pinned_field); /// println!("Dropping unpin field: {:?}", self.unpin_field); @@ -229,13 +332,13 @@ use crate::utils::{Immutable, Mutable, Owned}; /// } /// /// fn main() { -/// let _x = Foo { pinned_field: true, unpin_field: 40 }; +/// let _x = Struct { pinned_field: true, unpin_field: 40 }; /// } /// ``` /// /// See also [`pinned_drop`] attribute. /// -/// ### `project_replace()` +/// ## `project_replace()` /// /// In addition to the `project()` and `project_ref()` methods which are always /// provided when you use the `#[pin_project]` attribute, there is a third method, @@ -255,10 +358,10 @@ use crate::utils::{Immutable, Mutable, Owned}; /// ``` /// /// The `ProjectionOwned` type is identical to the `Self` type, except that -/// all pinned fields have been replaced by equivalent `PhantomData` types. +/// all pinned fields have been replaced by equivalent [`PhantomData`] types. /// -/// This method is opt-in, because it is only supported for `Sized` types, and -/// because it is incompatible with the `#[pinned_drop]` attribute described +/// This method is opt-in, because it is only supported for [`Sized`] types, and +/// because it is incompatible with the [`#[pinned_drop]`][pinned-drop] attribute described /// above. It can be enabled by using `#[pin_project(Replace)]`. /// /// For example: @@ -267,7 +370,7 @@ use crate::utils::{Immutable, Mutable, Owned}; /// use pin_project::{pin_project, project_replace}; /// /// #[pin_project(Replace)] -/// pub enum Foo { +/// enum Struct { /// A { /// #[pin] /// pinned_field: i32, @@ -278,12 +381,12 @@ use crate::utils::{Immutable, Mutable, Owned}; /// /// #[project_replace] /// fn main() { -/// let mut x = Box::pin(Foo::A { pinned_field: 42, unpinned_field: "hello" }); +/// let mut x = Box::pin(Struct::A { pinned_field: 42, unpinned_field: "hello" }); /// /// #[project_replace] -/// match x.as_mut().project_replace(Foo::B) { -/// Foo::A { unpinned_field, .. } => assert_eq!(unpinned_field, "hello"), -/// Foo::B => unreachable!(), +/// match x.as_mut().project_replace(Struct::B) { +/// Struct::A { unpinned_field, .. } => assert_eq!(unpinned_field, "hello"), +/// Struct::B => unreachable!(), /// } /// } /// ``` @@ -292,102 +395,22 @@ use crate::utils::{Immutable, Mutable, Owned}; /// type of `project_replace()`, and work in exactly the same way as the /// [`project`] and [`project_ref`] attributes. /// -/// ## Supported Items -/// -/// The current pin-project supports the following types of items. -/// -/// ### Structs (structs with named fields): -/// -/// ```rust -/// use pin_project::pin_project; -/// use std::pin::Pin; -/// -/// #[pin_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; -/// let _: &mut U = this.field; -/// } -/// } -/// ``` -/// -/// ### Tuple structs (structs with unnamed fields): -/// -/// ```rust -/// use pin_project::pin_project; -/// use std::pin::Pin; -/// -/// #[pin_project] -/// 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_project` also supports enums, but to use it, you need to use with the -/// [`project`] attribute. -/// -/// The attribute at the expression position is not stable, so you need to use -/// a dummy `#[project]` attribute for the function. -/// -/// ```rust -/// use pin_project::{pin_project, project}; -/// use std::pin::Pin; -/// -/// #[pin_project] -/// 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 => {} -/// } -/// } -/// } -/// ``` -/// -/// Enums without variants (zero-variant enums) are not supported. -/// -/// See also [`project`] and [`project_ref`] attributes. -/// +/// [`PhantomData`]: core::marker::PhantomData +/// [`PhantomPinned`]: core::marker::PhantomPinned /// [`Pin::as_mut`]: core::pin::Pin::as_mut /// [`Pin::set`]: core::pin::Pin::set -/// [`drop`]: Drop::drop +/// [`Pin`]: core::pin::Pin /// [`UnsafeUnpin`]: https://docs.rs/pin-project/0.4/pin_project/trait.UnsafeUnpin.html -/// [`project`]: ./attr.project.html +/// [`pinned_drop`]: ./attr.pinned_drop.html /// [`project_ref`]: ./attr.project_ref.html /// [`project_replace`]: ./attr.project_replace.html -/// [`pinned_drop`]: ./attr.pinned_drop.html +/// [`project`]: ./attr.project.html +/// [drop-guarantee]: https://doc.rust-lang.org/nightly/std/pin/index.html#drop-guarantee +/// [pinned-drop]: ./attr.pin_project.html#pinned_drop +/// [repr-packed]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked +/// [pin-projection]: https://doc.rust-lang.org/nightly/std/pin/index.html#projections-and-structural-pinning +/// [undefined-behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html +/// [unsafe-unpin]: ./attr.pin_project.html#pinned_drop #[proc_macro_attribute] pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { pin_project::attribute(&args.into(), input.into()).into() @@ -401,7 +424,7 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// This impl block acts just like a normal [`Drop`] impl, /// except for the following two: /// -/// * `drop` method takes `Pin<&mut Self>` +/// * `drop` method takes [`Pin`]`<&mut Self>` /// * Name of the trait is `PinnedDrop`. /// /// ```rust @@ -441,9 +464,7 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// ``` /// -/// See also ["pinned-drop" section of `pin_project` attribute][pinned-drop]. -/// -/// [pinned-drop]: ./attr.pin_project.html#pinned_drop +/// See also ["pinned-drop" section of `#[pin_project]` attribute][pinned-drop]. /// /// ## Why `#[pinned_drop]` attribute is needed? /// @@ -466,6 +487,9 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// This allows implementing [`Drop`] safely using `#[pinned_drop]`. /// Also by using the [`drop`] function just like dropping a type that directly implements [`Drop`], /// can drop safely a type that implements `PinnedDrop`. +/// +/// [`Pin`]: core::pin::Pin +/// [pinned-drop]: ./attr.pin_project.html#pinned_drop #[proc_macro_attribute] pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input); @@ -477,47 +501,6 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// /// The following syntaxes are supported. /// -/// ## `impl` blocks -/// -/// All methods (and associated functions) in `#[project] impl` block become -/// methods of the projected type. If you want to implement methods on the -/// original type, you need to create another (non-`#[project]`) `impl` block. -/// -/// To call a method implemented in `#[project] impl` block, you need to first -/// get the projected-type with `let this = self.project();`. -/// -/// ### Examples -/// -/// ```rust -/// use pin_project::{pin_project, project}; -/// use std::pin::Pin; -/// -/// #[pin_project] -/// struct Foo { -/// #[pin] -/// future: T, -/// field: U, -/// } -/// -/// // impl for the original type -/// impl Foo { -/// fn bar(self: Pin<&mut Self>) { -/// self.project().baz() -/// } -/// } -/// -/// // impl for the projected type -/// #[project] -/// impl Foo { -/// fn baz(self) { -/// let Self { future, field } = self; -/// -/// let _: Pin<&mut T> = future; -/// let _: &mut U = field; -/// } -/// } -/// ``` -/// /// ## `let` bindings /// /// *The attribute at the expression position is not stable, so you need to use @@ -584,6 +567,47 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// ``` /// +/// ## `impl` blocks +/// +/// All methods and associated functions in `#[project] impl` block become +/// methods of the projected type. If you want to implement methods on the +/// original type, you need to create another (non-`#[project]`) `impl` block. +/// +/// To call a method implemented in `#[project] impl` block, you need to first +/// get the projected-type with `let this = self.project();`. +/// +/// ### Examples +/// +/// ```rust +/// use pin_project::{pin_project, project}; +/// use std::pin::Pin; +/// +/// #[pin_project] +/// struct Foo { +/// #[pin] +/// future: T, +/// field: U, +/// } +/// +/// // impl for the original type +/// impl Foo { +/// fn bar(self: Pin<&mut Self>) { +/// self.project().baz() +/// } +/// } +/// +/// // impl for the projected type +/// #[project] +/// impl Foo { +/// fn baz(self) { +/// let Self { future, field } = self; +/// +/// let _: Pin<&mut T> = future; +/// let _: &mut U = field; +/// } +/// } +/// ``` +/// /// ## `use` statements /// /// ### Examples diff --git a/pin-project-internal/src/pin_project/derive.rs b/pin-project-internal/src/pin_project/derive.rs index 2f9d5422..f8040227 100644 --- a/pin-project-internal/src/pin_project/derive.rs +++ b/pin-project-internal/src/pin_project/derive.rs @@ -103,14 +103,13 @@ fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { } } -#[derive(Default)] struct Args { - /// `PinnedDrop`. + /// `PinnedDrop` argument. pinned_drop: Option, - /// `UnsafeUnpin`. - unsafe_unpin: Option, - /// `Replace`. + /// `Replace` argument. replace: Option, + /// `UnsafeUnpin` or `!Unpin` argument. + unpin_impl: UnpinImpl, } const DUPLICATE_PIN: &str = "duplicate #[pin] attribute"; @@ -151,6 +150,10 @@ impl Args { impl Parse for Args { fn parse(input: ParseStream<'_>) -> Result { + mod kw { + syn::custom_keyword!(Unpin); + } + // `(__private())` -> `` fn parse_input(input: ParseStream<'_>) -> Result> { if let Ok(content) = input.parenthesized() { @@ -171,7 +174,7 @@ impl Parse for Args { } // Replace `prev` with `new`. Returns `Err` if `prev` is `Some`. - fn replace(prev: &mut Option, new: T, token: &Ident) -> Result<()> { + fn update(prev: &mut Option, new: T, token: &Ident) -> Result<()> { if prev.replace(new).is_some() { Err(error!(token, "duplicate `{}` argument", token)) } else { @@ -180,32 +183,26 @@ impl Parse for Args { } let input = parse_input(input)?; - let mut args = Self::default(); + let mut pinned_drop = None; + let mut replace = None; + let mut unsafe_unpin = None; + let mut not_unpin = None; while !input.is_empty() { - let token = input.parse::()?; - match &*token.to_string() { - "PinnedDrop" => { - replace(&mut args.pinned_drop, token.span(), &token)?; - if args.replace.is_some() { - return Err(error!( - token, - "arguments `PinnedDrop` and `Replace` are mutually exclusive" - )); - } + if input.peek(token::Bang) { + let t: token::Bang = input.parse()?; + let k: kw::Unpin = input.parse()?; + if not_unpin.replace(k).is_some() { + let span = quote!(#t #k); + return Err(error!(span, "duplicate `!Unpin` argument",)); } - "Replace" => { - replace(&mut args.replace, token.span(), &token)?; - if args.pinned_drop.is_some() { - return Err(error!( - token, - "arguments `PinnedDrop` and `Replace` are mutually exclusive" - )); - } - } - "UnsafeUnpin" => { - replace(&mut args.unsafe_unpin, token.span(), &token)?; + } else { + let token = input.parse::()?; + match &*token.to_string() { + "PinnedDrop" => update(&mut pinned_drop, token.span(), &token)?, + "Replace" => update(&mut replace, token.span(), &token)?, + "UnsafeUnpin" => update(&mut unsafe_unpin, token.span(), &token)?, + _ => return Err(error!(token, "unexpected argument: {}", token)), } - _ => return Err(error!(token, "unexpected argument: {}", token)), } if !input.is_empty() { @@ -213,7 +210,25 @@ impl Parse for Args { } } - Ok(args) + if let (Some(span), Some(_)) = (pinned_drop, replace) { + return Err(Error::new( + span, + "arguments `PinnedDrop` and `Replace` are mutually exclusive", + )); + } + let unpin_impl = match (unsafe_unpin, not_unpin) { + (Some(span), Some(_)) => { + return Err(Error::new( + span, + "arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive", + )); + } + (None, None) => UnpinImpl::Default, + (Some(span), None) => UnpinImpl::Unsafe(span), + (None, Some(span)) => UnpinImpl::Negative(span.span), + }; + + Ok(Self { pinned_drop, replace, unpin_impl }) } } @@ -275,10 +290,19 @@ struct Context<'a> { pinned_fields: Vec, /// `PinnedDrop` argument. pinned_drop: Option, - /// `UnsafeUnpin` argument. - unsafe_unpin: Option, /// `Replace` argument (requires Sized bound) replace: Option, + /// `UnsafeUnpin` or `!Unpin` argument. + unpin_impl: UnpinImpl, +} + +#[derive(Clone, Copy)] +enum UnpinImpl { + Default, + /// `UnsafeUnpin`. + Unsafe(Span), + /// `!Unpin`. + Negative(Span), } impl<'a> Context<'a> { @@ -288,7 +312,7 @@ impl<'a> Context<'a> { ident: &'a Ident, generics: &'a mut Generics, ) -> Result { - let Args { pinned_drop, unsafe_unpin, replace } = Args::get(attrs)?; + let Args { pinned_drop, replace, unpin_impl } = Args::get(attrs)?; let ty_generics = generics.split_for_impl().1; let self_ty = syn::parse_quote!(#ident #ty_generics); @@ -312,6 +336,9 @@ impl<'a> Context<'a> { where_clause.predicates.push(pred); Ok(Self { + pinned_drop, + replace, + unpin_impl, proj: ProjectedType { vis: determine_visibility(vis), mut_ident: Mutable.proj_ident(ident), @@ -322,9 +349,6 @@ impl<'a> Context<'a> { where_clause, }, orig: OriginalType { attrs, vis, ident, generics }, - pinned_drop, - unsafe_unpin, - replace, pinned_fields: Vec::new(), }) } @@ -704,96 +728,138 @@ impl<'a> Context<'a> { }) } - /// Creates conditional `Unpin` implementation for original type. - fn make_unpin_impl(&mut self) -> TokenStream { - if let Some(unsafe_unpin) = self.unsafe_unpin { - let mut proj_generics = self.proj.generics.clone(); - let orig_ident = self.orig.ident; - let lifetime = &self.proj.lifetime; + /// Creates `Unpin` implementation for original type. + fn make_unpin_impl(&self) -> TokenStream { + match self.unpin_impl { + UnpinImpl::Unsafe(span) => { + let mut proj_generics = self.proj.generics.clone(); + let orig_ident = self.orig.ident; + let lifetime = &self.proj.lifetime; - proj_generics.make_where_clause().predicates.push( - // Make the error message highlight `UnsafeUnpin` argument. - parse_quote_spanned! { unsafe_unpin => - ::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin - }, - ); + proj_generics.make_where_clause().predicates.push( + // Make the error message highlight `UnsafeUnpin` argument. + parse_quote_spanned! { span => + ::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin + }, + ); - let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); - let ty_generics = self.orig.generics.split_for_impl().1; + let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); + let ty_generics = self.orig.generics.split_for_impl().1; - quote! { - impl #impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #where_clause {} + quote! { + impl #impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #where_clause {} + } } - } else { - let mut full_where_clause = self.orig.generics.where_clause.as_ref().cloned().unwrap(); + UnpinImpl::Negative(span) => { + let mut proj_generics = self.proj.generics.clone(); + let orig_ident = self.orig.ident; + let lifetime = &self.proj.lifetime; - // Generate a field in our new struct for every - // pinned field in the original type. - let fields = self.pinned_fields.iter().enumerate().map(|(i, ty)| { - let field_ident = format_ident!("__field{}", i); - quote!(#field_ident: #ty) - }); + proj_generics.make_where_clause().predicates.push(parse_quote! { + ::pin_project::__private::Wrapper< + #lifetime, ::pin_project::__reexport::marker::PhantomPinned + >: ::pin_project::__reexport::marker::Unpin + }); - // We could try to determine the subset of type parameters - // and lifetimes that are actually used by the pinned fields - // (as opposed to those only used by unpinned fields). - // However, this would be tricky and error-prone, since - // it's possible for users to create types that would alias - // with generic parameters (e.g. 'struct T'). - // - // Instead, we generate a use of every single type parameter - // and lifetime used in the original struct. For type parameters, - // we generate code like this: - // - // ```rust - // struct AlwaysUnpin(PhantomData) {} - // impl Unpin for AlwaysUnpin {} - // - // ... - // _field: AlwaysUnpin<(A, B, C)> - // ``` - // - // This ensures that any unused type parameters - // don't end up with `Unpin` bounds. - let lifetime_fields = self.orig.generics.lifetimes().enumerate().map( - |(i, LifetimeDef { lifetime, .. })| { - let field_ident = format_ident!("__lifetime{}", i); - quote!(#field_ident: &#lifetime ()) - }, - ); + let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl(); + let (impl_generics, ty_generics, orig_where_clause) = + self.orig.generics.split_for_impl(); - let orig_ident = self.orig.ident; - let struct_ident = format_ident!("__{}", orig_ident); - let vis = self.orig.vis; - let lifetime = &self.proj.lifetime; - let type_params = self.orig.generics.type_params().map(|t| &t.ident); - let proj_generics = &self.proj.generics; - let (impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); - let (_, ty_generics, where_clause) = self.orig.generics.split_for_impl(); - - full_where_clause.predicates.push(syn::parse_quote! { - #struct_ident #proj_ty_generics: ::pin_project::__reexport::marker::Unpin - }); + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be call-site span. + let unsafety = token::Unsafe::default(); + quote_spanned! { span => + impl #proj_impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #proj_where_clause {} - quote! { - // This needs to have the same visibility as the original type, - // due to the limitations of the 'public in private' error. - // - // Our goal is to implement the public trait `Unpin` for - // a potentially public user type. Because of this, rust - // requires that any types mentioned in the where clause of - // our `Unpin` impl also be public. This means that our generated - // `__UnpinStruct` type must also be public. - // However, we ensure that the user can never actually reference - // this 'public' type by creating this type in the inside of `const`. - #vis struct #struct_ident #proj_generics #where_clause { - __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<#lifetime, (#(#type_params),*)>, - - #(#fields,)* - #(#lifetime_fields,)* + // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing a `UnsafeUnpin` impl, + // they'll get a "conflicting implementations of trait" error when coherence + // checks are run. + #unsafety impl #impl_generics ::pin_project::UnsafeUnpin for #orig_ident #ty_generics #orig_where_clause {} } + } + UnpinImpl::Default => { + let mut full_where_clause = + self.orig.generics.where_clause.as_ref().cloned().unwrap(); + + // Generate a field in our new struct for every + // pinned field in the original type. + let fields = self.pinned_fields.iter().enumerate().map(|(i, ty)| { + let field_ident = format_ident!("__field{}", i); + quote!(#field_ident: #ty) + }); + + // We could try to determine the subset of type parameters + // and lifetimes that are actually used by the pinned fields + // (as opposed to those only used by unpinned fields). + // However, this would be tricky and error-prone, since + // it's possible for users to create types that would alias + // with generic parameters (e.g. 'struct T'). + // + // Instead, we generate a use of every single type parameter + // and lifetime used in the original struct. For type parameters, + // we generate code like this: + // + // ```rust + // struct AlwaysUnpin(PhantomData) {} + // impl Unpin for AlwaysUnpin {} + // + // ... + // _field: AlwaysUnpin<(A, B, C)> + // ``` + // + // This ensures that any unused type parameters + // don't end up with `Unpin` bounds. + let lifetime_fields = self.orig.generics.lifetimes().enumerate().map( + |(i, LifetimeDef { lifetime, .. })| { + let field_ident = format_ident!("__lifetime{}", i); + quote!(#field_ident: &#lifetime ()) + }, + ); + + let orig_ident = self.orig.ident; + let struct_ident = format_ident!("__{}", orig_ident); + let vis = self.orig.vis; + let lifetime = &self.proj.lifetime; + let type_params = self.orig.generics.type_params().map(|t| &t.ident); + let proj_generics = &self.proj.generics; + let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); + let (impl_generics, ty_generics, where_clause) = + self.orig.generics.split_for_impl(); + + full_where_clause.predicates.push(syn::parse_quote! { + #struct_ident #proj_ty_generics: ::pin_project::__reexport::marker::Unpin + }); + + quote! { + // This needs to have the same visibility as the original type, + // due to the limitations of the 'public in private' error. + // + // Our goal is to implement the public trait `Unpin` for + // a potentially public user type. Because of this, rust + // requires that any types mentioned in the where clause of + // our `Unpin` impl also be public. This means that our generated + // `__UnpinStruct` type must also be public. + // However, we ensure that the user can never actually reference + // this 'public' type by creating this type in the inside of `const`. + #vis struct #struct_ident #proj_generics #where_clause { + __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<#lifetime, (#(#type_params),*)>, + + #(#fields,)* + #(#lifetime_fields,)* + } - impl #impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #full_where_clause {} + impl #proj_impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #full_where_clause {} + + // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing a `UnsafeUnpin` impl, + // they'll get a "conflicting implementations of trait" error when coherence + // checks are run. + unsafe impl #impl_generics ::pin_project::UnsafeUnpin for #orig_ident #ty_generics #where_clause {} + } } } } diff --git a/src/lib.rs b/src/lib.rs index 28b086b6..d6896c80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! ## Examples //! -//! [`pin_project`] attribute creates a projection struct covering all the fields. +//! [`pin_project`] attribute creates a projection type covering all the fields of struct or enum. //! //! ```rust //! use pin_project::pin_project; diff --git a/tests/compiletest.rs b/tests/compiletest.rs index ae455003..078abaa1 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -5,6 +5,7 @@ fn ui() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/cfg/*.rs"); + t.compile_fail("tests/ui/not_unpin/*.rs"); t.compile_fail("tests/ui/pin_project/*.rs"); t.compile_fail("tests/ui/pinned_drop/*.rs"); t.compile_fail("tests/ui/project/*.rs"); diff --git a/tests/doc/Cargo.toml b/tests/doc/Cargo.toml index b309e51f..111b6a38 100644 --- a/tests/doc/Cargo.toml +++ b/tests/doc/Cargo.toml @@ -5,6 +5,9 @@ authors = ["Taiki Endo "] edition = "2018" publish = false +[lib] +path = "lib.rs" + [dependencies] [dev-dependencies] diff --git a/tests/doc/src/lib.rs b/tests/doc/lib.rs similarity index 91% rename from tests/doc/src/lib.rs rename to tests/doc/lib.rs index db0b8d4d..d90e9593 100644 --- a/tests/doc/src/lib.rs +++ b/tests/doc/lib.rs @@ -11,5 +11,5 @@ // * https://github.com/rust-lang/rust/issues/62210 // * https://github.com/rust-lang/rust/pull/63803 -#[doc(include = "../../../README.md")] +#[doc(include = "../../README.md")] const _README: () = (); diff --git a/tests/expand/tests/expand/default-enum.expanded.rs b/tests/expand/tests/expand/default-enum.expanded.rs index 54eb7f79..90659e17 100644 --- a/tests/expand/tests/expand/default-enum.expanded.rs +++ b/tests/expand/tests/expand/default-enum.expanded.rs @@ -90,6 +90,7 @@ const __SCOPE_Enum: () = { __Enum<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Enum {} trait EnumMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl EnumMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/default-struct.expanded.rs b/tests/expand/tests/expand/default-struct.expanded.rs index 0a81664a..900feec5 100644 --- a/tests/expand/tests/expand/default-struct.expanded.rs +++ b/tests/expand/tests/expand/default-struct.expanded.rs @@ -62,6 +62,7 @@ const __SCOPE_Struct: () = { __Struct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Struct {} trait StructMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl StructMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/default-tuple_struct.expanded.rs b/tests/expand/tests/expand/default-tuple_struct.expanded.rs index 8532da34..6b643156 100644 --- a/tests/expand/tests/expand/default-tuple_struct.expanded.rs +++ b/tests/expand/tests/expand/default-tuple_struct.expanded.rs @@ -53,6 +53,7 @@ const __SCOPE_TupleStruct: () = { __TupleStruct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for TupleStruct {} trait TupleStructMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl TupleStructMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/not_unpin-enum.expanded.rs b/tests/expand/tests/expand/not_unpin-enum.expanded.rs new file mode 100644 index 00000000..56a2a7a8 --- /dev/null +++ b/tests/expand/tests/expand/not_unpin-enum.expanded.rs @@ -0,0 +1,98 @@ +use pin_project::pin_project; +# [ pin ( __private ( ! Unpin ) ) ] +enum Enum { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), + Unit, +} +#[doc(hidden)] +#[allow(clippy::mut_mut)] +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +enum __EnumProjection<'pin, T, U> +where + Enum: 'pin, +{ + Struct { + pinned: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + unpinned: &'pin mut (U), + }, + Tuple( + ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + &'pin mut (U), + ), + Unit, +} +#[doc(hidden)] +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +enum __EnumProjectionRef<'pin, T, U> +where + Enum: 'pin, +{ + Struct { + pinned: ::pin_project::__reexport::pin::Pin<&'pin (T)>, + unpinned: &'pin (U), + }, + Tuple(::pin_project::__reexport::pin::Pin<&'pin (T)>, &'pin (U)), + Unit, +} +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Enum: () = { + impl Enum { + fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __EnumProjection<'pin, T, U> { + unsafe { + match self.get_unchecked_mut() { + Enum::Struct { pinned, unpinned } => __EnumProjection::Struct { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + }, + Enum::Tuple(_0, _1) => __EnumProjection::Tuple( + ::pin_project::__reexport::pin::Pin::new_unchecked(_0), + _1, + ), + Enum::Unit => __EnumProjection::Unit, + } + } + } + fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __EnumProjectionRef<'pin, T, U> { + unsafe { + match self.get_ref() { + Enum::Struct { pinned, unpinned } => __EnumProjectionRef::Struct { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + }, + Enum::Tuple(_0, _1) => __EnumProjectionRef::Tuple( + ::pin_project::__reexport::pin::Pin::new_unchecked(_0), + _1, + ), + Enum::Unit => __EnumProjectionRef::Unit, + } + } + } + } + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Enum where + ::pin_project::__private::Wrapper<'pin, ::pin_project::__reexport::marker::PhantomPinned>: + ::pin_project::__reexport::marker::Unpin + { + } + unsafe impl ::pin_project::UnsafeUnpin for Enum {} + trait EnumMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl EnumMustNotImplDrop for T {} + impl EnumMustNotImplDrop for Enum {} + impl ::pin_project::__private::PinnedDrop for Enum { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } +}; +fn main() {} diff --git a/tests/expand/tests/expand/not_unpin-enum.rs b/tests/expand/tests/expand/not_unpin-enum.rs new file mode 100644 index 00000000..aae554e8 --- /dev/null +++ b/tests/expand/tests/expand/not_unpin-enum.rs @@ -0,0 +1,14 @@ +use pin_project::pin_project; + +#[pin_project(!Unpin)] +enum Enum { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), + Unit, +} + +fn main() {} diff --git a/tests/expand/tests/expand/not_unpin-struct.expanded.rs b/tests/expand/tests/expand/not_unpin-struct.expanded.rs new file mode 100644 index 00000000..6fb37e47 --- /dev/null +++ b/tests/expand/tests/expand/not_unpin-struct.expanded.rs @@ -0,0 +1,76 @@ +use pin_project::pin_project; +# [ pin ( __private ( ! Unpin ) ) ] +struct Struct { + #[pin] + pinned: T, + unpinned: U, +} +#[doc(hidden)] +#[allow(clippy::mut_mut)] +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +struct __StructProjection<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + unpinned: &'pin mut (U), +} +#[doc(hidden)] +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +struct __StructProjectionRef<'pin, T, U> +where + Struct: 'pin, +{ + pinned: ::pin_project::__reexport::pin::Pin<&'pin (T)>, + unpinned: &'pin (U), +} +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_Struct: () = { + impl Struct { + fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __StructProjection<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_unchecked_mut(); + __StructProjection { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __StructProjectionRef<'pin, T, U> { + unsafe { + let Self { pinned, unpinned } = self.get_ref(); + __StructProjectionRef { + pinned: ::pin_project::__reexport::pin::Pin::new_unchecked(pinned), + unpinned, + } + } + } + } + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for Struct where + ::pin_project::__private::Wrapper<'pin, ::pin_project::__reexport::marker::PhantomPinned>: + ::pin_project::__reexport::marker::Unpin + { + } + unsafe impl ::pin_project::UnsafeUnpin for Struct {} + trait StructMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl StructMustNotImplDrop for T {} + impl StructMustNotImplDrop for Struct {} + impl ::pin_project::__private::PinnedDrop for Struct { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed(val: &Struct) { + &val.pinned; + &val.unpinned; + } +}; +fn main() {} diff --git a/tests/expand/tests/expand/not_unpin-struct.rs b/tests/expand/tests/expand/not_unpin-struct.rs new file mode 100644 index 00000000..233e6d41 --- /dev/null +++ b/tests/expand/tests/expand/not_unpin-struct.rs @@ -0,0 +1,10 @@ +use pin_project::pin_project; + +#[pin_project(!Unpin)] +struct Struct { + #[pin] + pinned: T, + unpinned: U, +} + +fn main() {} diff --git a/tests/expand/tests/expand/not_unpin-tuple_struct.expanded.rs b/tests/expand/tests/expand/not_unpin-tuple_struct.expanded.rs new file mode 100644 index 00000000..ccad517a --- /dev/null +++ b/tests/expand/tests/expand/not_unpin-tuple_struct.expanded.rs @@ -0,0 +1,67 @@ +use pin_project::pin_project; +# [ pin ( __private ( ! Unpin ) ) ] +struct TupleStruct(#[pin] T, U); +#[doc(hidden)] +#[allow(clippy::mut_mut)] +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +struct __TupleStructProjection<'pin, T, U>( + ::pin_project::__reexport::pin::Pin<&'pin mut (T)>, + &'pin mut (U), +) +where + TupleStruct: 'pin; +#[doc(hidden)] +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +struct __TupleStructProjectionRef<'pin, T, U>( + ::pin_project::__reexport::pin::Pin<&'pin (T)>, + &'pin (U), +) +where + TupleStruct: 'pin; +#[doc(hidden)] +#[allow(non_upper_case_globals)] +#[allow(single_use_lifetimes)] +const __SCOPE_TupleStruct: () = { + impl TupleStruct { + fn project<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin mut Self>, + ) -> __TupleStructProjection<'pin, T, U> { + unsafe { + let Self(_0, _1) = self.get_unchecked_mut(); + __TupleStructProjection(::pin_project::__reexport::pin::Pin::new_unchecked(_0), _1) + } + } + fn project_ref<'pin>( + self: ::pin_project::__reexport::pin::Pin<&'pin Self>, + ) -> __TupleStructProjectionRef<'pin, T, U> { + unsafe { + let Self(_0, _1) = self.get_ref(); + __TupleStructProjectionRef( + ::pin_project::__reexport::pin::Pin::new_unchecked(_0), + _1, + ) + } + } + } + impl<'pin, T, U> ::pin_project::__reexport::marker::Unpin for TupleStruct where + ::pin_project::__private::Wrapper<'pin, ::pin_project::__reexport::marker::PhantomPinned>: + ::pin_project::__reexport::marker::Unpin + { + } + unsafe impl ::pin_project::UnsafeUnpin for TupleStruct {} + trait TupleStructMustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl TupleStructMustNotImplDrop for T {} + impl TupleStructMustNotImplDrop for TupleStruct {} + impl ::pin_project::__private::PinnedDrop for TupleStruct { + unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + } + #[deny(safe_packed_borrows)] + fn __assert_not_repr_packed(val: &TupleStruct) { + &val.0; + &val.1; + } +}; +fn main() {} diff --git a/tests/expand/tests/expand/not_unpin-tuple_struct.rs b/tests/expand/tests/expand/not_unpin-tuple_struct.rs new file mode 100644 index 00000000..c8065db6 --- /dev/null +++ b/tests/expand/tests/expand/not_unpin-tuple_struct.rs @@ -0,0 +1,6 @@ +use pin_project::pin_project; + +#[pin_project(!Unpin)] +struct TupleStruct(#[pin] T, U); + +fn main() {} diff --git a/tests/expand/tests/expand/pinned_drop-enum.expanded.rs b/tests/expand/tests/expand/pinned_drop-enum.expanded.rs index fda858a0..d445f5f0 100644 --- a/tests/expand/tests/expand/pinned_drop-enum.expanded.rs +++ b/tests/expand/tests/expand/pinned_drop-enum.expanded.rs @@ -91,6 +91,7 @@ const __SCOPE_Enum: () = { __Enum<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Enum {} impl ::pin_project::__reexport::ops::Drop for Enum { fn drop(&mut self) { let pinned_self = unsafe { ::pin_project::__reexport::pin::Pin::new_unchecked(self) }; diff --git a/tests/expand/tests/expand/pinned_drop-struct.expanded.rs b/tests/expand/tests/expand/pinned_drop-struct.expanded.rs index 42b16bb8..b103d095 100644 --- a/tests/expand/tests/expand/pinned_drop-struct.expanded.rs +++ b/tests/expand/tests/expand/pinned_drop-struct.expanded.rs @@ -63,6 +63,7 @@ const __SCOPE_Struct: () = { __Struct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Struct {} impl ::pin_project::__reexport::ops::Drop for Struct { fn drop(&mut self) { let pinned_self = unsafe { ::pin_project::__reexport::pin::Pin::new_unchecked(self) }; diff --git a/tests/expand/tests/expand/pinned_drop-tuple_struct.expanded.rs b/tests/expand/tests/expand/pinned_drop-tuple_struct.expanded.rs index 757f4368..1d9dbfb0 100644 --- a/tests/expand/tests/expand/pinned_drop-tuple_struct.expanded.rs +++ b/tests/expand/tests/expand/pinned_drop-tuple_struct.expanded.rs @@ -54,6 +54,7 @@ const __SCOPE_TupleStruct: () = { __TupleStruct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for TupleStruct {} impl ::pin_project::__reexport::ops::Drop for TupleStruct { fn drop(&mut self) { let pinned_self = unsafe { ::pin_project::__reexport::pin::Pin::new_unchecked(self) }; diff --git a/tests/expand/tests/expand/project_replace-enum.expanded.rs b/tests/expand/tests/expand/project_replace-enum.expanded.rs index 77887854..7237896f 100644 --- a/tests/expand/tests/expand/project_replace-enum.expanded.rs +++ b/tests/expand/tests/expand/project_replace-enum.expanded.rs @@ -148,6 +148,7 @@ const __SCOPE_Enum: () = { __Enum<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Enum {} trait EnumMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl EnumMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/project_replace-struct.expanded.rs b/tests/expand/tests/expand/project_replace-struct.expanded.rs index 693cd070..ce647168 100644 --- a/tests/expand/tests/expand/project_replace-struct.expanded.rs +++ b/tests/expand/tests/expand/project_replace-struct.expanded.rs @@ -90,6 +90,7 @@ const __SCOPE_Struct: () = { __Struct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Struct {} trait StructMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl StructMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/project_replace-tuple_struct.expanded.rs b/tests/expand/tests/expand/project_replace-tuple_struct.expanded.rs index 884287e7..d31de5e7 100644 --- a/tests/expand/tests/expand/project_replace-tuple_struct.expanded.rs +++ b/tests/expand/tests/expand/project_replace-tuple_struct.expanded.rs @@ -78,6 +78,7 @@ const __SCOPE_TupleStruct: () = { __TupleStruct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for TupleStruct {} trait TupleStructMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl TupleStructMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/pub-enum.expanded.rs b/tests/expand/tests/expand/pub-enum.expanded.rs index 860dc9af..5786384b 100644 --- a/tests/expand/tests/expand/pub-enum.expanded.rs +++ b/tests/expand/tests/expand/pub-enum.expanded.rs @@ -90,6 +90,7 @@ const __SCOPE_Enum: () = { __Enum<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Enum {} trait EnumMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl EnumMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/pub-struct.expanded.rs b/tests/expand/tests/expand/pub-struct.expanded.rs index a8d463c0..84d9a0d5 100644 --- a/tests/expand/tests/expand/pub-struct.expanded.rs +++ b/tests/expand/tests/expand/pub-struct.expanded.rs @@ -62,6 +62,7 @@ const __SCOPE_Struct: () = { __Struct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for Struct {} trait StructMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl StructMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/pub-tuple_struct.expanded.rs b/tests/expand/tests/expand/pub-tuple_struct.expanded.rs index 3a8c8eaf..fe950a33 100644 --- a/tests/expand/tests/expand/pub-tuple_struct.expanded.rs +++ b/tests/expand/tests/expand/pub-tuple_struct.expanded.rs @@ -53,6 +53,7 @@ const __SCOPE_TupleStruct: () = { __TupleStruct<'pin, T, U>: ::pin_project::__reexport::marker::Unpin { } + unsafe impl ::pin_project::UnsafeUnpin for TupleStruct {} trait TupleStructMustNotImplDrop {} #[allow(clippy::drop_bounds)] impl TupleStructMustNotImplDrop for T {} diff --git a/tests/expand/tests/expand/unsafe_unpin-enum.expanded.rs b/tests/expand/tests/expand/unsafe_unpin-enum.expanded.rs index 550a0197..90b06203 100644 --- a/tests/expand/tests/expand/unsafe_unpin-enum.expanded.rs +++ b/tests/expand/tests/expand/unsafe_unpin-enum.expanded.rs @@ -93,4 +93,5 @@ const __SCOPE_Enum: () = { unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} } }; +unsafe impl UnsafeUnpin for Enum {} fn main() {} diff --git a/tests/expand/tests/expand/unsafe_unpin-enum.rs b/tests/expand/tests/expand/unsafe_unpin-enum.rs index dd32850c..c1350f61 100644 --- a/tests/expand/tests/expand/unsafe_unpin-enum.rs +++ b/tests/expand/tests/expand/unsafe_unpin-enum.rs @@ -11,4 +11,6 @@ enum Enum { Unit, } +unsafe impl UnsafeUnpin for Enum {} + fn main() {} diff --git a/tests/forbid_unsafe.rs b/tests/forbid_unsafe.rs index ec11cea5..4b2e2482 100644 --- a/tests/forbid_unsafe.rs +++ b/tests/forbid_unsafe.rs @@ -2,31 +2,23 @@ #![warn(rust_2018_idioms, single_use_lifetimes)] #![allow(dead_code)] -// default #[pin_project], PinnedDrop, Replace are completely safe. +// default #[pin_project], PinnedDrop, Replace, and !Unpin are completely safe. use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; #[pin_project] -struct StructDefault { +pub struct StructDefault { #[pin] - pinned: T, - unpinned: U, -} - -// UnsafeUnpin without UnsafeUnpin impl is also safe -#[pin_project(UnsafeUnpin)] -struct StructUnsafeUnpin { - #[pin] - pinned: T, - unpinned: U, + pub pinned: T, + pub unpinned: U, } #[pin_project(PinnedDrop)] -struct StructPinnedDrop { +pub struct StructPinnedDrop { #[pin] - pinned: T, - unpinned: U, + pub pinned: T, + pub unpinned: U, } #[pinned_drop] @@ -35,38 +27,45 @@ impl PinnedDrop for StructPinnedDrop { } #[pin_project(Replace)] -struct StructReplace { +pub struct StructReplace { #[pin] - pinned: T, - unpinned: U, -} - -#[pin_project] -enum EnumDefault { - Variant { - #[pin] - pinned: T, - unpinned: U, - }, + pub pinned: T, + pub unpinned: U, } // UnsafeUnpin without UnsafeUnpin impl is also safe #[pin_project(UnsafeUnpin)] -enum EnumUnsafeUnpin { - Variant { +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pin_project(PinnedDrop)] -enum EnumPinnedDrop { - Variant { +pub enum EnumPinnedDrop { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pinned_drop] @@ -75,12 +74,34 @@ impl PinnedDrop for EnumPinnedDrop { } #[pin_project(Replace)] -enum EnumReplace { - Variant { +pub enum EnumReplace { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +// UnsafeUnpin without UnsafeUnpin impl is also safe +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[test] diff --git a/tests/lints.rs b/tests/lints.rs index 71157d10..4009f55b 100644 --- a/tests/lints.rs +++ b/tests/lints.rs @@ -12,15 +12,6 @@ pub struct StructDefault { pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub struct StructUnsafeUnpin { - #[pin] - pub pinned: T, - pub unpinned: U, -} - -unsafe impl UnsafeUnpin for StructUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub struct StructPinnedDrop { #[pin] @@ -40,6 +31,22 @@ pub struct StructReplace { pub unpinned: U, } +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + #[pin_project] pub struct StructMutMut<'a, T, U> { #[pin] @@ -49,31 +56,22 @@ pub struct StructMutMut<'a, T, U> { #[pin_project] pub enum EnumDefault { - Variant { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -#[pin_project(UnsafeUnpin)] -pub enum EnumUnsafeUnpin { - Variant { - #[pin] - pinned: T, - unpinned: U, - }, -} - -unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub enum EnumPinnedDrop { - Variant { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pinned_drop] @@ -83,20 +81,44 @@ impl PinnedDrop for EnumPinnedDrop { #[pin_project(Replace)] pub enum EnumReplace { - Variant { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pin_project] pub enum EnumMutMut<'a, T, U> { - Variant { + Struct { #[pin] pinned: &'a mut T, unpinned: &'a mut U, }, + Tuple(#[pin] T, U), } #[allow(clippy::missing_const_for_fn)] diff --git a/tests/no-std/Cargo.toml b/tests/no-std/Cargo.toml index 40a031fe..59b375ac 100644 --- a/tests/no-std/Cargo.toml +++ b/tests/no-std/Cargo.toml @@ -5,6 +5,9 @@ authors = ["Taiki Endo "] edition = "2018" publish = false +[lib] +path = "lib.rs" + [workspace] [dependencies] diff --git a/tests/no-std/src/lib.rs b/tests/no-std/lib.rs similarity index 77% rename from tests/no-std/src/lib.rs rename to tests/no-std/lib.rs index 12c945ff..4d088563 100644 --- a/tests/no-std/src/lib.rs +++ b/tests/no-std/lib.rs @@ -11,15 +11,6 @@ pub struct StructDefault { pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub struct StructUnsafeUnpin { - #[pin] - pub pinned: T, - pub unpinned: U, -} - -unsafe impl UnsafeUnpin for StructUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub struct StructPinnedDrop { #[pin] @@ -39,33 +30,40 @@ pub struct StructReplace { pub unpinned: U, } -#[pin_project] -pub enum EnumDefault { - Variant { - #[pin] - pinned: T, - unpinned: U, - }, +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub enum EnumUnsafeUnpin { - Variant { +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub enum EnumPinnedDrop { - Variant { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pinned_drop] @@ -75,9 +73,32 @@ impl PinnedDrop for EnumPinnedDrop { #[pin_project(Replace)] pub enum EnumReplace { - Variant { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } diff --git a/tests/overwriting_core_crate.rs b/tests/overwriting_core_crate.rs index b4f50fbc..121104cb 100644 --- a/tests/overwriting_core_crate.rs +++ b/tests/overwriting_core_crate.rs @@ -1,5 +1,4 @@ #![warn(rust_2018_idioms, single_use_lifetimes)] -#![allow(dead_code)] // See https://github.com/rust-lang/pin-utils/pull/26#discussion_r344491597 // @@ -15,26 +14,17 @@ use ::pin_project::{pin_project, pinned_drop, UnsafeUnpin}; use std::pin::Pin; #[pin_project] -struct StructDefault { +pub struct StructDefault { #[pin] - pinned: T, - unpinned: U, + pub pinned: T, + pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -struct StructUnsafeUnpin { - #[pin] - pinned: T, - unpinned: U, -} - -unsafe impl UnsafeUnpin for StructUnsafeUnpin {} - #[pin_project(PinnedDrop)] -struct StructPinnedDrop { +pub struct StructPinnedDrop { #[pin] - pinned: T, - unpinned: U, + pub pinned: T, + pub unpinned: U, } #[pinned_drop] @@ -43,53 +33,83 @@ impl PinnedDrop for StructPinnedDrop { } #[pin_project(Replace)] -struct StructReplace { +pub struct StructReplace { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { #[pin] - pinned: T, - unpinned: U, + pub pinned: T, + pub unpinned: U, +} + +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, } #[pin_project] -enum EnumDefault { - Variant { +pub enum EnumDefault { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -#[pin_project(UnsafeUnpin)] -enum EnumUnsafeUnpin { - Variant { +#[pin_project(PinnedDrop)] +pub enum EnumPinnedDrop { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} +#[pinned_drop] +impl PinnedDrop for EnumPinnedDrop { + fn drop(self: Pin<&mut Self>) {} +} -#[pin_project(PinnedDrop)] -enum EnumPinnedDrop { - Variant { +#[pin_project(Replace)] +pub enum EnumReplace { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -#[pinned_drop] -impl PinnedDrop for EnumPinnedDrop { - fn drop(self: Pin<&mut Self>) {} +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), } -#[pin_project(Replace)] -enum EnumReplace { - Variant { +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[test] diff --git a/tests/pin_project.rs b/tests/pin_project.rs index ee050ad5..c84ca4b7 100644 --- a/tests/pin_project.rs +++ b/tests/pin_project.rs @@ -363,6 +363,23 @@ fn combine() { } unsafe impl UnsafeUnpin for Struct2 {} + + #[pin_project(PinnedDrop, !Unpin)] + pub struct Struct3 { + #[pin] + field: T, + } + + #[pinned_drop] + impl PinnedDrop for Struct3 { + fn drop(self: Pin<&mut Self>) {} + } + + #[pin_project(!Unpin, Replace)] + pub struct Struct4 { + #[pin] + field: T, + } } #[test] @@ -572,6 +589,17 @@ fn dst() { fn drop(self: Pin<&mut Self>) {} } + #[pin_project(!Unpin)] + struct Struct9 { + x: T, + } + + #[pin_project(!Unpin)] + struct Struct10 { + #[pin] + x: T, + } + #[pin_project] struct TupleStruct1(T); @@ -599,6 +627,12 @@ fn dst() { impl PinnedDrop for TupleStruct8 { fn drop(self: Pin<&mut Self>) {} } + + #[pin_project(!Unpin)] + struct TupleStruct9(T); + + #[pin_project(!Unpin)] + struct TupleStruct10(#[pin] T); } #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993 diff --git a/tests/rust-2015/tests/no-std.rs b/tests/rust-2015/tests/no-std.rs index 2140b956..e214b1a3 100644 --- a/tests/rust-2015/tests/no-std.rs +++ b/tests/rust-2015/tests/no-std.rs @@ -12,15 +12,6 @@ pub struct StructDefault { pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub struct StructUnsafeUnpin { - #[pin] - pub pinned: T, - pub unpinned: U, -} - -unsafe impl UnsafeUnpin for StructUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub struct StructPinnedDrop { #[pin] @@ -40,33 +31,40 @@ pub struct StructReplace { pub unpinned: U, } -#[pin_project] -pub enum EnumDefault { - Variant { - #[pin] - pinned: T, - unpinned: U, - }, +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub enum EnumUnsafeUnpin { - Variant { +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub enum EnumPinnedDrop { - Variant { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pinned_drop] @@ -76,9 +74,32 @@ impl PinnedDrop for EnumPinnedDrop { #[pin_project(Replace)] pub enum EnumReplace { - Variant { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } diff --git a/tests/rust-2015/tests/std.rs b/tests/rust-2015/tests/std.rs index d67a1d98..dcab4ac2 100644 --- a/tests/rust-2015/tests/std.rs +++ b/tests/rust-2015/tests/std.rs @@ -10,15 +10,6 @@ pub struct StructDefault { pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub struct StructUnsafeUnpin { - #[pin] - pub pinned: T, - pub unpinned: U, -} - -unsafe impl UnsafeUnpin for StructUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub struct StructPinnedDrop { #[pin] @@ -38,33 +29,40 @@ pub struct StructReplace { pub unpinned: U, } -#[pin_project] -pub enum EnumDefault { - Variant { - #[pin] - pinned: T, - unpinned: U, - }, +#[pin_project(UnsafeUnpin)] +pub struct StructUnsafeUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, } -#[pin_project(UnsafeUnpin)] -pub enum EnumUnsafeUnpin { - Variant { +unsafe impl UnsafeUnpin for StructUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub struct StructNotUnpin { + #[pin] + pub pinned: T, + pub unpinned: U, +} + +#[pin_project] +pub enum EnumDefault { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } -unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} - #[pin_project(PinnedDrop)] pub enum EnumPinnedDrop { - Variant { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } #[pinned_drop] @@ -74,9 +72,32 @@ impl PinnedDrop for EnumPinnedDrop { #[pin_project(Replace)] pub enum EnumReplace { - Variant { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +#[pin_project(UnsafeUnpin)] +pub enum EnumUnsafeUnpin { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Tuple(#[pin] T, U), +} + +unsafe impl UnsafeUnpin for EnumUnsafeUnpin {} + +#[pin_project(!Unpin)] +pub enum EnumNotUnpin { + Struct { #[pin] pinned: T, unpinned: U, }, + Tuple(#[pin] T, U), } diff --git a/tests/ui/auxiliary/Cargo.toml b/tests/ui/auxiliary/Cargo.toml index 4531b300..9170dc77 100644 --- a/tests/ui/auxiliary/Cargo.toml +++ b/tests/ui/auxiliary/Cargo.toml @@ -6,8 +6,10 @@ edition = "2018" publish = false [lib] +path = "lib.rs" proc-macro = true + [dependencies] quote = "1.0" syn = { version = "1.0", features = ["full"] } diff --git a/tests/ui/auxiliary/src/lib.rs b/tests/ui/auxiliary/lib.rs similarity index 100% rename from tests/ui/auxiliary/src/lib.rs rename to tests/ui/auxiliary/lib.rs diff --git a/tests/ui/not_unpin/assert-not-unpin.rs b/tests/ui/not_unpin/assert-not-unpin.rs new file mode 100644 index 00000000..b8f8238e --- /dev/null +++ b/tests/ui/not_unpin/assert-not-unpin.rs @@ -0,0 +1,40 @@ +use pin_project::pin_project; +use std::marker::PhantomPinned; + +struct Inner { + val: T, +} + +#[pin_project(!Unpin)] +struct Foo { + #[pin] + inner: Inner, + other: U, +} + +#[pin_project(!Unpin)] +struct TrivialBounds { + #[pin] + field1: PhantomPinned, +} + +#[pin_project(!Unpin)] +struct Bar<'a, T, U> { + #[pin] + inner: &'a mut Inner, + other: U, +} + +fn is_unpin() {} + +fn main() { + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + + is_unpin::(); //~ ERROR E0277 + + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 +} diff --git a/tests/ui/not_unpin/assert-not-unpin.stderr b/tests/ui/not_unpin/assert-not-unpin.stderr new file mode 100644 index 00000000..5e323fc1 --- /dev/null +++ b/tests/ui/not_unpin/assert-not-unpin.stderr @@ -0,0 +1,83 @@ +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:31:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +31 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), ()>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:32:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +32 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:33:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +33 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), std::marker::PhantomPinned>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:34:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +34 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:36:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +36 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:38:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +38 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<'_, (), ()>` + +error[E0277]: `std::marker::PhantomPinned` cannot be unpinned + --> $DIR/assert-not-unpin.rs:39:5 + | +28 | fn is_unpin() {} + | ----- required by this bound in `is_unpin` +... +39 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>` diff --git a/tests/ui/not_unpin/conflict-unpin.rs b/tests/ui/not_unpin/conflict-unpin.rs new file mode 100644 index 00000000..f259f6c0 --- /dev/null +++ b/tests/ui/not_unpin/conflict-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::pin_project; + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Unpin for Foo where T: Unpin {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +impl Unpin for Bar {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +impl Unpin for Baz {} + +fn main() {} diff --git a/tests/ui/not_unpin/conflict-unpin.stderr b/tests/ui/not_unpin/conflict-unpin.stderr new file mode 100644 index 00000000..7407bdfd --- /dev/null +++ b/tests/ui/not_unpin/conflict-unpin.stderr @@ -0,0 +1,26 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: + --> $DIR/conflict-unpin.rs:3:16 + | +3 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | impl Unpin for Foo where T: Unpin {} + | --------------------------------------------- first implementation here + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: + --> $DIR/conflict-unpin.rs:12:16 + | +12 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | impl Unpin for Bar {} + | ------------------------------ first implementation here + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: + --> $DIR/conflict-unpin.rs:21:16 + | +21 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | impl Unpin for Baz {} + | -------------------------------------------- first implementation here diff --git a/tests/ui/not_unpin/impl-unsafe-unpin.rs b/tests/ui/not_unpin/impl-unsafe-unpin.rs new file mode 100644 index 00000000..625dc290 --- /dev/null +++ b/tests/ui/not_unpin/impl-unsafe-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::{pin_project, UnsafeUnpin}; + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Foo where T: Unpin {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Bar {} + +#[pin_project(!Unpin)] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Baz {} + +fn main() {} diff --git a/tests/ui/not_unpin/impl-unsafe-unpin.stderr b/tests/ui/not_unpin/impl-unsafe-unpin.stderr new file mode 100644 index 00000000..ba80d5eb --- /dev/null +++ b/tests/ui/not_unpin/impl-unsafe-unpin.stderr @@ -0,0 +1,32 @@ +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Foo<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:3:1 + | +3 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | unsafe impl UnsafeUnpin for Foo where T: Unpin {} + | ---------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Bar<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:12:1 + | +12 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | unsafe impl UnsafeUnpin for Bar {} + | ------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Baz<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:21:1 + | +21 | #[pin_project(!Unpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | unsafe impl UnsafeUnpin for Baz {} + | --------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/impl-unsafe-unpin.rs b/tests/ui/pin_project/impl-unsafe-unpin.rs new file mode 100644 index 00000000..94af322a --- /dev/null +++ b/tests/ui/pin_project/impl-unsafe-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::{pin_project, UnsafeUnpin}; + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Foo where T: Unpin {} + +#[pin_project] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Bar {} + +#[pin_project] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +unsafe impl UnsafeUnpin for Baz {} + +fn main() {} diff --git a/tests/ui/pin_project/impl-unsafe-unpin.stderr b/tests/ui/pin_project/impl-unsafe-unpin.stderr new file mode 100644 index 00000000..78545c2c --- /dev/null +++ b/tests/ui/pin_project/impl-unsafe-unpin.stderr @@ -0,0 +1,32 @@ +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Foo<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:3:1 + | +3 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | unsafe impl UnsafeUnpin for Foo where T: Unpin {} + | ---------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Bar<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:12:1 + | +12 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | unsafe impl UnsafeUnpin for Bar {} + | ------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Baz<_, _>`: + --> $DIR/impl-unsafe-unpin.rs:21:1 + | +21 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | unsafe impl UnsafeUnpin for Baz {} + | --------------------------------------------------------- first implementation here + | + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/pin_project/invalid.rs b/tests/ui/pin_project/invalid.rs index 9a2d9607..1d056084 100644 --- a/tests/ui/pin_project/invalid.rs +++ b/tests/ui/pin_project/invalid.rs @@ -100,15 +100,18 @@ mod pin_project_argument { #[pin_project()] // Ok struct Unexpected4(#[pin] ()); - #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument - struct DuplicateUnsafeUnpin(#[pin] ()); - #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument struct DuplicatePinnedDrop(#[pin] ()); #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument struct DuplicateReplace(#[pin] ()); + #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument + struct DuplicateUnsafeUnpin(#[pin] ()); + + #[pin_project(!Unpin, !Unpin)] //~ ERROR duplicate `!Unpin` argument + struct DuplicateNotUnpin(#[pin] ()); + #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument struct Duplicate3(#[pin] ()); @@ -116,7 +119,22 @@ mod pin_project_argument { struct Duplicate4(#[pin] ()); #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive - struct PinnedDropWithReplace(#[pin] ()); + struct PinnedDropWithReplace1(#[pin] ()); + + #[pin_project(Replace, UnsafeUnpin, PinnedDrop)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + struct PinnedDropWithReplace2(#[pin] ()); + + #[pin_project(UnsafeUnpin, !Unpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + struct UnsafeUnpinWithNotUnpin1(#[pin] ()); + + #[pin_project(!Unpin, PinnedDrop, UnsafeUnpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + struct UnsafeUnpinWithNotUnpin2(#[pin] ()); + + #[pin_project(!)] //~ ERROR unexpected end of input, expected `Unpin` + struct NotUnpin1(#[pin] ()); + + #[pin_project(Unpin)] //~ ERROR unexpected argument + struct NotUnpin2(#[pin] ()); } mod pin_project_attribute { diff --git a/tests/ui/pin_project/invalid.stderr b/tests/ui/pin_project/invalid.stderr index c7f2a462..95b57f0a 100644 --- a/tests/ui/pin_project/invalid.stderr +++ b/tests/ui/pin_project/invalid.stderr @@ -82,109 +82,147 @@ error: expected identifier 97 | #[pin_project(,UnsafeUnpin)] //~ ERROR expected identifier | ^ -error: duplicate `UnsafeUnpin` argument - --> $DIR/invalid.rs:103:32 - | -103 | #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument - | ^^^^^^^^^^^ - error: duplicate `PinnedDrop` argument - --> $DIR/invalid.rs:106:31 + --> $DIR/invalid.rs:103:31 | -106 | #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument +103 | #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument | ^^^^^^^^^^ error: duplicate `Replace` argument - --> $DIR/invalid.rs:109:28 + --> $DIR/invalid.rs:106:28 | -109 | #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument +106 | #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument | ^^^^^^^ error: duplicate `UnsafeUnpin` argument - --> $DIR/invalid.rs:112:44 + --> $DIR/invalid.rs:109:32 + | +109 | #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument + | ^^^^^^^^^^^ + +error: duplicate `!Unpin` argument + --> $DIR/invalid.rs:112:27 + | +112 | #[pin_project(!Unpin, !Unpin)] //~ ERROR duplicate `!Unpin` argument + | ^^^^^^ + +error: duplicate `UnsafeUnpin` argument + --> $DIR/invalid.rs:115:44 | -112 | #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument +115 | #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument | ^^^^^^^^^^^ error: duplicate `PinnedDrop` argument - --> $DIR/invalid.rs:115:44 + --> $DIR/invalid.rs:118:44 | -115 | #[pin_project(PinnedDrop, UnsafeUnpin, PinnedDrop, UnsafeUnpin)] //~ ERROR duplicate `PinnedDrop` argument +118 | #[pin_project(PinnedDrop, UnsafeUnpin, PinnedDrop, UnsafeUnpin)] //~ ERROR duplicate `PinnedDrop` argument | ^^^^^^^^^^ error: arguments `PinnedDrop` and `Replace` are mutually exclusive - --> $DIR/invalid.rs:118:31 + --> $DIR/invalid.rs:121:19 + | +121 | #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + | ^^^^^^^^^^ + +error: arguments `PinnedDrop` and `Replace` are mutually exclusive + --> $DIR/invalid.rs:124:41 + | +124 | #[pin_project(Replace, UnsafeUnpin, PinnedDrop)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive + | ^^^^^^^^^^ + +error: arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + --> $DIR/invalid.rs:127:19 + | +127 | #[pin_project(UnsafeUnpin, !Unpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + | ^^^^^^^^^^^ + +error: arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + --> $DIR/invalid.rs:130:39 + | +130 | #[pin_project(!Unpin, PinnedDrop, UnsafeUnpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive + | ^^^^^^^^^^^ + +error: unexpected end of input, expected `Unpin` + --> $DIR/invalid.rs:133:5 + | +133 | #[pin_project(!)] //~ ERROR unexpected end of input, expected `Unpin` + | ^^^^^^^^^^^^^^^^^ + | + = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected argument: Unpin + --> $DIR/invalid.rs:136:19 | -118 | #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive - | ^^^^^^^ +136 | #[pin_project(Unpin)] //~ ERROR unexpected argument + | ^^^^^ error: duplicate #[pin_project] attribute - --> $DIR/invalid.rs:126:5 + --> $DIR/invalid.rs:144:5 | -126 | #[pin_project] //~ ERROR duplicate #[pin_project] attribute +144 | #[pin_project] //~ ERROR duplicate #[pin_project] attribute | ^^^^^^^^^^^^^^ error: #[pin_project] attribute may not be used on structs with zero fields - --> $DIR/invalid.rs:134:19 + --> $DIR/invalid.rs:152:19 | -134 | struct Struct {} //~ ERROR may not be used on structs with zero fields +152 | struct Struct {} //~ ERROR may not be used on structs with zero fields | ^^ error: #[pin_project] attribute may not be used on structs with zero fields - --> $DIR/invalid.rs:137:23 + --> $DIR/invalid.rs:155:23 | -137 | struct TupleStruct(); //~ ERROR may not be used on structs with zero fields +155 | struct TupleStruct(); //~ ERROR may not be used on structs with zero fields | ^^ error: #[pin_project] attribute may not be used on structs with zero fields - --> $DIR/invalid.rs:140:12 + --> $DIR/invalid.rs:158:12 | -140 | struct UnitStruct; //~ ERROR may not be used on structs with zero fields +158 | struct UnitStruct; //~ ERROR may not be used on structs with zero fields | ^^^^^^^^^^ error: #[pin_project] attribute may not be used on enums without variants - --> $DIR/invalid.rs:143:20 + --> $DIR/invalid.rs:161:20 | -143 | enum EnumEmpty {} //~ ERROR may not be used on enums without variants +161 | enum EnumEmpty {} //~ ERROR may not be used on enums without variants | ^^ error: #[pin_project] attribute may not be used on enums with discriminants - --> $DIR/invalid.rs:147:13 + --> $DIR/invalid.rs:165:13 | -147 | V = 2, //~ ERROR may not be used on enums with discriminants +165 | V = 2, //~ ERROR may not be used on enums with discriminants | ^ error: #[pin_project] attribute may not be used on enums with zero fields - --> $DIR/invalid.rs:152:9 + --> $DIR/invalid.rs:170:9 | -152 | / Unit, //~ ERROR may not be used on enums with zero fields -153 | | Tuple(), -154 | | Struct {}, +170 | / Unit, //~ ERROR may not be used on enums with zero fields +171 | | Tuple(), +172 | | Struct {}, | |__________________^ error: #[pin_project] attribute may only be used on structs or enums - --> $DIR/invalid.rs:158:5 + --> $DIR/invalid.rs:176:5 | -158 | / union Union { -159 | | //~^ ERROR may only be used on structs or enums -160 | | f: (), -161 | | } +176 | / union Union { +177 | | //~^ ERROR may only be used on structs or enums +178 | | f: (), +179 | | } | |_____^ error: #[pin_project] attribute may not be used on #[repr(packed)] types - --> $DIR/invalid.rs:169:12 + --> $DIR/invalid.rs:187:12 | -169 | #[repr(packed)] +187 | #[repr(packed)] | ^^^^^^ error: #[pin_project] attribute may not be used on #[repr(packed)] types - --> $DIR/invalid.rs:173:12 + --> $DIR/invalid.rs:191:12 | -173 | #[repr(packed)] +191 | #[repr(packed)] | ^^^^^^ error: #[pin_project] attribute may not be used on #[repr(packed)] types - --> $DIR/invalid.rs:177:12 + --> $DIR/invalid.rs:195:12 | -177 | #[repr(packed)] +195 | #[repr(packed)] | ^^^^^^ diff --git a/tests/ui/unsafe_unpin/conflict-unpin.rs b/tests/ui/unsafe_unpin/conflict-unpin.rs new file mode 100644 index 00000000..e0c8a7b1 --- /dev/null +++ b/tests/ui/unsafe_unpin/conflict-unpin.rs @@ -0,0 +1,30 @@ +use pin_project::pin_project; + +#[pin_project(UnsafeUnpin)] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Unpin for Foo where T: Unpin {} + +#[pin_project(UnsafeUnpin)] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +impl Unpin for Bar {} + +#[pin_project(UnsafeUnpin)] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +impl Unpin for Baz {} + +fn main() {} diff --git a/tests/ui/unsafe_unpin/conflict-unpin.stderr b/tests/ui/unsafe_unpin/conflict-unpin.stderr new file mode 100644 index 00000000..62de0161 --- /dev/null +++ b/tests/ui/unsafe_unpin/conflict-unpin.stderr @@ -0,0 +1,35 @@ +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: + --> $DIR/conflict-unpin.rs:3:1 + | +3 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +10 | impl Unpin for Foo where T: Unpin {} + | --------------------------------------------- first implementation here + | + = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Foo<_, _>>` in future versions + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: + --> $DIR/conflict-unpin.rs:12:1 + | +12 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +... +19 | impl Unpin for Bar {} + | ------------------------------ first implementation here + | + = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Bar<_, _>>` in future versions + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: + --> $DIR/conflict-unpin.rs:21:1 + | +21 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +... +28 | impl Unpin for Baz {} + | -------------------------------------------- first implementation here + | + = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Baz<_, _>>` in future versions + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)