Skip to content

Commit

Permalink
Clean up projected type generation
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Aug 19, 2019
1 parent 696b8ae commit 403d0da
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 79 deletions.
121 changes: 51 additions & 70 deletions pin-project-internal/src/pin_project/enums.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use proc_macro2::{Ident, TokenStream};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{parse::Nothing, Field, Fields, FieldsNamed, FieldsUnnamed, ItemEnum, Result, Variant};

Expand All @@ -23,125 +23,106 @@ pub(super) fn parse(mut cx: Context, mut item: ItemEnum) -> Result<TokenStream>
return Err(error!(item.variants, "cannot be implemented for enums that have no field"));
}

let (proj_item_body, proj_arms) = variants(&mut cx, &mut item)?;
let (proj_variants, proj_arms) = variants(&mut cx, &mut item)?;

let orig_ident = &cx.original;
let proj_ident = &cx.projected;
let lifetime = &cx.lifetime;
let impl_drop = cx.impl_drop(&item.generics);
let Context { original, projected, lifetime, impl_unpin, .. } = cx;
let proj_generics = proj_generics(&item.generics, &lifetime);
let proj_ty_generics = proj_generics.split_for_impl().1;
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

let mut proj_items = quote! {
enum #proj_ident #proj_generics #where_clause #proj_item_body
enum #projected #proj_generics #where_clause { #(#proj_variants,)* }
};
let proj_method = quote! {
impl #impl_generics #orig_ident #ty_generics #where_clause {
fn project<#lifetime>(self: ::core::pin::Pin<&#lifetime mut Self>) -> #proj_ident #proj_ty_generics {
impl #impl_generics #original #ty_generics #where_clause {
fn project<#lifetime>(self: ::core::pin::Pin<&#lifetime mut Self>) -> #projected #proj_ty_generics {
unsafe {
match ::core::pin::Pin::get_unchecked_mut(self) {
#proj_arms
#(#proj_arms,)*
}
}
}
}
};

proj_items.extend(impl_drop.build(orig_ident));
proj_items.extend(cx.impl_unpin.build(orig_ident));
proj_items.extend(impl_drop.build(&original));
proj_items.extend(impl_unpin.build(&original));
proj_items.extend(proj_method);

let mut item = item.into_token_stream();
item.extend(proj_items);
Ok(item)
}

fn variants(
cx: &mut Context,
ItemEnum { variants, .. }: &mut ItemEnum,
) -> Result<(TokenStream, TokenStream)> {
let mut arm_vec = Vec::with_capacity(variants.len());
let mut ty_vec = Vec::with_capacity(variants.len());
for Variant { fields, ident, .. } in variants {
let (proj_arm, proj_ty) = match fields {
Fields::Unnamed(fields) => unnamed(cx, fields, ident)?,
Fields::Named(fields) => named(cx, fields, ident)?,
Fields::Unit => unit(cx, ident),
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 {
let (proj_pat, proj_body, proj_field) = match fields {
Fields::Unnamed(fields) => unnamed(cx, fields)?,
Fields::Named(fields) => named(cx, fields)?,
Fields::Unit => (TokenStream::new(), TokenStream::new(), TokenStream::new()),
};
arm_vec.push(proj_arm);
ty_vec.push(proj_ty);
let Context { original, projected, .. } = &cx;
let proj_arm = quote!(#original::#ident #proj_pat => #projected::#ident #proj_body );
let proj_variant = quote!(#ident #proj_field);
proj_arms.push(proj_arm);
proj_variants.push(proj_variant);
}

let proj_item_body = quote!({ #(#ty_vec,)* });
let proj_arms = quote!(#(#arm_vec,)*);
Ok((proj_item_body, proj_arms))
Ok((proj_variants, proj_arms))
}

fn named(
Context { original, projected, lifetime, impl_unpin, .. }: &mut Context,
Context { lifetime, impl_unpin, .. }: &mut Context,
FieldsNamed { named: fields, .. }: &mut FieldsNamed,
variant_ident: &Ident,
) -> Result<(TokenStream, TokenStream)> {
let mut pat_vec = Vec::with_capacity(fields.len());
let mut expr_vec = Vec::with_capacity(fields.len());
let mut ty_vec = Vec::with_capacity(fields.len());
) -> Result<(TokenStream, TokenStream, TokenStream)> {
let mut proj_pat = Vec::with_capacity(fields.len());
let mut proj_body = Vec::with_capacity(fields.len());
let mut proj_field = 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)?;
impl_unpin.push(ty);
expr_vec.push(quote!(#ident: ::core::pin::Pin::new_unchecked(#ident)));
ty_vec.push(quote!(#ident: ::core::pin::Pin<&#lifetime mut #ty>));
proj_body.push(quote!(#ident: ::core::pin::Pin::new_unchecked(#ident)));
proj_field.push(quote!(#ident: ::core::pin::Pin<&#lifetime mut #ty>));
} else {
expr_vec.push(quote!(#ident: #ident));
ty_vec.push(quote!(#ident: &#lifetime mut #ty));
proj_body.push(quote!(#ident));
proj_field.push(quote!(#ident: &#lifetime mut #ty));
}
pat_vec.push(ident);
proj_pat.push(ident);
}

let proj_arm = quote! {
#original::#variant_ident { #(#pat_vec),* } => #projected::#variant_ident { #(#expr_vec),* }
};
let proj_ty = quote!(#variant_ident { #(#ty_vec),* });
Ok((proj_arm, proj_ty))
let proj_pat = quote!({ #(#proj_pat),* });
let proj_body = quote!({ #(#proj_body),* });
let proj_field = quote!({ #(#proj_field),* });
Ok((proj_pat, proj_body, proj_field))
}

fn unnamed(
Context { original, projected, lifetime, impl_unpin, .. }: &mut Context,
Context { lifetime, impl_unpin, .. }: &mut Context,
FieldsUnnamed { unnamed: fields, .. }: &mut FieldsUnnamed,
variant_ident: &Ident,
) -> Result<(TokenStream, TokenStream)> {
let mut pat_vec = Vec::with_capacity(fields.len());
let mut expr_vec = Vec::with_capacity(fields.len());
let mut ty_vec = Vec::with_capacity(fields.len());
) -> Result<(TokenStream, TokenStream, TokenStream)> {
let mut proj_pat = Vec::with_capacity(fields.len());
let mut proj_body = Vec::with_capacity(fields.len());
let mut proj_field = 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)?;
impl_unpin.push(ty);
expr_vec.push(quote!(::core::pin::Pin::new_unchecked(#x)));
ty_vec.push(quote!(::core::pin::Pin<&#lifetime mut #ty>));
proj_body.push(quote!(::core::pin::Pin::new_unchecked(#x)));
proj_field.push(quote!(::core::pin::Pin<&#lifetime mut #ty>));
} else {
expr_vec.push(quote!(#x));
ty_vec.push(quote!(&#lifetime mut #ty));
proj_body.push(quote!(#x));
proj_field.push(quote!(&#lifetime mut #ty));
}
pat_vec.push(x);
proj_pat.push(x);
}

let proj_arm = quote! {
#original::#variant_ident(#(#pat_vec),*) => #projected::#variant_ident(#(#expr_vec),*)
};
let proj_ty = quote!(#variant_ident(#(#ty_vec),*));
Ok((proj_arm, proj_ty))
}

fn unit(
Context { original, projected, .. }: &mut Context,
variant_ident: &Ident,
) -> (TokenStream, TokenStream) {
let proj_arm = quote! {
#original::#variant_ident => #projected::#variant_ident
};
let proj_ty = quote!(#variant_ident);
(proj_arm, proj_ty)
let proj_pat = quote!((#(#proj_pat),*));
let proj_body = quote!((#(#proj_body),*));
let proj_field = quote!((#(#proj_field),*));
Ok((proj_pat, proj_body, proj_field))
}
18 changes: 9 additions & 9 deletions pin-project-internal/src/pin_project/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::utils::VecExt;
use super::{proj_generics, Context, PIN};

pub(super) fn parse(mut cx: Context, mut item: ItemStruct) -> Result<TokenStream> {
let (proj_item_body, proj_init_body) = match &mut item.fields {
let (proj_fields, proj_init) = match &mut item.fields {
Fields::Named(FieldsNamed { named: fields, .. })
| Fields::Unnamed(FieldsUnnamed { unnamed: fields, .. })
if fields.is_empty() =>
Expand All @@ -29,14 +29,14 @@ pub(super) fn parse(mut cx: Context, mut item: ItemStruct) -> Result<TokenStream
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

let mut proj_items = quote! {
struct #proj_ident #proj_generics #where_clause #proj_item_body
struct #proj_ident #proj_generics #where_clause #proj_fields
};
let proj_method = quote! {
impl #impl_generics #orig_ident #ty_generics #where_clause {
fn project<#lifetime>(self: ::core::pin::Pin<&#lifetime mut Self>) -> #proj_ident #proj_ty_generics {
unsafe {
let this = ::core::pin::Pin::get_unchecked_mut(self);
#proj_ident #proj_init_body
#proj_ident #proj_init
}
}
}
Expand Down Expand Up @@ -69,9 +69,9 @@ fn named(
}
}

let proj_item_body = quote!({ #(#proj_fields,)* });
let proj_init_body = quote!({ #(#proj_init,)* });
Ok((proj_item_body, proj_init_body))
let proj_fields = quote!({ #(#proj_fields,)* });
let proj_init = quote!({ #(#proj_init,)* });
Ok((proj_fields, proj_init))
}

fn unnamed(
Expand All @@ -93,7 +93,7 @@ fn unnamed(
}
}

let proj_item_body = quote!((#(#proj_fields,)*););
let proj_init_body = quote!((#(#proj_init,)*));
Ok((proj_item_body, proj_init_body))
let proj_fields = quote!((#(#proj_fields,)*););
let proj_init = quote!((#(#proj_init,)*));
Ok((proj_fields, proj_init))
}

0 comments on commit 403d0da

Please sign in to comment.