Skip to content

Commit

Permalink
Move automatic generation of Unpin implementation to proc-macro-derive
Browse files Browse the repository at this point in the history
To generate the correct `Unpin` implementation, we need to collect the
types of the pinned fields. However, since proc-macro-attribute is
applied before cfg, we cannot be collecting field types at this timing.
So instead of generating the `Unpin` implementation here, we need to
delegate automatic generation of the `Unpin` implementation to
proc-macro-derive.
  • Loading branch information
taiki-e committed Sep 7, 2019
1 parent 69f8014 commit a99ad13
Show file tree
Hide file tree
Showing 25 changed files with 668 additions and 260 deletions.
9 changes: 7 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:
parameters:
toolchain: nightly
name: nightly
cross: true

- job: compiletest
pool:
Expand All @@ -49,7 +50,9 @@ jobs:
toolchain: nightly
- script: |
cargo clean
RUSTFLAGS='--cfg compiletest --cfg pin_project_show_unpin_struct' cargo test -p pin-project --all-features --test compiletest
cargo test -p pin-project --all-features --test compiletest
env:
RUSTFLAGS: -Dwarnings --cfg compiletest --cfg pin_project_show_unpin_struct
displayName: compiletest
- job: clippy
Expand Down Expand Up @@ -122,5 +125,7 @@ jobs:
parameters:
toolchain: nightly
- script: |
RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps --all --all-features
cargo doc --no-deps --all --all-features
env:
RUSTDOCFLAGS: -Dwarnings
displayName: cargo doc
12 changes: 10 additions & 2 deletions ci/azure-test.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
parameters:
vmImage: ubuntu-16.04
cmd: test

jobs:
- job: ${{ parameters.name }}
strategy:
matrix:
Linux:
vmImage: ubuntu-16.04
${{ if parameters.cross }}:
MacOS:
vmImage: macOS-10.13
Windows:
vmImage: vs2017-win2016
pool:
vmImage: ${{ parameters.vmImage }}
vmImage: $(vmImage)

steps:
- template: azure-install-rust.yml
Expand Down
5 changes: 5 additions & 0 deletions compiletest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

# A script to run compile tests with the same condition that pin-project executes with CI.

rm -rf target/debug/deps/libpin_project* && RUSTFLAGS='--cfg compiletest --cfg pin_project_show_unpin_struct' cargo test -p pin-project --all-features --test compiletest
16 changes: 13 additions & 3 deletions pin-project-internal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ use syn::parse::Nothing;
/// [`pinned_drop`]: ./attr.pinned_drop.html
#[proc_macro_attribute]
pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
pin_project::attribute(args.into(), input.into()).into()
let input = syn::parse_macro_input!(input);
pin_project::attribute(args.into(), input).into()
}

// TODO: Move this doc into pin-project crate when https://github.com/rust-lang/rust/pull/62855 merged.
Expand Down Expand Up @@ -372,7 +373,8 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
let _: Nothing = syn::parse_macro_input!(args);
pinned_drop::attribute(input.into()).into()
let input = syn::parse_macro_input!(input);
pinned_drop::attribute(&input).into()
}

// TODO: Move this doc into pin-project crate when https://github.com/rust-lang/rust/pull/62855 merged.
Expand Down Expand Up @@ -493,7 +495,15 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn project(args: TokenStream, input: TokenStream) -> TokenStream {
let _: Nothing = syn::parse_macro_input!(args);
project::attribute(input.into()).into()
let input = syn::parse_macro_input!(input);
project::attribute(input).into()
}

#[doc(hidden)]
#[proc_macro_derive(__PinProjectAutoImplUnpin, attributes(pin))]
pub fn derive_unpin(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input);
pin_project::derive(input).into()
}

#[cfg(feature = "renamed")]
Expand Down
85 changes: 57 additions & 28 deletions pin-project-internal/src/pin_project/enums.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{parse::Nothing, Field, Fields, FieldsNamed, FieldsUnnamed, ItemEnum, Result, Variant};
use syn::{Field, Fields, FieldsNamed, FieldsUnnamed, ItemEnum, Result, Variant};

use crate::utils::VecExt;
use crate::utils::collect_cfg;

use super::{Context, PIN};
use super::Context;

pub(super) fn parse(cx: &mut Context, mut item: ItemEnum) -> Result<TokenStream> {
if item.variants.is_empty() {
return Err(error!(
item,
"#[pin_project] attribute may not be used on enums without variants"
return Err(syn::Error::new(
item.brace_token.span,
"#[pin_project] attribute may not be used on enums without variants",
));
}
let has_field = item.variants.iter().try_fold(false, |has_field, v| {
Expand Down Expand Up @@ -62,17 +62,22 @@ pub(super) fn parse(cx: &mut Context, mut item: ItemEnum) -> Result<TokenStream>
fn variants(cx: &mut Context, item: &mut ItemEnum) -> Result<(Vec<TokenStream>, Vec<TokenStream>)> {
let mut proj_variants = Vec::with_capacity(item.variants.len());
let mut proj_arms = Vec::with_capacity(item.variants.len());
for Variant { fields, ident, .. } in &mut item.variants {
for Variant { attrs, fields, ident, .. } in &mut item.variants {
let (proj_pat, proj_body, proj_fields) = match fields {
Fields::Unnamed(fields) => unnamed(cx, fields)?,
Fields::Named(fields) => named(cx, fields)?,
Fields::Unit => (TokenStream::new(), TokenStream::new(), TokenStream::new()),
};
let cfg = collect_cfg(attrs);
let Context { orig_ident, proj_ident, .. } = &cx;
let proj_arm = quote!(#orig_ident::#ident #proj_pat => #proj_ident::#ident #proj_body );
let proj_variant = quote!(#ident #proj_fields);
proj_arms.push(proj_arm);
proj_variants.push(proj_variant);
proj_variants.push(quote! {
#(#cfg)* #ident #proj_fields
});
proj_arms.push(quote! {
#(#cfg)* #orig_ident::#ident #proj_pat => {
#proj_ident::#ident #proj_body
}
});
}

Ok((proj_variants, proj_arms))
Expand All @@ -86,18 +91,27 @@ fn named(
let mut proj_body = Vec::with_capacity(fields.len());
let mut proj_fields = Vec::with_capacity(fields.len());
for Field { attrs, ident, ty, .. } in fields {
if let Some(attr) = attrs.find_remove(PIN) {
let _: Nothing = syn::parse2(attr.tokens)?;
cx.push_unpin_bounds(ty.clone());
let cfg = collect_cfg(attrs);
if cx.find_pin_attr(attrs)? {
let lifetime = &cx.lifetime;
proj_fields.push(quote!(#ident: ::core::pin::Pin<&#lifetime mut #ty>));
proj_body.push(quote!(#ident: ::core::pin::Pin::new_unchecked(#ident)));
proj_fields.push(quote! {
#(#cfg)* #ident: ::core::pin::Pin<&#lifetime mut #ty>
});
proj_body.push(quote! {
#(#cfg)* #ident: ::core::pin::Pin::new_unchecked(#ident)
});
} else {
let lifetime = &cx.lifetime;
proj_fields.push(quote!(#ident: &#lifetime mut #ty));
proj_body.push(quote!(#ident));
proj_fields.push(quote! {
#(#cfg)* #ident: &#lifetime mut #ty
});
proj_body.push(quote! {
#(#cfg)* #ident: #ident
});
}
proj_pat.push(ident);
proj_pat.push(quote! {
#(#cfg)* #ident
});
}

let proj_pat = quote!({ #(#proj_pat),* });
Expand All @@ -114,19 +128,34 @@ fn unnamed(
let mut proj_body = Vec::with_capacity(fields.len());
let mut proj_fields = Vec::with_capacity(fields.len());
for (i, Field { attrs, ty, .. }) in fields.iter_mut().enumerate() {
let x = format_ident!("_x{}", i);
if let Some(attr) = attrs.find_remove(PIN) {
let _: Nothing = syn::parse2(attr.tokens)?;
cx.push_unpin_bounds(ty.clone());
let id = format_ident!("_x{}", i);
let cfg = collect_cfg(attrs);
if !cfg.is_empty() {
return Err(error!(
cfg.first(),
"`cfg` attributes on the field of tuple variants are not supported"
));
}
if cx.find_pin_attr(attrs)? {
let lifetime = &cx.lifetime;
proj_fields.push(quote!(::core::pin::Pin<&#lifetime mut #ty>));
proj_body.push(quote!(::core::pin::Pin::new_unchecked(#x)));
proj_fields.push(quote! {
::core::pin::Pin<&#lifetime mut #ty>
});
proj_body.push(quote! {
::core::pin::Pin::new_unchecked(#id)
});
} else {
let lifetime = &cx.lifetime;
proj_fields.push(quote!(&#lifetime mut #ty));
proj_body.push(quote!(#x));
proj_fields.push(quote! {
&#lifetime mut #ty
});
proj_body.push(quote! {
#id
});
}
proj_pat.push(x);
proj_pat.push(quote! {
#id
});
}

let proj_pat = quote!((#(#proj_pat),*));
Expand Down
Loading

0 comments on commit a99ad13

Please sign in to comment.