Skip to content

Commit

Permalink
use match_expansion
Browse files Browse the repository at this point in the history
  • Loading branch information
sokra committed Jul 17, 2024
1 parent c9a738c commit 47efbda
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 222 deletions.
61 changes: 33 additions & 28 deletions crates/turbo-tasks-macros-shared/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ use syn::{
/// These helpers should themselves call [generate_destructuring] to generate
/// the destructure necessary to access the fields of the value.
pub fn match_expansion<
EN: Fn(&Ident, &FieldsNamed) -> (TokenStream, TokenStream),
EU: Fn(&Ident, &FieldsUnnamed) -> (TokenStream, TokenStream),
U: Fn(&Ident) -> TokenStream,
EN: Fn(TokenStream, &FieldsNamed) -> (TokenStream, TokenStream),
EU: Fn(TokenStream, &FieldsUnnamed) -> (TokenStream, TokenStream),
U: Fn(TokenStream) -> TokenStream,
>(
derive_input: &DeriveInput,
expand_named: &EN,
Expand All @@ -33,36 +33,41 @@ pub fn match_expansion<
let expand_unit = move |ident| (TokenStream::new(), expand_unit(ident));
match &derive_input.data {
Data::Enum(DataEnum { variants, .. }) => {
let (variants_idents, (variants_fields_capture, expansion)): (
Vec<_>,
(Vec<_>, Vec<_>),
) = variants
.iter()
.map(|variant| {
(
&variant.ident,
expand_fields(
&variant.ident,
&variant.fields,
expand_named,
expand_unnamed,
expand_unit,
),
)
})
.unzip();
let (idents, (variants_fields_capture, expansion)): (Vec<_>, (Vec<_>, Vec<_>)) =
variants
.iter()
.map(|variant| {
let variants_idents = &variant.ident;
let ident = quote! { #ident::#variants_idents };
(
ident.clone(),
expand_fields(
ident,
&variant.fields,
expand_named,
expand_unnamed,
expand_unit,
),
)
})
.unzip();

quote! {
match self {
#(
#ident::#variants_idents #variants_fields_capture => #expansion,
#idents #variants_fields_capture => #expansion,
)*
}
}
}
Data::Struct(DataStruct { fields, .. }) => {
let (captures, expansion) =
expand_fields(ident, fields, expand_named, expand_unnamed, expand_unit);
let (captures, expansion) = expand_fields(
quote! { #ident },
fields,
expand_named,
expand_unnamed,
expand_unit,
);

if fields.is_empty() {
assert!(captures.is_empty());
Expand Down Expand Up @@ -100,12 +105,12 @@ pub fn match_expansion<
pub fn expand_fields<
'ident,
'fields,
EN: Fn(&'ident Ident, &'fields FieldsNamed) -> R,
EU: Fn(&'ident Ident, &'fields FieldsUnnamed) -> R,
U: Fn(&'ident Ident) -> R,
EN: Fn(TokenStream, &'fields FieldsNamed) -> R,
EU: Fn(TokenStream, &'fields FieldsUnnamed) -> R,
U: Fn(TokenStream) -> R,
R,
>(
ident: &'ident Ident,
ident: TokenStream,
fields: &'fields Fields,
expand_named: EN,
expand_unnamed: EU,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use proc_macro::TokenStream;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, FieldsNamed, FieldsUnnamed};
use turbo_tasks_macros_shared::{generate_exhaustive_destructuring, match_expansion};
Expand Down Expand Up @@ -35,7 +35,7 @@ pub fn derive_deterministic_hash(input: TokenStream) -> TokenStream {

/// Hashes a struct or enum variant with named fields (e.g. `struct Foo {
/// bar: u32 }`, `Foo::Bar { baz: u32 }`).
fn hash_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) {
fn hash_named(_ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) {
let (captures, fields_idents) = generate_exhaustive_destructuring(fields.named.iter());
(
captures,
Expand All @@ -49,7 +49,7 @@ fn hash_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStrea

/// Hashes a struct or enum variant with unnamed fields (e.g. `struct
/// Foo(u32)`, `Foo::Bar(u32)`).
fn hash_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) {
fn hash_unnamed(_ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) {
let (captures, fields_idents) = generate_exhaustive_destructuring(fields.unnamed.iter());
(
captures,
Expand All @@ -62,6 +62,6 @@ fn hash_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenS
}

/// Hashes a unit struct or enum variant (e.g. `struct Foo;`, `Foo::Bar`).
fn hash_unit(_ident: &Ident) -> TokenStream2 {
fn hash_unit(_ident: TokenStream2) -> TokenStream2 {
quote! { { } }
}
259 changes: 86 additions & 173 deletions crates/turbo-tasks-macros/src/derive/task_input_macro.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::{parse_macro_input, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput};
use syn::{parse_macro_input, spanned::Spanned, DeriveInput};
use turbo_tasks_macros_shared::{generate_exhaustive_destructuring, match_expansion};

pub fn derive_task_input(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
Expand Down Expand Up @@ -51,177 +51,90 @@ pub fn derive_task_input(input: TokenStream) -> TokenStream {
}
}

let is_resolved_impl;
let is_transient_impl;
let resolve_impl;

match &derive_input.data {
Data::Enum(DataEnum { variants, .. }) => {
let variants = if variants.is_empty() {
vec![(
quote! { _ => true },
quote! { _ => false },
quote! { _ => unreachable!() },
)]
} else {
variants
.iter()
.map(|variant| {
let variant_ident = &variant.ident;
let pattern;
let construct;
let field_names: Vec<_>;
match &variant.fields {
syn::Fields::Named(fields) => {
field_names = fields
.named
.iter()
.map(|field| field.ident.clone().unwrap())
.collect();
pattern = quote! {
#ident::#variant_ident { #(#field_names,)* }
};
construct = quote! {
#ident::#variant_ident { #(#field_names,)* }
};
}
syn::Fields::Unnamed(fields) => {
field_names = (0..fields.unnamed.len())
.map(|i| Ident::new(&format!("field{}", i), fields.span()))
.collect();
pattern = quote! {
#ident::#variant_ident ( #(#field_names,)* )
};
construct = quote! {
#ident::#variant_ident ( #(#field_names,)* )
};
}
syn::Fields::Unit => {
field_names = vec![];
pattern = quote! {
#ident::#variant_ident
};
construct = quote! {
#ident::#variant_ident
};
}
}
(
quote! {
#pattern => {
#(#field_names.is_resolved() &&)* true
},
},
quote! {
#pattern => {
#(#field_names.is_transient() ||)* false
},
},
quote! {
#pattern => {
#(
let #field_names = #field_names.resolve().await?;
)*
#construct
},
},
)
})
.collect::<Vec<_>>()
};

let is_resolve_variants = variants.iter().map(|(is_resolved, _, _)| is_resolved);
let is_transient_variants = variants.iter().map(|(_, is_transient, _)| is_transient);
let resolve_variants = variants.iter().map(|(_, _, resolve)| resolve);

is_resolved_impl = quote! {
match self {
#(
#is_resolve_variants
)*
}
};
is_transient_impl = quote! {
match self {
#(
#is_transient_variants
)*
}
};
resolve_impl = quote! {
Ok(match self {
#(
#resolve_variants
)*
})
};
}
Data::Struct(DataStruct { fields, .. }) => {
let destruct;
let construct;
let field_names: Vec<Ident>;
match fields {
syn::Fields::Named(fields) => {
field_names = fields
.named
.iter()
.map(|field| field.ident.clone().unwrap())
.collect();
destruct = quote! {
let #ident { #(#field_names,)* } = self;
};
construct = quote! {
#ident { #(#field_names,)* }
};
}
syn::Fields::Unnamed(fields) => {
field_names = (0..fields.unnamed.len())
.map(|i| Ident::new(&format!("field{}", i), fields.span()))
.collect();
destruct = quote! {
let #ident ( #(#field_names,)* ) = self;
};
construct = quote! {
#ident ( #(#field_names,)* )
};
}
syn::Fields::Unit => {
field_names = vec![];
destruct = quote! {};
construct = quote! {
#ident
};
}
}

is_resolved_impl = quote! {
#destruct
#(#field_names.is_resolved() &&)* true
};
is_transient_impl = quote! {
#destruct
#(#field_names.is_transient() ||)* false
};
resolve_impl = quote! {
#destruct
#(
let #field_names = #field_names.resolve().await?;
)*
Ok(#construct)
};
}
_ => {
derive_input
.span()
.unwrap()
.error("unsupported syntax")
.emit();

is_resolved_impl = quote! {};
is_transient_impl = quote! {};
resolve_impl = quote! {};
}
};
let is_resolved_impl = match_expansion(
&derive_input,
&|_ident, fields| {
let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter());
(
capture,
quote! {
{#(
#fields.is_resolved() &&
)* true}
},
)
},
&|_ident, fields| {
let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter());
(
capture,
quote! {
{#(
#fields.is_resolved() &&
)* true}
},
)
},
&|_ident| quote! {true},
);
let is_transient_impl = match_expansion(
&derive_input,
&|_ident, fields| {
let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter());
(
capture,
quote! {
{#(
#fields.is_transient() ||
)* false}
},
)
},
&|_ident, fields| {
let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter());
(
capture,
quote! {
{#(
#fields.is_transient() ||
)* false}
},
)
},
&|_ident| quote! {false},
);
let resolve_impl = match_expansion(
&derive_input,
&|ident, fields| {
let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter());
(
capture,
quote! {
{
#(
let #fields = #fields.resolve().await?;
)*
Ok(#ident { #(#fields),* })
}
},
)
},
&|ident, fields| {
let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter());
(
capture,
quote! {
{
#(
let #fields = #fields.resolve().await?;
)*
Ok(#ident(#(#fields),*))
}
},
)
},
&|ident| quote! {Ok(#ident)},
);

let generic_params: Vec<_> = generics
.params
Expand Down
Loading

0 comments on commit 47efbda

Please sign in to comment.