From 479b9a45ea9816cec31156504dc7c0d88f24091f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 25 Sep 2019 15:58:28 +0900 Subject: [PATCH] Add a dummy lifetime to Wrapper If we know that `Self` doesn't implement `Unpin` and `Self` doesn't contain any generics or lifetimes, we get an error `E0277`. To avoid this, add a dummy lifetime to the `Wrapper`. This allows `Wrapper` to always compile regardless of whether `Self` implements `UnsafeUnpin`. As the `Wrapper` implements `UnsafeUnpin` regardless of a lifetime, this should not change the actual bounds of the `Unpin` implementation. --- README.md | 2 + examples/unsafe_unpin-expanded.rs | 5 +- pin-project-internal/src/lib.rs | 10 +-- .../src/pin_project/attribute.rs | 12 +-- src/lib.rs | 6 +- ...-impl.rs => not-implement-unsafe-unpin.rs} | 2 - ...derr => not-implement-unsafe-unpin.stderr} | 8 +- tests/ui/unsafe_unpin/proper_unpin.rs | 37 ++++++++-- tests/ui/unsafe_unpin/proper_unpin.stderr | 73 +++++++++++++++++-- .../trivial_bounds-feature-gate.rs | 6 +- .../trivial_bounds-feature-gate.stderr | 10 +-- tests/unsafe_unpin.rs | 52 ++++++++++--- 12 files changed, 170 insertions(+), 53 deletions(-) rename tests/ui/unsafe_unpin/{forget-unsafe-unpin-impl.rs => not-implement-unsafe-unpin.rs} (95%) rename tests/ui/unsafe_unpin/{forget-unsafe-unpin-impl.stderr => not-implement-unsafe-unpin.stderr} (71%) diff --git a/README.md b/README.md index ef95b2b5..0a2bd3a2 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ A crate for safe and ergonomic pin-projection. [Documentation][docs-url] +[Examples](examples/README.md) + ## Usage Add this to your `Cargo.toml`: diff --git a/examples/unsafe_unpin-expanded.rs b/examples/unsafe_unpin-expanded.rs index ac09dad3..b7c5975f 100644 --- a/examples/unsafe_unpin-expanded.rs +++ b/examples/unsafe_unpin-expanded.rs @@ -62,8 +62,9 @@ impl Foo { unsafe impl UnsafeUnpin for Foo {} -impl ::core::marker::Unpin for Foo where - ::pin_project::__private::Wrapper: ::pin_project::UnsafeUnpin +#[allow(single_use_lifetimes)] +impl<'_pin, T, U> ::core::marker::Unpin for Foo where + ::pin_project::__private::Wrapper<'_pin, Self>: ::pin_project::UnsafeUnpin { } diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index 7a682a0a..770e6a94 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -136,6 +136,10 @@ use utils::{Immutable, Mutable}; /// } /// ``` /// +/// 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 implement [`Unpin`] manually, you must use the `UnsafeUnpin` /// argument to `#[pin_project]`. /// @@ -167,11 +171,7 @@ use utils::{Immutable, Mutable}; /// are being used. If you implement [`UnsafeUnpin`], you must ensure that it is /// only implemented when all pin-projected fields implement [`Unpin`]. /// -/// Note that borrowing the field where `#[pin]` attribute is used multiple -/// times requires using [`.as_mut()`][`Pin::as_mut`] to avoid -/// consuming the `Pin`. -/// -/// See also [`UnsafeUnpin`] trait. +/// See [`UnsafeUnpin`] trait for more details. /// /// ### `#[pinned_drop]` /// diff --git a/pin-project-internal/src/pin_project/attribute.rs b/pin-project-internal/src/pin_project/attribute.rs index 4cc3840a..39f1d055 100644 --- a/pin-project-internal/src/pin_project/attribute.rs +++ b/pin-project-internal/src/pin_project/attribute.rs @@ -184,19 +184,21 @@ impl Context { return TokenStream::new(); }; - let mut generics = self.generics.clone(); - let orig_ident = &self.orig_ident; + let mut proj_generics = self.proj_generics(); + let Self { orig_ident, lifetime, .. } = self; - generics.make_where_clause().predicates.push( + proj_generics.make_where_clause().predicates.push( syn::parse2(quote_spanned! { unsafe_unpin => - ::pin_project::__private::Wrapper: ::pin_project::UnsafeUnpin + ::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin }) .unwrap(), ); - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); + let ty_generics = self.generics.split_for_impl().1; quote! { + #[allow(single_use_lifetimes)] impl #impl_generics ::core::marker::Unpin for #orig_ident #ty_generics #where_clause {} } } diff --git a/src/lib.rs b/src/lib.rs index d3e9d77c..23c59ea1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,7 +124,7 @@ pub unsafe trait UnsafeUnpin {} #[doc(hidden)] pub mod __private { use super::UnsafeUnpin; - use core::pin::Pin; + use core::{marker::PhantomData, pin::Pin}; #[doc(hidden)] pub use pin_project_internal::__PinProjectAutoImplUnpin; @@ -195,8 +195,8 @@ pub mod __private { // 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 struct Wrapper<'a, T>(T, PhantomData<&'a ()>); #[allow(unsafe_code)] - unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {} + unsafe impl UnsafeUnpin for Wrapper<'_, T> where T: UnsafeUnpin {} } diff --git a/tests/ui/unsafe_unpin/forget-unsafe-unpin-impl.rs b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs similarity index 95% rename from tests/ui/unsafe_unpin/forget-unsafe-unpin-impl.rs rename to tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs index f8480b14..c94d365f 100644 --- a/tests/ui/unsafe_unpin/forget-unsafe-unpin-impl.rs +++ b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs @@ -1,7 +1,5 @@ // compile-fail -// FIXME? - use pin_project::pin_project; #[pin_project(UnsafeUnpin)] diff --git a/tests/ui/unsafe_unpin/forget-unsafe-unpin-impl.stderr b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr similarity index 71% rename from tests/ui/unsafe_unpin/forget-unsafe-unpin-impl.stderr rename to tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr index 4aff52d4..1fc0f613 100644 --- a/tests/ui/unsafe_unpin/forget-unsafe-unpin-impl.stderr +++ b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr @@ -1,13 +1,13 @@ error[E0277]: the trait bound `Foo<(), ()>: pin_project::UnsafeUnpin` is not satisfied - --> $DIR/forget-unsafe-unpin-impl.rs:17:16 + --> $DIR/not-implement-unsafe-unpin.rs:15:16 | -14 | fn is_unpin() {} +12 | fn is_unpin() {} | -------- ----- required by this bound in `is_unpin` ... -17 | is_unpin::>(); //~ ERROR E0277 +15 | is_unpin::>(); //~ ERROR E0277 | ^^^^^^^^^^^ the trait `pin_project::UnsafeUnpin` is not implemented for `Foo<(), ()>` | - = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper>` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Foo<(), ()>>` = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), ()>` error: aborting due to previous error diff --git a/tests/ui/unsafe_unpin/proper_unpin.rs b/tests/ui/unsafe_unpin/proper_unpin.rs index 0e57a27e..e018dbbf 100644 --- a/tests/ui/unsafe_unpin/proper_unpin.rs +++ b/tests/ui/unsafe_unpin/proper_unpin.rs @@ -3,19 +3,42 @@ use pin_project::{pin_project, UnsafeUnpin}; use std::marker::PhantomPinned; +fn is_unpin() {} + #[pin_project(UnsafeUnpin)] -struct Foo { +pub struct Blah { + field1: U, #[pin] - inner: T, - other: U, + field2: T, } -unsafe impl UnsafeUnpin for Foo {} +#[allow(unsafe_code)] +unsafe impl UnsafeUnpin for Blah {} -fn is_unpin() {} +#[pin_project(UnsafeUnpin)] +pub struct NotImplementUnsafUnpin { + #[pin] + field1: PhantomPinned, +} + +#[pin_project(UnsafeUnpin)] +pub struct OverlappingLifetimeNames<'_pin, T, U> { + #[pin] + field1: U, + #[pin] + field2: Option, + field3: &'_pin (), +} + +#[allow(unsafe_code)] +unsafe impl UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {} -fn foo_is_unpin() { - is_unpin::>(); //~ ERROR E0277 +fn unsafe_unpin() { + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + is_unpin::(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 + is_unpin::>(); //~ ERROR E0277 } fn main() {} diff --git a/tests/ui/unsafe_unpin/proper_unpin.stderr b/tests/ui/unsafe_unpin/proper_unpin.stderr index 63d09d09..97059199 100644 --- a/tests/ui/unsafe_unpin/proper_unpin.stderr +++ b/tests/ui/unsafe_unpin/proper_unpin.stderr @@ -1,18 +1,75 @@ error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied - --> $DIR/proper_unpin.rs:18:5 + --> $DIR/proper_unpin.rs:37:5 | -15 | fn is_unpin() {} +6 | fn is_unpin() {} | -------- ----- required by this bound in `is_unpin` ... -18 | is_unpin::>(); //~ ERROR E0277 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` +37 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` | = help: the following implementations were found: - = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Foo` - = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper>` - = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah` -error: aborting due to previous error +error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied + --> $DIR/proper_unpin.rs:38:5 + | +6 | fn is_unpin() {} + | -------- ----- required by this bound in `is_unpin` +... +38 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = help: the following implementations were found: + + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah` + +error[E0277]: the trait bound `NotImplementUnsafUnpin: pin_project::UnsafeUnpin` is not satisfied + --> $DIR/proper_unpin.rs:39:16 + | +6 | fn is_unpin() {} + | -------- ----- required by this bound in `is_unpin` +... +39 | is_unpin::(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `pin_project::UnsafeUnpin` is not implemented for `NotImplementUnsafUnpin` + | + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, NotImplementUnsafUnpin>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `NotImplementUnsafUnpin` + +error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied + --> $DIR/proper_unpin.rs:40:5 + | +6 | fn is_unpin() {} + | -------- ----- required by this bound in `is_unpin` +... +40 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = help: the following implementations were found: + + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>` + +error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied + --> $DIR/proper_unpin.rs:41:5 + | +6 | fn is_unpin() {} + | -------- ----- required by this bound in `is_unpin` +... +41 | is_unpin::>(); //~ ERROR E0277 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` + | + = help: the following implementations were found: + + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>` + = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>>` + = note: required because of the requirements on the impl of `std::marker::Unpin` for `OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>` + +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/unstable-features/trivial_bounds-feature-gate.rs b/tests/ui/unstable-features/trivial_bounds-feature-gate.rs index 8a269c42..789ed087 100644 --- a/tests/ui/unstable-features/trivial_bounds-feature-gate.rs +++ b/tests/ui/unstable-features/trivial_bounds-feature-gate.rs @@ -7,7 +7,11 @@ use std::marker::PhantomPinned; struct Inner(PhantomPinned); -#[pin_project] //~ ERROR E0277 +// As a workaround, you need to use `UnsafeUnpin`. +#[pin_project(UnsafeUnpin)] // Ok struct Foo(#[pin] Inner); +#[pin_project] //~ ERROR E0277 +struct Bar(#[pin] Inner); + fn main() {} diff --git a/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr b/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr index 9fe3e57b..8973216d 100644 --- a/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr +++ b/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr @@ -1,13 +1,13 @@ -error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructFoo` - --> $DIR/trivial_bounds-feature-gate.rs:10:1 +error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructBar` + --> $DIR/trivial_bounds-feature-gate.rs:14:1 | -10 | #[pin_project] //~ ERROR E0277 - | ^^^^^^^^^^^^^^ within `UnpinStructFoo`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` +14 | #[pin_project] //~ ERROR E0277 + | ^^^^^^^^^^^^^^ within `UnpinStructBar`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned` | = help: the following implementations were found: = note: required because it appears within the type `Inner` - = note: required because it appears within the type `UnpinStructFoo` + = note: required because it appears within the type `UnpinStructBar` = help: see issue #48214 = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable diff --git a/tests/unsafe_unpin.rs b/tests/unsafe_unpin.rs index 53b2c74f..51cd51f9 100644 --- a/tests/unsafe_unpin.rs +++ b/tests/unsafe_unpin.rs @@ -3,21 +3,51 @@ #![allow(dead_code)] use pin_project::{pin_project, UnsafeUnpin}; -use std::marker::PhantomPinned; +use std::{marker::PhantomPinned, pin::Pin}; fn is_unpin() {} -#[test] -fn unsafe_unpin() { - #[pin_project(UnsafeUnpin)] - pub struct Blah { - field1: U, - #[pin] - field2: Option, - } +#[pin_project(UnsafeUnpin)] +pub struct Blah { + field1: U, + #[pin] + field2: T, +} + +#[allow(unsafe_code)] +unsafe impl UnsafeUnpin for Blah {} - #[allow(unsafe_code)] - unsafe impl UnsafeUnpin for Blah {} +#[pin_project(UnsafeUnpin)] +pub struct NotImplementUnsafUnpin { + #[pin] + field1: PhantomPinned, +} + +#[pin_project(UnsafeUnpin)] +pub struct OverlappingLifetimeNames<'_pin, T, U> { + #[pin] + field1: T, + field2: U, + field3: &'_pin (), +} +#[allow(unsafe_code)] +unsafe impl UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {} + +#[test] +fn unsafe_unpin() { is_unpin::>(); + is_unpin::>(); +} + +#[test] +fn test() { + let mut x = OverlappingLifetimeNames { field1: 0, field2: 1, field3: &() }; + let x = Pin::new(&mut x); + let y = x.as_ref().project_ref(); + let _: Pin<&u8> = y.field1; + let _: &u8 = y.field2; + let y = x.project(); + let _: Pin<&mut u8> = y.field1; + let _: &mut u8 = y.field2; }