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

Reduce amount of code generated by ValueDebugFormat #8239

Merged
merged 2 commits into from
Jun 4, 2024
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
30 changes: 25 additions & 5 deletions crates/turbo-tasks-macros-shared/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ use syn::{
pub fn match_expansion<
EN: Fn(&Ident, &FieldsNamed) -> (TokenStream, TokenStream),
EU: Fn(&Ident, &FieldsUnnamed) -> (TokenStream, TokenStream),
U: Fn(&Ident) -> (TokenStream, TokenStream),
U: Fn(&Ident) -> TokenStream,
>(
derive_input: &DeriveInput,
expand_named: &EN,
expand_unnamed: &EU,
expand_unit: &U,
) -> TokenStream {
let ident = &derive_input.ident;
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)): (
Expand Down Expand Up @@ -63,9 +64,19 @@ pub fn match_expansion<
let (captures, expansion) =
expand_fields(ident, fields, expand_named, expand_unnamed, expand_unit);

quote! {
match self {
#ident #captures => #expansion
if fields.is_empty() {
assert!(captures.is_empty());
// a match expression here doesn't make sense as there's no fields to capture,
// just pass through the inner expression.
expansion
} else {
match fields {
Fields::Named(_) | Fields::Unnamed(_) => quote! {
match self {
#ident #captures => #expansion
}
},
Fields::Unit => unreachable!(),
}
}
}
Expand All @@ -82,6 +93,10 @@ pub fn match_expansion<
}

/// Formats the fields of any structure or enum variant.
///
/// Empty lists of named or unnamed fields are treated as unit structs, as they
/// are semantically identical, and the `expand_unit` codepath can usually
/// generate better code.
pub fn expand_fields<
'ident,
'fields,
Expand All @@ -96,10 +111,15 @@ pub fn expand_fields<
expand_unnamed: EU,
expand_unit: U,
) -> R {
if fields.is_empty() {
// any empty struct (regardless of the syntax used during declaration) is
// equivalent to a unit struct
return expand_unit(ident);
}
match fields {
Fields::Named(named) => expand_named(ident, named),
Fields::Unnamed(unnamed) => expand_unnamed(ident, unnamed),
Fields::Unit => expand_unit(ident),
Fields::Unit => unreachable!(),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, TokenStream2) {
(quote! {}, quote! { { } })
fn hash_unit(_ident: &Ident) -> TokenStream2 {
quote! { { } }
}
4 changes: 2 additions & 2 deletions crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ fn trace_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token
)
}

fn trace_unit(_ident: &Ident) -> (TokenStream2, TokenStream2) {
(quote! {}, quote! { { } })
fn trace_unit(_ident: &Ident) -> TokenStream2 {
quote! { { } }
}
73 changes: 43 additions & 30 deletions crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,35 @@ pub fn derive_value_debug_format(input: TokenStream) -> TokenStream {
/// Formats a single field nested inside named or unnamed fields.
fn format_field(value: TokenStream2) -> TokenStream2 {
quote! {
turbo_tasks::macro_helpers::value_debug_format_field(#value.value_debug_format(depth.saturating_sub(1))).await
turbo_tasks::macro_helpers::value_debug_format_field(#value.value_debug_format(depth.saturating_sub(1)))
}
}

/// Formats a struct or enum variant with named fields (e.g. `struct Foo {
/// bar: u32 }`, `Foo::Bar { baz: u32 }`).
fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) {
let (captures, fields_idents) = generate_destructuring(fields.named.iter(), &filter_field);
let fields_values = fields_idents.iter().cloned().map(format_field);
(
captures,
quote! {
FormattingStruct::new_named(
stringify!(#ident),
vec![#(
FormattingField::new(
stringify!(#fields_idents),
#fields_values,
),
)*],
)
if fields_idents.is_empty() {
// this can happen if all fields are ignored, we must special-case this to avoid
// rustc being unable to infer the type of an empty vec of futures
quote! {
FormattingStruct::new_named(stringify!(#ident), vec![])
}
} else {
let fields_values = fields_idents.iter().cloned().map(format_field);
quote! {
FormattingStruct::new_named_async(
stringify!(#ident),
vec![#(
AsyncFormattingField::new(
stringify!(#fields_idents),
#fields_values,
),
)*],
).await
}
},
)
}
Expand All @@ -85,29 +93,34 @@ fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre
/// Foo(u32)`, `Foo::Bar(u32)`).
fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) {
let (captures, fields_idents) = generate_destructuring(fields.unnamed.iter(), &filter_field);
let fields_values = fields_idents.into_iter().map(format_field);
(
captures,
quote! {
FormattingStruct::new_unnamed(
stringify!(#ident),
vec![#(
#fields_values,
)*],
)
if fields_idents.is_empty() {
// this can happen if all fields are ignored, we must special-case this to avoid
// rustc being unable to infer the type of an empty vec of futures
quote! {
FormattingStruct::new_unnamed(stringify!(#ident), vec![])
}
} else {
let fields_values = fields_idents.into_iter().map(format_field);
quote! {
FormattingStruct::new_unnamed_async(
stringify!(#ident),
vec![#(
#fields_values,
)*],
).await
}
},
)
}

/// Formats a unit struct or enum variant (e.g. `struct Foo;`, `Foo::Bar`).
fn format_unit(ident: &Ident) -> (TokenStream2, TokenStream2) {
(
quote! {},
quote! {
FormattingStruct::new_unnamed(
stringify!(#ident),
vec![],
)
},
)
fn format_unit(ident: &Ident) -> TokenStream2 {
quote! {
FormattingStruct::new_unnamed(
stringify!(#ident),
vec![],
)
}
}
51 changes: 50 additions & 1 deletion crates/turbo-tasks/src/debug/internal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::borrow::Cow;
use std::{borrow::Cow, future::Future};

use futures::future::join_all;
pub use turbo_tasks_macros::ValueDebug;

/// Representation of a named field of a structure for formatting purposes of
Expand All @@ -16,6 +17,34 @@ impl<'a> FormattingField<'a> {
}
}

/// Representation of a named field of a structure for formatting purposes of
/// `ValueDebug` implementations.
#[derive(Debug)]
pub struct AsyncFormattingField<'a, Fut>
where
Fut: Future<Output = String>,
{
name: &'a str,
contents: Fut,
}

impl<'a, Fut: Future<Output = String>> AsyncFormattingField<'a, Fut>
where
Fut: Future<Output = String>,
{
pub fn new(name: &'a str, contents: Fut) -> Self {
Self { name, contents }
}

pub async fn resolve(self) -> FormattingField<'a> {
let Self { name, contents } = self;
FormattingField {
name,
contents: contents.await,
}
}
}

/// Representation of a structure for formatting purposes of `ValueDebug`
/// implementations.
pub enum FormattingStruct<'a> {
Expand All @@ -36,6 +65,26 @@ impl<'a> FormattingStruct<'a> {
pub fn new_unnamed(name: &'a str, fields: Vec<String>) -> Self {
Self::Unnamed { name, fields }
}

pub async fn new_named_async(
name: &'a str,
fields: Vec<AsyncFormattingField<'a, impl Future<Output = String>>>,
) -> Self {
Self::Named {
name,
fields: join_all(fields.into_iter().map(AsyncFormattingField::resolve)).await,
}
}

pub async fn new_unnamed_async(
name: &'a str,
fields: Vec<impl Future<Output = String>>,
) -> Self {
Self::Unnamed {
name,
fields: join_all(fields).await,
}
}
}

impl<'a> std::fmt::Debug for FormattingStruct<'a> {
Expand Down
Loading