Skip to content

Commit

Permalink
Merge #107
Browse files Browse the repository at this point in the history
107: Add a dummy lifetime to Wrapper r=taiki-e a=taiki-e

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<Self>` 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.

This allows workaround of #102 that "using `pin_project(UnsafeUnpin)`
and not providing a manual `UnsafeUnpin` implementation" to work.

Closes #102 

cc @Aaron1011 @ogoffart

Co-authored-by: Taiki Endo <te316e89@gmail.com>
  • Loading branch information
bors[bot] and taiki-e authored Sep 25, 2019
2 parents dfcf405 + 479b9a4 commit 816e3f0
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 53 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
Expand Down
5 changes: 3 additions & 2 deletions examples/unsafe_unpin-expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ impl<T, U> Foo<T, U> {

unsafe impl<T: Unpin, U> UnsafeUnpin for Foo<T, U> {}

impl<T, U> ::core::marker::Unpin for Foo<T, U> where
::pin_project::__private::Wrapper<Self>: ::pin_project::UnsafeUnpin
#[allow(single_use_lifetimes)]
impl<'_pin, T, U> ::core::marker::Unpin for Foo<T, U> where
::pin_project::__private::Wrapper<'_pin, Self>: ::pin_project::UnsafeUnpin
{
}

Expand Down
10 changes: 5 additions & 5 deletions pin-project-internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]`.
///
Expand Down Expand Up @@ -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]`
///
Expand Down
12 changes: 7 additions & 5 deletions pin-project-internal/src/pin_project/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self>: ::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 {}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>(T);
pub struct Wrapper<'a, T>(T, PhantomData<&'a ()>);

#[allow(unsafe_code)]
unsafe impl<T> UnsafeUnpin for Wrapper<T> where T: UnsafeUnpin {}
unsafe impl<T> UnsafeUnpin for Wrapper<'_, T> where T: UnsafeUnpin {}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// compile-fail

// FIXME?

use pin_project::pin_project;

#[pin_project(UnsafeUnpin)]
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T: Unpin>() {}
12 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
17 | is_unpin::<Foo<(), ()>>(); //~ ERROR E0277
15 | is_unpin::<Foo<(), ()>>(); //~ 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<Foo<(), ()>>`
= 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
Expand Down
37 changes: 30 additions & 7 deletions tests/ui/unsafe_unpin/proper_unpin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,42 @@
use pin_project::{pin_project, UnsafeUnpin};
use std::marker::PhantomPinned;

fn is_unpin<T: Unpin>() {}

#[pin_project(UnsafeUnpin)]
struct Foo<T, U> {
pub struct Blah<T, U> {
field1: U,
#[pin]
inner: T,
other: U,
field2: T,
}

unsafe impl<T: Unpin, U> UnsafeUnpin for Foo<T, U> {}
#[allow(unsafe_code)]
unsafe impl<T: Unpin, U> UnsafeUnpin for Blah<T, U> {}

fn is_unpin<T: 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<T>,
field3: &'_pin (),
}

#[allow(unsafe_code)]
unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {}

fn foo_is_unpin() {
is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
fn unsafe_unpin() {
is_unpin::<Blah<PhantomPinned, ()>>(); //~ ERROR E0277
is_unpin::<Blah<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
is_unpin::<NotImplementUnsafUnpin>(); //~ ERROR E0277
is_unpin::<OverlappingLifetimeNames<'_, PhantomPinned, ()>>(); //~ ERROR E0277
is_unpin::<OverlappingLifetimeNames<'_, (), PhantomPinned>>(); //~ ERROR E0277
}

fn main() {}
73 changes: 65 additions & 8 deletions tests/ui/unsafe_unpin/proper_unpin.stderr
Original file line number Diff line number Diff line change
@@ -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<T: Unpin>() {}
6 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
18 | is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
37 | is_unpin::<Blah<PhantomPinned, ()>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
|
= help: the following implementations were found:
<std::marker::PhantomPinned as std::marker::Unpin>
= note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Foo<std::marker::PhantomPinned, std::marker::PhantomPinned>`
= note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<Foo<std::marker::PhantomPinned, std::marker::PhantomPinned>>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned, std::marker::PhantomPinned>`
= note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah<std::marker::PhantomPinned, ()>`
= note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah<std::marker::PhantomPinned, ()>>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah<std::marker::PhantomPinned, ()>`

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<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
38 | is_unpin::<Blah<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
|
= help: the following implementations were found:
<std::marker::PhantomPinned as std::marker::Unpin>
= note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah<std::marker::PhantomPinned, std::marker::PhantomPinned>`
= note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah<std::marker::PhantomPinned, std::marker::PhantomPinned>>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah<std::marker::PhantomPinned, std::marker::PhantomPinned>`

error[E0277]: the trait bound `NotImplementUnsafUnpin: pin_project::UnsafeUnpin` is not satisfied
--> $DIR/proper_unpin.rs:39:16
|
6 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
39 | is_unpin::<NotImplementUnsafUnpin>(); //~ 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<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
40 | is_unpin::<OverlappingLifetimeNames<'_, PhantomPinned, ()>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
|
= help: the following implementations were found:
<std::marker::PhantomPinned as std::marker::Unpin>
= 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<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
41 | is_unpin::<OverlappingLifetimeNames<'_, (), PhantomPinned>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
|
= help: the following implementations were found:
<std::marker::PhantomPinned as std::marker::Unpin>
= 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`.
6 changes: 5 additions & 1 deletion tests/ui/unstable-features/trivial_bounds-feature-gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
10 changes: 5 additions & 5 deletions tests/ui/unstable-features/trivial_bounds-feature-gate.stderr
Original file line number Diff line number Diff line change
@@ -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:
<std::marker::PhantomPinned as std::marker::Unpin>
= 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

Expand Down
52 changes: 41 additions & 11 deletions tests/unsafe_unpin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Unpin>() {}

#[test]
fn unsafe_unpin() {
#[pin_project(UnsafeUnpin)]
pub struct Blah<T, U> {
field1: U,
#[pin]
field2: Option<T>,
}
#[pin_project(UnsafeUnpin)]
pub struct Blah<T, U> {
field1: U,
#[pin]
field2: T,
}

#[allow(unsafe_code)]
unsafe impl<T: Unpin, U> UnsafeUnpin for Blah<T, U> {}

#[allow(unsafe_code)]
unsafe impl<T: Unpin, U> UnsafeUnpin for Blah<T, U> {}
#[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<T: Unpin, U> UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {}

#[test]
fn unsafe_unpin() {
is_unpin::<Blah<(), PhantomPinned>>();
is_unpin::<OverlappingLifetimeNames<'_, (), ()>>();
}

#[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;
}

0 comments on commit 816e3f0

Please sign in to comment.