Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a dummy lifetime to UnpinStruct #111

Merged
merged 1 commit into from
Sep 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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