Skip to content

Commit

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

Basically the same as #107.

This fixes #102 completely.

cc #108


Co-authored-by: Taiki Endo <te316e89@gmail.com>
  • Loading branch information
bors[bot] and taiki-e authored Sep 26, 2019
2 parents 0cf5990 + 9d0e875 commit 2b5bbb4
Show file tree
Hide file tree
Showing 17 changed files with 158 additions and 96 deletions.
15 changes: 7 additions & 8 deletions examples/enum-default-expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use pin_project::pin_project;

enum Enum<T, U> {
Pinned(T),
Pinned(/* #[pin] */ T),
Unpinned(U),
}

Expand Down Expand Up @@ -63,17 +63,16 @@ impl<T, U> Enum<T, U> {
// for details.
#[allow(non_snake_case)]
fn __unpin_scope_Enum() {
struct AlwaysUnpinEnum<T: ?Sized> {
val: ::core::marker::PhantomData<T>,
}
impl<T: ?Sized> ::core::marker::Unpin for AlwaysUnpinEnum<T> {}
#[allow(dead_code)]
#[doc(hidden)]
struct __UnpinStructEnum<T, U> {
__pin_project_use_generics: AlwaysUnpinEnum<(T, U)>,
struct __UnpinStructEnum<'_pin, T, U> {
__pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'_pin, (T, U)>,
__field0: T,
}
impl<T, U> ::core::marker::Unpin for Enum<T, U> where __UnpinStructEnum<T, U>: ::core::marker::Unpin {}
impl<'_pin, T, U> ::core::marker::Unpin for Enum<T, U> where
__UnpinStructEnum<'_pin, T, U>: ::core::marker::Unpin
{
}
}

// Ensure that enum does not implement `Drop`.
Expand Down
15 changes: 7 additions & 8 deletions examples/pinned_drop-expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use std::pin::Pin;

pub struct Foo<'a, T> {
was_dropped: &'a mut bool,
// #[pin]
field: T,
}

Expand Down Expand Up @@ -109,19 +110,17 @@ impl<T> ::pin_project::__private::PinnedDrop for Foo<'_, T> {
// for details.
#[allow(non_snake_case)]
fn __unpin_scope_Foo() {
struct AlwaysUnpinFoo<T: ?Sized> {
val: ::core::marker::PhantomData<T>,
}
impl<T: ?Sized> ::core::marker::Unpin for AlwaysUnpinFoo<T> {}
#[allow(dead_code)]
#[doc(hidden)]
pub struct __UnpinStructFoo<'a, T> {
__pin_project_use_generics: AlwaysUnpinFoo<(T)>,
pub struct __UnpinStructFoo<'_pin, 'a, T> {
__pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'_pin, (T)>,
__field0: T,
__lifetime0: &'a (),
}
impl<'a, T> ::core::marker::Unpin for Foo<'a, T> where __UnpinStructFoo<'a, T>: ::core::marker::Unpin
{}
impl<'_pin, 'a, T> ::core::marker::Unpin for Foo<'a, T> where
__UnpinStructFoo<'_pin, 'a, T>: ::core::marker::Unpin
{
}
}

// Ensure that it's impossible to use pin projections on a #[repr(packed)] struct.
Expand Down
13 changes: 5 additions & 8 deletions examples/struct-default-expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use pin_project::pin_project;

struct Struct<T, U> {
// #[pin]
pinned: T,
unpinned: U,
}
Expand Down Expand Up @@ -86,18 +87,14 @@ impl<T, U> Struct<T, U> {
// See also https://github.com/taiki-e/pin-project/pull/53.
#[allow(non_snake_case)]
fn __unpin_scope_Struct() {
struct AlwaysUnpinStruct<T: ?Sized> {
val: ::core::marker::PhantomData<T>,
}
impl<T: ?Sized> ::core::marker::Unpin for AlwaysUnpinStruct<T> {}
#[allow(dead_code)]
#[doc(hidden)]
struct __UnpinStructStruct<T, U> {
__pin_project_use_generics: AlwaysUnpinStruct<(T, U)>,
struct __UnpinStructStruct<'_pin, T, U> {
__pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<'_pin, (T, U)>,
__field0: T,
}
impl<T, U> ::core::marker::Unpin for Struct<T, U> where
__UnpinStructStruct<T, U>: ::core::marker::Unpin
impl<'_pin, T, U> ::core::marker::Unpin for Struct<T, U> where
__UnpinStructStruct<'_pin, T, U>: ::core::marker::Unpin
{
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/unsafe_unpin-expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use pin_project::{pin_project, UnsafeUnpin};

pub struct Foo<T, U> {
// #[pin]
pinned: T,
unpinned: U,
}
Expand Down
43 changes: 25 additions & 18 deletions pin-project-internal/src/pin_project/derive.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use proc_macro2::TokenStream;
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{parse::Nothing, *};

use crate::utils::{Variants, VecExt};
use crate::utils::{self, proj_lifetime_name, Variants, VecExt, DEFAULT_LIFETIME_NAME};

use super::PIN;

Expand Down Expand Up @@ -34,13 +34,27 @@ struct DeriveContext {
/// Generics of the original type.
generics: Generics,

/// Lifetime on the generated projected type.
lifetime: Lifetime,

/// Types of the pinned fields.
pinned_fields: Vec<Type>,
}

impl DeriveContext {
fn new(ident: Ident, vis: Visibility, generics: Generics) -> Self {
Self { ident, vis, generics, pinned_fields: Vec::new() }
let mut lifetime_name = String::from(DEFAULT_LIFETIME_NAME);
proj_lifetime_name(&mut lifetime_name, &generics.params);
let lifetime = Lifetime::new(&lifetime_name, Span::call_site());

Self { ident, vis, generics, lifetime, pinned_fields: Vec::new() }
}

/// Creates the generics of projected type.
fn proj_generics(&self) -> Generics {
let mut generics = self.generics.clone();
utils::proj_generics(&mut generics, self.lifetime.clone());
generics
}

fn visit_variants(&mut self, variants: &Variants) {
Expand Down Expand Up @@ -68,8 +82,6 @@ impl DeriveContext {
fn make_unpin_impl(&mut self) -> TokenStream {
let where_clause = self.generics.make_where_clause().clone();
let orig_ident = &self.ident;
let (impl_generics, ty_generics, _) = self.generics.split_for_impl();
let type_params: Vec<_> = self.generics.type_params().map(|t| t.ident.clone()).collect();

let make_span = || {
#[cfg(proc_macro_def_site)]
Expand All @@ -87,7 +99,6 @@ impl DeriveContext {
} else {
format_ident!("__UnpinStruct{}", orig_ident)
};
let always_unpin_ident = format_ident!("AlwaysUnpin{}", orig_ident, span = make_span());

// Generate a field in our new struct for every
// pinned field in the original type.
Expand Down Expand Up @@ -138,25 +149,21 @@ impl DeriveContext {

let scope_ident = format_ident!("__unpin_scope_{}", orig_ident);

let vis = &self.vis;
let full_generics = &self.generics;
let Self { vis, lifetime, .. } = &self;
let type_params: Vec<_> = self.generics.type_params().map(|t| t.ident.clone()).collect();
let proj_generics = self.proj_generics();
let (impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl();
let ty_generics = self.generics.split_for_impl().1;
let mut full_where_clause = where_clause.clone();

let unpin_clause: WherePredicate = syn::parse_quote! {
#struct_ident #ty_generics: ::core::marker::Unpin
#struct_ident #proj_ty_generics: ::core::marker::Unpin
};

full_where_clause.predicates.push(unpin_clause);

let attrs = if cfg!(proc_macro_def_site) { quote!() } else { quote!(#[doc(hidden)]) };

let inner_data = quote! {
struct #always_unpin_ident <T: ?Sized> {
val: ::core::marker::PhantomData<T>
}

impl<T: ?Sized> ::core::marker::Unpin for #always_unpin_ident <T> {}

// This needs to have the same visibility as the original type,
// due to the limitations of the 'public in private' error.
//
Expand All @@ -171,8 +178,8 @@ impl DeriveContext {
// See also https://github.com/taiki-e/pin-project/pull/53.
#[allow(dead_code)]
#attrs
#vis struct #struct_ident #full_generics #where_clause {
__pin_project_use_generics: #always_unpin_ident <(#(#type_params),*)>,
#vis struct #struct_ident #proj_generics #where_clause {
__pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<#lifetime, (#(#type_params),*)>,

#(#fields,)*
#(#lifetime_fields,)*
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,12 @@ pub mod __private {

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

// This is an internal helper struct used by `pin-project-internal`.
//
// See https://github.com/taiki-e/pin-project/pull/53 for more details.
#[doc(hidden)]
pub struct AlwaysUnpin<'a, T: ?Sized>(PhantomData<T>, PhantomData<&'a ()>);

impl<T: ?Sized> Unpin for AlwaysUnpin<'_, T> {}
}
11 changes: 10 additions & 1 deletion tests/pin_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![warn(rust_2018_idioms, single_use_lifetimes)]
#![allow(dead_code)]

use core::pin::Pin;
use core::{marker::PhantomPinned, pin::Pin};
use pin_project::{pin_project, pinned_drop, UnsafeUnpin};

#[test]
Expand Down Expand Up @@ -348,3 +348,12 @@ fn visibility() {
let y = x.project();
let _: &mut u8 = y.b;
}

#[test]
fn trivial_bounds() {
#[pin_project]
pub struct NoGenerics {
#[pin]
field: PhantomPinned,
}
}
3 changes: 2 additions & 1 deletion tests/ui/cfg/proper_unpin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ struct Bar<T> {
fn is_unpin<T: Unpin>() {}

fn baz<T, U>() {
is_unpin::<Foo<PhantomPinned>>(); // Pass
is_unpin::<Foo<PhantomPinned>>(); // Ok
is_unpin::<Bar<()>>(); // Ok
is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
}

Expand Down
10 changes: 5 additions & 5 deletions tests/ui/cfg/proper_unpin.stderr
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructBar<std::marker::PhantomPinned>`
--> $DIR/proper_unpin.rs:28:5
error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructBar<'_, std::marker::PhantomPinned>`
--> $DIR/proper_unpin.rs:29:5
|
24 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
28 | is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `UnpinStructBar<std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
29 | is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `UnpinStructBar<'_, std::marker::PhantomPinned>`, 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 `UnpinStructBar<std::marker::PhantomPinned>`
= note: required because it appears within the type `UnpinStructBar<'_, std::marker::PhantomPinned>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<std::marker::PhantomPinned>`

error: aborting due to previous error
Expand Down
25 changes: 22 additions & 3 deletions tests/ui/pin_project/proper_unpin.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// compile-fail

use pin_project::pin_project;
use std::pin::Pin;
use std::marker::PhantomPinned;

struct Inner<T> {
val: T,
Expand All @@ -14,10 +14,29 @@ struct Foo<T, U> {
other: U,
}

#[pin_project]
pub struct TrivialBounds {
#[pin]
field1: PhantomPinned,
}

#[pin_project]
struct Bar<'a, T, U> {
#[pin]
inner: &'a mut Inner<T>,
other: U,
}

fn is_unpin<T: Unpin>() {}

fn bar<T, U>() {
is_unpin::<Foo<T, U>>(); //~ ERROR E0277
fn assert_unpin() {
is_unpin::<Foo<PhantomPinned, ()>>(); //~ ERROR E0277
is_unpin::<Foo<(), PhantomPinned>>(); // Ok
is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277

is_unpin::<TrivialBounds>(); //~ ERROR E0277

is_unpin::<Bar<'_, PhantomPinned, PhantomPinned>>(); //~ Ok
}

fn main() {}
50 changes: 40 additions & 10 deletions tests/ui/pin_project/proper_unpin.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
error[E0277]: the trait bound `T: std::marker::Unpin` is not satisfied in `UnpinStructFoo<T, U>`
--> $DIR/proper_unpin.rs:20:5
error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructFoo<'_, std::marker::PhantomPinned, ()>`
--> $DIR/proper_unpin.rs:33:5
|
17 | fn is_unpin<T: Unpin>() {}
30 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
20 | is_unpin::<Foo<T, U>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^ within `UnpinStructFoo<T, U>`, the trait `std::marker::Unpin` is not implemented for `T`
33 | is_unpin::<Foo<PhantomPinned, ()>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `UnpinStructFoo<'_, std::marker::PhantomPinned, ()>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
|
= help: consider adding a `where T: std::marker::Unpin` bound
= note: required because it appears within the type `Inner<T>`
= note: required because it appears within the type `UnpinStructFoo<T, U>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<T, U>`
= help: the following implementations were found:
<std::marker::PhantomPinned as std::marker::Unpin>
= note: required because it appears within the type `Inner<std::marker::PhantomPinned>`
= note: required because it appears within the type `UnpinStructFoo<'_, std::marker::PhantomPinned, ()>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned, ()>`

error: aborting due to previous error
error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructFoo<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>`
--> $DIR/proper_unpin.rs:35:5
|
30 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
35 | is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `UnpinStructFoo<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>`, 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<std::marker::PhantomPinned>`
= note: required because it appears within the type `UnpinStructFoo<'_, 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>`

error[E0277]: the trait bound `std::marker::PhantomPinned: std::marker::Unpin` is not satisfied in `UnpinStructTrivialBounds<'_>`
--> $DIR/proper_unpin.rs:37:5
|
30 | fn is_unpin<T: Unpin>() {}
| -------- ----- required by this bound in `is_unpin`
...
37 | is_unpin::<TrivialBounds>(); //~ ERROR E0277
| ^^^^^^^^^^^^^^^^^^^^^^^^^ within `UnpinStructTrivialBounds<'_>`, 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 `UnpinStructTrivialBounds<'_>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
3 changes: 3 additions & 0 deletions tests/ui/unsafe_unpin/proper_unpin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for OverlappingLifetimeNames<'_, T,

fn unsafe_unpin() {
is_unpin::<Blah<PhantomPinned, ()>>(); //~ ERROR E0277
is_unpin::<Blah<(), PhantomPinned>>(); // Ok
is_unpin::<Blah<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277

is_unpin::<NotImplementUnsafUnpin>(); //~ ERROR E0277

is_unpin::<OverlappingLifetimeNames<'_, PhantomPinned, ()>>(); //~ ERROR E0277
is_unpin::<OverlappingLifetimeNames<'_, (), PhantomPinned>>(); //~ ERROR E0277
}
Expand Down
Loading

0 comments on commit 2b5bbb4

Please sign in to comment.