Bug 1532134 - When deriving something with an output type, map preconditions as well. r=xidorn
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 03 Mar 2019 11:31:21 +0000
changeset 520055 7aa72a6aa1b501c6f7a0efc7a4a4ea14d15f0030
parent 520054 b5dec9e96f42cd166b4cdaf70b6765be34454aae
child 520056 5a4a2f2465b0142e901d52f1850c2fc1a6e0cc6b
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersxidorn
bugs1532134
milestone67.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 1532134 - When deriving something with an output type, map preconditions as well. r=xidorn Otherwise, deriving ToComputedValue and ToAnimatedValue in structs or enums with other where clauses just doesn't work. Differential Revision: https://phabricator.services.mozilla.com/D21859
servo/components/style_derive/cg.rs
servo/components/style_derive/to_animated_value.rs
servo/components/style_derive/to_computed_value.rs
--- a/servo/components/style_derive/cg.rs
+++ b/servo/components/style_derive/cg.rs
@@ -7,16 +7,80 @@ use proc_macro2::{Span, TokenStream};
 use quote::TokenStreamExt;
 use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
 use syn::{GenericArgument, GenericParam, Ident, Path};
 use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray};
 use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
 use syn::{Variant, WherePredicate};
 use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};
 
+/// Given an input type which has some where clauses already, like:
+///
+/// struct InputType<T>
+/// where
+///     T: Zero,
+/// {
+///     ...
+/// }
+///
+/// Add the necessary `where` clauses so that the output type of a trait
+/// fulfils them.
+///
+/// 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,
+) {
+    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 {
+            syn::WherePredicate::Type(ref ty) => ty,
+            ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
+        };
+
+        let path = match ty.bounded_ty {
+            syn::Type::Path(ref p) => &p.path,
+            ref ty => panic!("Unhanded complex where type: {:?}", ty),
+        };
+
+        assert!(
+            ty.lifetimes.is_none(),
+            "Unhanded complex lifetime bound: {:?}",
+            ty,
+        );
+
+        let ident = match path_to_ident(path) {
+            Some(i) => i,
+            None => panic!("Unhanded complex where type path: {:?}", path),
+        };
+
+        if generics.type_params().any(|param| param.ident == *ident) {
+            extra_bounds.push(ty.clone());
+        }
+    }
+
+    for bound in extra_bounds {
+        let ty = bound.bounded_ty;
+        let bounds = bound.bounds;
+        where_clause.predicates.push(
+            parse_quote!(<#ty as #trait_path>::#trait_output: #bounds),
+        )
+    }
+}
+
 pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
     where_clause
         .get_or_insert(parse_quote!(where))
         .predicates
         .push(pred);
 }
 
 pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, mut f: F) -> TokenStream
--- a/servo/components/style_derive/to_animated_value.rs
+++ b/servo/components/style_derive/to_animated_value.rs
@@ -4,16 +4,22 @@
 
 use crate::cg;
 use proc_macro2::{Span, TokenStream};
 use syn::{DeriveInput, Ident};
 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),
         );
     }
 
     let to_body = cg::fmap_match(
--- a/servo/components/style_derive/to_computed_value.rs
+++ b/servo/components/style_derive/to_computed_value.rs
@@ -4,16 +4,22 @@
 
 use crate::cg;
 use proc_macro2::{Span, TokenStream};
 use syn::{DeriveInput, Ident};
 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::computed::ToComputedValue),
+        parse_quote!(ComputedValue),
+    );
     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),
             );
         }