Bug 1542179 - Share more code between ToAnimatedValue and ToComputedValue derive. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 09 Apr 2019 09:37:26 +0000
changeset 468546 7f7030e4c60dd6e44c811c2969c158095cba4536
parent 468545 c59414352a98db6eec0634e793597c53cdf6d2ee
child 468547 dfef5e9b459d20352443637a47b24fe9f26070e4
push id112733
push usercsabou@mozilla.com
push dateTue, 09 Apr 2019 16:30:22 +0000
treeherdermozilla-inbound@e14dba56bbfd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1542179
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1542179 - Share more code between ToAnimatedValue and ToComputedValue derive. r=heycam I'm going to add a ToResolvedValue, and I don't want to add more copy-pasta. This shouldn't change behavior. Differential Revision: https://phabricator.services.mozilla.com/D26289
servo/components/derive_common/cg.rs
servo/components/style_derive/to_animated_value.rs
servo/components/style_derive/to_computed_value.rs
--- a/servo/components/derive_common/cg.rs
+++ b/servo/components/derive_common/cg.rs
@@ -27,18 +27,18 @@ use synstructure::{self, BindStyle, Bind
 /// For example:
 ///
 ///     <T as ToComputedValue>::ComputedValue: Zero,
 ///
 /// This needs to run before adding other bounds to the type parameters.
 pub fn propagate_clauses_to_output_type(
     where_clause: &mut Option<syn::WhereClause>,
     generics: &syn::Generics,
-    trait_path: Path,
-    trait_output: Ident,
+    trait_path: &Path,
+    trait_output: &Ident,
 ) {
     let where_clause = match *where_clause {
         Some(ref mut clause) => clause,
         None => return,
     };
     let mut extra_bounds = vec![];
     for pred in &where_clause.predicates {
         let ty = match *pred {
@@ -99,17 +99,17 @@ where
             let expr = f(field.clone());
             quote! { let #mapped_field = #expr; }
         }));
         computations.append_all(mapped);
         Some(computations)
     })
 }
 
-pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: Ident) -> Path {
+pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &Ident) -> Path {
     let segment = PathSegment {
         ident: input.ident.clone(),
         arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
             args: input
                 .generics
                 .params
                 .iter()
                 .map(|arg| match arg {
--- a/servo/components/style_derive/to_animated_value.rs
+++ b/servo/components/style_derive/to_animated_value.rs
@@ -1,65 +1,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
-use derive_common::cg;
-use proc_macro2::{Span, TokenStream};
-use syn::{DeriveInput, Ident};
+use proc_macro2::TokenStream;
+use syn::DeriveInput;
 use synstructure::BindStyle;
-
-pub fn derive(mut input: DeriveInput) -> TokenStream {
-    let mut where_clause = input.generics.where_clause.take();
-    cg::propagate_clauses_to_output_type(
-        &mut where_clause,
-        &input.generics,
-        parse_quote!(crate::values::animated::ToAnimatedValue),
-        parse_quote!(AnimatedValue),
-    );
-    for param in input.generics.type_params() {
-        cg::add_predicate(
-            &mut where_clause,
-            parse_quote!(#param: crate::values::animated::ToAnimatedValue),
-        );
-    }
+use to_computed_value;
 
-    let to_body = cg::fmap_match(
-        &input,
-        BindStyle::Move,
-        |binding| quote!(crate::values::animated::ToAnimatedValue::to_animated_value(#binding)),
-    );
-    let from_body = cg::fmap_match(
-        &input,
-        BindStyle::Move,
-        |binding| quote!(crate::values::animated::ToAnimatedValue::from_animated_value(#binding)),
-    );
+pub fn derive(input: DeriveInput) -> TokenStream {
+    let trait_impl = |from_body, to_body| {
+        quote! {
+            #[inline]
+            fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+                match animated {
+                    #from_body
+                }
+            }
 
-    input.generics.where_clause = where_clause;
-    let name = &input.ident;
-    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-    let animated_value_type = cg::fmap_trait_output(
-        &input,
-        &parse_quote!(crate::values::animated::ToAnimatedValue),
-        Ident::new("AnimatedValue", Span::call_site()),
-    );
-
-    quote! {
-        impl #impl_generics crate::values::animated::ToAnimatedValue for #name #ty_generics #where_clause {
-            type AnimatedValue = #animated_value_type;
-
-            #[allow(unused_variables)]
             #[inline]
             fn to_animated_value(self) -> Self::AnimatedValue {
                 match self {
                     #to_body
                 }
             }
+       }
+    };
 
-            #[inline]
-            fn from_animated_value(animated: Self::AnimatedValue) -> Self {
-                match animated {
-                    #from_body
-                }
-            }
-        }
-    }
+    // TODO(emilio): Consider optimizing away non-generic cases as well?
+    let non_generic_implementation = || None;
+
+    to_computed_value::derive_to_value(
+        input,
+        parse_quote!(crate::values::animated::ToAnimatedValue),
+        parse_quote!(AnimatedValue),
+        BindStyle::Move,
+        |_| false,
+        |binding| quote!(crate::values::animated::ToAnimatedValue::from_animated_value(#binding)),
+        |binding| quote!(crate::values::animated::ToAnimatedValue::to_animated_value(#binding)),
+        trait_impl,
+        non_generic_implementation,
+    )
 }
--- a/servo/components/style_derive/to_computed_value.rs
+++ b/servo/components/style_derive/to_computed_value.rs
@@ -1,117 +1,160 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 use derive_common::cg;
-use proc_macro2::{Span, TokenStream};
-use syn::{DeriveInput, Ident};
-use synstructure::BindStyle;
+use proc_macro2::TokenStream;
+use syn::{DeriveInput, Ident, Path};
+use synstructure::{BindStyle, BindingInfo};
 
-pub fn derive(mut input: DeriveInput) -> TokenStream {
+pub fn derive_to_value(
+    mut input: DeriveInput,
+    trait_path: Path,
+    output_type_name: Ident,
+    bind_style: BindStyle,
+    // Returns whether to apply the field bound for a given item.
+    mut field_bound: impl FnMut(&BindingInfo) -> bool,
+    // Returns a token stream of the form: trait_path::from_foo(#binding)
+    mut call_from: impl FnMut(&BindingInfo) -> TokenStream,
+    mut call_to: impl FnMut(&BindingInfo) -> TokenStream,
+    // Returns a tokenstream of the form:
+    // fn from_function_syntax(foobar) -> Baz {
+    //     #first_arg
+    // }
+    //
+    // fn to_function_syntax(foobar) -> Baz {
+    //     #second_arg
+    // }
+    mut trait_impl: impl FnMut(TokenStream, TokenStream) -> TokenStream,
+    // if this is provided, the derive for non-generic types will be simplified
+    // to this token stream, which should be the body of the impl block.
+    non_generic_implementation: impl FnOnce() -> Option<TokenStream>,
+) -> TokenStream {
     let mut where_clause = input.generics.where_clause.take();
     cg::propagate_clauses_to_output_type(
         &mut where_clause,
         &input.generics,
-        parse_quote!(crate::values::computed::ToComputedValue),
-        parse_quote!(ComputedValue),
+        &trait_path,
+        &output_type_name,
     );
     let (to_body, from_body) = {
         let params = input.generics.type_params().collect::<Vec<_>>();
         for param in &params {
             cg::add_predicate(
                 &mut where_clause,
-                parse_quote!(#param: crate::values::computed::ToComputedValue),
+                parse_quote!(#param: #trait_path),
             );
         }
 
-        let to_body = cg::fmap_match(&input, BindStyle::Ref, |binding| {
-            let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast());
-            if attrs.field_bound {
+        let to_body = cg::fmap_match(&input, bind_style, |binding| {
+            if field_bound(&binding) {
                 let ty = &binding.ast().ty;
 
                 let output_type = cg::map_type_params(
                     ty,
                     &params,
-                    &mut |ident| parse_quote!(<#ident as crate::values::computed::ToComputedValue>::ComputedValue),
+                    &mut |ident| parse_quote!(<#ident as #trait_path>::#output_type_name),
                 );
 
                 cg::add_predicate(
                     &mut where_clause,
                     parse_quote!(
-                        #ty: crate::values::computed::ToComputedValue<ComputedValue = #output_type>
+                        #ty: #trait_path<#output_type_name = #output_type>
                     ),
                 );
             }
-            quote! {
-                crate::values::computed::ToComputedValue::to_computed_value(#binding, context)
-            }
+            call_to(&binding)
         });
-        let from_body = cg::fmap_match(&input, BindStyle::Ref, |binding| {
-            quote! {
-                crate::values::computed::ToComputedValue::from_computed_value(#binding)
-            }
+        let from_body = cg::fmap_match(&input, bind_style, |binding| {
+            call_from(&binding)
         });
 
         (to_body, from_body)
     };
 
     input.generics.where_clause = where_clause;
     let name = &input.ident;
     let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
     if input.generics.type_params().next().is_none() {
-        return quote! {
-            impl #impl_generics crate::values::computed::ToComputedValue for #name #ty_generics
-            #where_clause
-            {
-                type ComputedValue = Self;
-
-                #[inline]
-                fn to_computed_value(
-                    &self,
-                    _context: &crate::values::computed::Context,
-                ) -> Self::ComputedValue {
-                    std::clone::Clone::clone(self)
+        if let Some(non_generic_implementation) = non_generic_implementation() {
+            return quote! {
+                impl #impl_generics #trait_path for #name #ty_generics
+                #where_clause
+                {
+                    #non_generic_implementation
                 }
-
-                #[inline]
-                fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-                    std::clone::Clone::clone(computed)
-                }
-            }
-        };
+            };
+        }
     }
 
     let computed_value_type = cg::fmap_trait_output(
         &input,
-        &parse_quote!(crate::values::computed::ToComputedValue),
-        Ident::new("ComputedValue", Span::call_site()),
+        &trait_path,
+        &output_type_name,
     );
 
+    let impl_ = trait_impl(from_body, to_body);
+
     quote! {
-        impl #impl_generics crate::values::computed::ToComputedValue for #name #ty_generics #where_clause {
-            type ComputedValue = #computed_value_type;
+        impl #impl_generics #trait_path for #name #ty_generics #where_clause {
+            type #output_type_name = #computed_value_type;
+
+            #impl_
+        }
+    }
+}
+
+pub fn derive(input: DeriveInput) -> TokenStream {
+    let trait_impl = |from_body, to_body| {
+        quote! {
+            #[inline]
+            fn from_computed_value(computed: &Self::ComputedValue) -> Self {
+                match *computed {
+                    #from_body
+                }
+            }
 
             #[allow(unused_variables)]
             #[inline]
             fn to_computed_value(&self, context: &crate::values::computed::Context) -> Self::ComputedValue {
                 match *self {
                     #to_body
                 }
             }
+       }
+    };
+
+    let non_generic_implementation = || {
+        Some(quote! {
+            type ComputedValue = Self;
+
+            #[inline]
+            fn to_computed_value(&self, _: &crate::values::computed::Context) -> Self::ComputedValue {
+                std::clone::Clone::clone(self)
+            }
 
             #[inline]
             fn from_computed_value(computed: &Self::ComputedValue) -> Self {
-                match *computed {
-                    #from_body
-                }
+                std::clone::Clone::clone(computed)
             }
-        }
-    }
+        })
+    };
+
+    derive_to_value(
+        input,
+        parse_quote!(crate::values::computed::ToComputedValue),
+        parse_quote!(ComputedValue),
+        BindStyle::Ref,
+        |binding| cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast()).field_bound,
+        |binding| quote!(crate::values::computed::ToComputedValue::from_computed_value(#binding)),
+        |binding| quote!(crate::values::computed::ToComputedValue::to_computed_value(#binding, context)),
+        trait_impl,
+        non_generic_implementation,
+    )
 }
 
 #[darling(attributes(compute), default)]
 #[derive(Default, FromField)]
 struct ComputedValueAttrs {
     field_bound: bool,
 }