servo: Merge #20237 - Opt into field bounds when deriving ToCss, instead of opting out (from servo:derive-all-the-things); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 08 Mar 2018 12:25:20 -0500
changeset 462234 3ebf1fc64bf0074c4fee1928df7542bd30b60ea6
parent 462233 c5650d15df01218401ce2ae9c57e1db6ae207054
child 462235 c15be736fff0142881ff6129f18728dd96a6f7a7
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone60.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
servo: Merge #20237 - Opt into field bounds when deriving ToCss, instead of opting out (from servo:derive-all-the-things); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: b6de0563fbd74799060735b8452f86a533f19573
servo/components/style/values/generics/basic_shape.rs
servo/components/style/values/generics/border.rs
servo/components/style/values/generics/font.rs
servo/components/style/values/generics/svg.rs
servo/components/style/values/generics/transform.rs
servo/components/style_derive/animate.rs
servo/components/style_derive/cg.rs
servo/components/style_derive/to_animated_zero.rs
servo/components/style_derive/to_computed_value.rs
servo/components/style_derive/to_css.rs
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -57,19 +57,19 @@ pub enum ShapeSource<BasicShape, Referen
     #[animation(error)]
     None,
 }
 
 #[allow(missing_docs)]
 #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
 #[derive(ToComputedValue, ToCss)]
 pub enum BasicShape<H, V, LengthOrPercentage> {
-    Inset(InsetRect<LengthOrPercentage>),
-    Circle(Circle<H, V, LengthOrPercentage>),
-    Ellipse(Ellipse<H, V, LengthOrPercentage>),
+    Inset(#[css(field_bound)] InsetRect<LengthOrPercentage>),
+    Circle(#[css(field_bound)] Circle<H, V, LengthOrPercentage>),
+    Ellipse(#[css(field_bound)] Ellipse<H, V, LengthOrPercentage>),
     Polygon(Polygon<LengthOrPercentage>),
 }
 
 /// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
 #[allow(missing_docs)]
 #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToComputedValue)]
 pub struct InsetRect<LengthOrPercentage> {
     pub rect: Rect<LengthOrPercentage>,
--- a/servo/components/style/values/generics/border.rs
+++ b/servo/components/style/values/generics/border.rs
@@ -27,29 +27,29 @@ pub struct BorderImageSlice<NumberOrPerc
     pub offsets: Rect<NumberOrPercentage>,
     /// Whether to fill the middle part.
     pub fill: bool,
 }
 
 /// A generic value for the `border-*-radius` longhand properties.
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]
 #[derive(MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
-pub struct BorderCornerRadius<L>(pub Size<L>);
+pub struct BorderCornerRadius<L>(#[css(field_bound)] pub Size<L>);
 
 impl<L> BorderCornerRadius<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderCornerRadius(Size::new(w, h))
     }
 }
 
 /// A generic value for the `border-spacing` property.
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
 #[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
-pub struct BorderSpacing<L>(pub Size<L>);
+pub struct BorderSpacing<L>(#[css(field_bound)] pub Size<L>);
 
 impl<L> BorderSpacing<L> {
     /// Trivially create a `BorderCornerRadius`.
     pub fn new(w: L, h: L) -> Self {
         BorderSpacing(Size::new(w, h))
     }
 }
 
--- a/servo/components/style/values/generics/font.rs
+++ b/servo/components/style/values/generics/font.rs
@@ -63,18 +63,19 @@ where
         if self.tag != other.tag {
             return Err(());
         }
         self.value.compute_squared_distance(&other.value)
     }
 }
 
 /// A value both for font-variation-settings and font-feature-settings.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
-pub struct FontSettings<T>(pub Box<[T]>);
+#[css(comma)]
+#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
+pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
 
 impl<T> FontSettings<T> {
     /// Default value of font settings as `normal`.
     #[inline]
     pub fn normal() -> Self {
         FontSettings(vec![].into_boxed_slice())
     }
 }
@@ -91,40 +92,16 @@ impl<T: Parse> Parse for FontSettings<T>
         }
 
         Ok(FontSettings(
             input.parse_comma_separated(|i| T::parse(context, i))?.into_boxed_slice()
         ))
     }
 }
 
-impl<T: ToCss> ToCss for FontSettings<T> {
-    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
-    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        if self.0.is_empty() {
-            return dest.write_str("normal");
-        }
-
-        let mut first = true;
-        for item in self.0.iter() {
-            if !first {
-                dest.write_str(", ")?;
-            }
-            first = false;
-            item.to_css(dest)?;
-        }
-
-        Ok(())
-    }
-}
-
 /// A font four-character tag, represented as a u32 for convenience.
 ///
 /// See:
 ///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
 ///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
 ///
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
 pub struct FontTag(pub u32);
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -1,18 +1,17 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 //! Generic types for CSS values in SVG
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use style_traits::{ParseError, StyleParseErrorKind};
 use values::{Either, None_};
 use values::computed::NumberOrPercentage;
 use values::computed::length::LengthOrPercentage;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// An SVG paint value
 ///
 /// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
@@ -194,51 +193,26 @@ impl <LengthOrPercentageType: Parse, Num
 pub enum SVGLength<LengthType> {
     /// `<length> | <percentage> | <number>`
     Length(LengthType),
     /// `context-value`
     ContextValue,
 }
 
 /// Generic value for stroke-dasharray.
-#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, ToAnimatedValue,
-         ToComputedValue)]
+#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
+#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
 pub enum SVGStrokeDashArray<LengthType> {
     /// `[ <length> | <percentage> | <number> ]#`
-    Values(Vec<LengthType>),
+    #[css(comma)]
+    Values(#[css(if_empty = "none", iterable)] Vec<LengthType>),
     /// `context-value`
     ContextValue,
 }
 
-impl<LengthType> ToCss for SVGStrokeDashArray<LengthType> where LengthType: ToCss {
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        match self {
-            &SVGStrokeDashArray::Values(ref values) => {
-                let mut iter = values.iter();
-                if let Some(first) = iter.next() {
-                    first.to_css(dest)?;
-                    for item in iter {
-                        dest.write_str(", ")?;
-                        item.to_css(dest)?;
-                    }
-                    Ok(())
-                } else {
-                    dest.write_str("none")
-                }
-            }
-            &SVGStrokeDashArray::ContextValue => {
-                dest.write_str("context-value")
-            }
-        }
-    }
-}
-
 /// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
 /// addition to opacity value.
 #[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
 #[derive(PartialEq, ToAnimatedZero, ToComputedValue, ToCss)]
 pub enum SVGOpacity<OpacityType> {
     /// `<opacity-value>`
     Opacity(OpacityType),
     /// `context-fill-opacity`
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -2,18 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic types for CSS values that are related to transformations.
 
 use app_units::Au;
 use euclid::{self, Rect, Transform3D};
 use num_traits::Zero;
-use std::fmt::{self, Write};
-use style_traits::{CssWriter, ToCss};
 use values::{computed, CSSFloat};
 use values::computed::length::Length as ComputedLength;
 use values::computed::length::LengthOrPercentage as ComputedLengthOrPercentage;
 use values::specified::length::Length as SpecifiedLength;
 use values::specified::length::LengthOrPercentage as SpecifiedLengthOrPercentage;
 
 /// A generic 2D transformation matrix.
 #[allow(missing_docs)]
@@ -245,28 +243,26 @@ pub enum TransformOperation<Angle, Numbe
     /// The value must be greater than or equal to zero.
     #[css(function)]
     Perspective(Length),
     /// A intermediate type for interpolation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "interpolatematrix")]
     InterpolateMatrix {
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         from_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
         >,
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         to_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
@@ -274,45 +270,42 @@ pub enum TransformOperation<Angle, Numbe
         #[compute(clone)]
         progress: computed::Percentage,
     },
     /// A intermediate type for accumulation of mismatched transform lists.
     #[allow(missing_docs)]
     #[css(comma, function = "accumulatematrix")]
     AccumulateMatrix {
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         from_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
         >,
         #[compute(ignore_bound)]
-        #[css(ignore_bound)]
         to_list: Transform<
             TransformOperation<
                 Angle,
                 Number,
                 Length,
                 Integer,
                 LengthOrPercentage,
             >,
         >,
         count: Integer,
     },
 }
 
-#[derive(Animate, ToComputedValue)]
-#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
+#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
 /// A value of the `transform` property
-pub struct Transform<T>(pub Vec<T>);
+pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
 
 impl<Angle, Number, Length, Integer, LengthOrPercentage>
     TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
     /// Check if it is any translate function
     pub fn is_translate(&self) -> bool {
         use self::TransformOperation::*;
         match *self {
             Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => true,
@@ -510,37 +503,16 @@ where
                 // Note: DOMMatrix doesn't go into this arm.
                 Transform3D::identity()
             },
         };
         Ok(matrix)
     }
 }
 
-impl<T: ToCss> ToCss for Transform<T> {
-    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
-    where
-        W: Write,
-    {
-        if self.0.is_empty() {
-            return dest.write_str("none");
-        }
-
-        let mut first = true;
-        for operation in &self.0 {
-            if !first {
-                dest.write_str(" ")?;
-            }
-            first = false;
-            operation.to_css(dest)?
-        }
-        Ok(())
-    }
-}
-
 impl<T> Transform<T> {
     /// `none`
     pub fn none() -> Self {
         Transform(vec![])
     }
 }
 
 impl<T: ToMatrix> Transform<T> {
--- a/servo/components/style_derive/animate.rs
+++ b/servo/components/style_derive/animate.rs
@@ -27,26 +27,32 @@ pub fn derive(input: DeriveInput) -> Tok
         let (other_pattern, other_info) = cg::ref_pattern(&variant, "other");
         let (result_value, result_info) = cg::value(&variant, "result");
         let mut computations = quote!();
         let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
         computations.append_all(iter.map(|(result, (this, other))| {
             let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.ast());
             if field_attrs.constant {
                 if cg::is_parameterized(&result.ast().ty, &where_clause.params, None) {
-                    where_clause.add_predicate(cg::where_predicate(
-                        result.ast().ty.clone(),
-                        &parse_quote!(std::cmp::PartialEq),
-                        None,
-                    ));
-                    where_clause.add_predicate(cg::where_predicate(
-                        result.ast().ty.clone(),
-                        &parse_quote!(std::clone::Clone),
-                        None,
-                    ));
+                    cg::add_predicate(
+                        &mut where_clause.inner,
+                        cg::where_predicate(
+                            result.ast().ty.clone(),
+                            &parse_quote!(std::cmp::PartialEq),
+                            None,
+                        ),
+                    );
+                    cg::add_predicate(
+                        &mut where_clause.inner,
+                        cg::where_predicate(
+                            result.ast().ty.clone(),
+                            &parse_quote!(std::clone::Clone),
+                            None,
+                        ),
+                    );
                 }
                 quote! {
                     if #this != #other {
                         return Err(());
                     }
                     let #result = ::std::clone::Clone::clone(#this);
                 }
             } else {
--- a/servo/components/style_derive/cg.rs
+++ b/servo/components/style_derive/cg.rs
@@ -1,20 +1,20 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
 use darling::{FromDeriveInput, FromField, FromVariant};
 use quote::{ToTokens, Tokens};
 use std::collections::HashSet;
-use syn::{self, DeriveInput, Field, Ident};
-use syn::{ImplGenerics, Path, PathArguments, PathSegment, AngleBracketedGenericArguments, GenericParam};
-use syn::{QSelf, Type, TypeGenerics, TypeParam};
-use syn::{TypeSlice, TypeArray, TypeTuple, TypePath, TypeParen};
-use syn::{Variant, WherePredicate, GenericArgument, Binding};
+use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
+use syn::{GenericArgument, GenericParam, Ident, ImplGenerics, Path};
+use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGenerics};
+use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
+use syn::{Variant, WherePredicate};
 use syn::visit::{self, Visit};
 use synstructure::{self, BindingInfo, BindStyle, VariantAst, VariantInfo};
 
 pub struct WhereClause<'input, 'path> {
     pub inner: Option<syn::WhereClause>,
     pub params: Vec<&'input TypeParam>,
     trait_path: &'path Path,
     trait_output: Option<Ident>,
@@ -37,61 +37,60 @@ impl<'input, 'path> WhereClause<'input, 
         if !is_parameterized(&ty, &self.params, found.as_mut()) {
             return;
         }
         self.bounded_types.insert(ty.clone());
 
         let output = if let Some(output) = self.trait_output {
             output
         } else {
-            self.add_predicate(where_predicate(ty.clone(), trait_path, None));
+            add_predicate(&mut self.inner, where_predicate(ty.clone(), trait_path, None));
             return;
         };
 
         if let Type::Path(syn::TypePath { ref path, .. }) = *ty {
             if path_to_ident(path).is_some() {
-                self.add_predicate(where_predicate(ty.clone(), trait_path, None));
+                add_predicate(&mut self.inner, where_predicate(ty.clone(), trait_path, None));
                 return;
             }
         }
 
         let output_type = map_type_params(ty, &self.params, &mut |ident| {
             let ty = Type::Path(syn::TypePath { qself: None, path: ident.clone().into() });
             fmap_output_type(ty, trait_path, output)
         });
 
         let pred = where_predicate(
             ty.clone(),
             trait_path,
             Some((output, output_type)),
         );
 
-        self.add_predicate(pred);
+        add_predicate(&mut self.inner, pred);
 
         if let Some(found) = found {
             for ident in found {
                 let ty = Type::Path(syn::TypePath { qself: None, path: ident.into() });
                 if !self.bounded_types.contains(&ty) {
                     self.bounded_types.insert(ty.clone());
-                    self.add_predicate(
+                    add_predicate(
+                        &mut self.inner,
                         where_predicate(ty, trait_path, None),
                     );
                 };
             }
         }
     }
+}
 
-    pub fn add_predicate(&mut self, pred: WherePredicate) {
-        if let Some(ref mut inner) = self.inner {
-            inner.predicates.push(pred);
-        } else {
-            self.inner = Some(parse_quote!(where));
-            self.add_predicate(pred);
-        }
-    }
+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,
 ) -> Tokens
 where
--- a/servo/components/style_derive/to_animated_zero.rs
+++ b/servo/components/style_derive/to_animated_zero.rs
@@ -22,21 +22,24 @@ pub fn derive(input: syn::DeriveInput) -
         }
         let (mapped, mapped_bindings) = cg::value(variant, "mapped");
         let bindings_pairs = variant.bindings().into_iter().zip(mapped_bindings);
         let mut computations = quote!();
         computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| {
             let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.ast());
             if field_attrs.constant {
                 if cg::is_parameterized(&binding.ast().ty, &where_clause.params, None) {
-                    where_clause.add_predicate(cg::where_predicate(
-                        binding.ast().ty.clone(),
-                        &parse_quote!(std::clone::Clone),
-                        None,
-                    ));
+                    cg::add_predicate(
+                        &mut where_clause.inner,
+                        cg::where_predicate(
+                            binding.ast().ty.clone(),
+                            &parse_quote!(std::clone::Clone),
+                            None,
+                        ),
+                    );
                 }
                 quote! {
                     let #mapped_binding = ::std::clone::Clone::clone(#binding);
                 }
             } else {
                 where_clause.add_trait_bound(&binding.ast().ty);
                 quote! {
                     let #mapped_binding =
--- a/servo/components/style_derive/to_computed_value.rs
+++ b/servo/components/style_derive/to_computed_value.rs
@@ -35,21 +35,24 @@ pub fn derive(input: DeriveInput) -> Tok
             }
         }
     }
 
     let to_body = cg::fmap_match(&input, BindStyle::Ref, |binding| {
         let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast());
         if attrs.clone {
             if cg::is_parameterized(&binding.ast().ty, &where_clause.params, None) {
-                where_clause.add_predicate(cg::where_predicate(
-                    binding.ast().ty.clone(),
-                    &parse_quote!(std::clone::Clone),
-                    None,
-                ));
+                cg::add_predicate(
+                    &mut where_clause.inner,
+                    cg::where_predicate(
+                        binding.ast().ty.clone(),
+                        &parse_quote!(std::clone::Clone),
+                        None,
+                    ),
+                );
             }
             quote! { ::std::clone::Clone::clone(#binding) }
         } else {
             if !attrs.ignore_bound {
                 where_clause.add_trait_bound(&binding.ast().ty);
             }
             quote! {
                 ::values::computed::ToComputedValue::to_computed_value(#binding, context)
--- a/servo/components/style_derive/to_css.rs
+++ b/servo/components/style_derive/to_css.rs
@@ -1,45 +1,58 @@
 /* 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 http://mozilla.org/MPL/2.0/. */
 
-use cg::{self, WhereClause};
+use cg;
 use darling::util::Override;
 use quote::{ToTokens, Tokens};
-use syn::{self, Data, Path};
+use syn::{self, Data, GenericParam, Path, WhereClause};
 use synstructure::{BindingInfo, Structure, VariantInfo};
 
-pub fn derive(input: syn::DeriveInput) -> Tokens {
-    let name = &input.ident;
-    let trait_path = parse_quote!(::style_traits::ToCss);
-    let (impl_generics, ty_generics, mut where_clause) =
-        cg::trait_parts(&input, &trait_path);
+pub fn derive(mut input: syn::DeriveInput) -> Tokens {
+    let mut where_clause = input.generics.where_clause.take();
+    for param in &input.generics.params {
+        let param = match *param {
+            GenericParam::Type(ref param) => param,
+            _ => continue,
+        };
+        cg::add_predicate(
+            &mut where_clause,
+            parse_quote!(#param: ::style_traits::ToCss),
+        );
+    }
 
     let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
     if let Data::Enum(_) = input.data {
         assert!(input_attrs.function.is_none(), "#[css(function)] is not allowed on enums");
         assert!(!input_attrs.comma, "#[css(comma)] is not allowed on enums");
     }
-    let s = Structure::new(&input);
 
-    let match_body = s.each_variant(|variant| {
-        derive_variant_arm(variant, &mut where_clause)
-    });
+    let match_body = {
+        let s = Structure::new(&input);
+        s.each_variant(|variant| {
+            derive_variant_arm(variant, &mut where_clause)
+        })
+    };
+    input.generics.where_clause = where_clause;
+
+    let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 
     let mut impls = quote! {
         impl #impl_generics ::style_traits::ToCss for #name #ty_generics #where_clause {
             #[allow(unused_variables)]
             #[inline]
             fn to_css<W>(
                 &self,
                 dest: &mut ::style_traits::CssWriter<W>,
             ) -> ::std::fmt::Result
             where
-                W: ::std::fmt::Write
+                W: ::std::fmt::Write,
             {
                 match *self {
                     #match_body
                 }
             }
         }
     };
 
@@ -56,17 +69,17 @@ pub fn derive(input: syn::DeriveInput) -
         });
     }
 
     impls
 }
 
 fn derive_variant_arm(
     variant: &VariantInfo,
-    where_clause: &mut WhereClause,
+    generics: &mut Option<WhereClause>,
 ) -> Tokens {
     let bindings = variant.bindings();
     let identifier = cg::to_css_identifier(variant.ast().ident.as_ref());
     let ast = variant.ast();
     let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&ast);
     let separator = if variant_attrs.comma { ", " } else { " " };
 
     if variant_attrs.dimension {
@@ -78,17 +91,17 @@ fn derive_variant_arm(
     }
 
     let mut expr = if let Some(keyword) = variant_attrs.keyword {
         assert!(bindings.is_empty());
         quote! {
             ::std::fmt::Write::write_str(dest, #keyword)
         }
     } else if !bindings.is_empty() {
-        derive_variant_fields_expr(bindings, where_clause, separator)
+        derive_variant_fields_expr(bindings, generics, separator)
     } else {
         quote! {
             ::std::fmt::Write::write_str(dest, #identifier)
         }
     };
 
     if variant_attrs.dimension {
         expr = quote! {
@@ -104,34 +117,35 @@ fn derive_variant_arm(
             ::std::fmt::Write::write_str(dest, ")")
         }
     }
     expr
 }
 
 fn derive_variant_fields_expr(
     bindings: &[BindingInfo],
-    where_clause: &mut WhereClause,
+    where_clause: &mut Option<WhereClause>,
     separator: &str,
 ) -> Tokens {
     let mut iter = bindings.iter().filter_map(|binding| {
         let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());
         if attrs.skip {
             return None;
         }
         Some((binding, attrs))
     }).peekable();
 
     let (first, attrs) = match iter.next() {
         Some(pair) => pair,
         None => return quote! { Ok(()) },
     };
     if !attrs.iterable && iter.peek().is_none() {
-        if !attrs.ignore_bound {
-            where_clause.add_trait_bound(&first.ast().ty);
+        if attrs.field_bound {
+            let ty = &first.ast().ty;
+            cg::add_predicate(where_clause, parse_quote!(#ty: ::style_traits::ToCss));
         }
         let mut expr = quote! { ::style_traits::ToCss::to_css(#first, dest) };
         if let Some(condition) = attrs.skip_if {
             expr = quote! {
                 if !#condition(#first) {
                     #expr
                 }
             }
@@ -149,17 +163,17 @@ fn derive_variant_fields_expr(
         #expr
         Ok(())
     }}
 }
 
 fn derive_single_field_expr(
     field: &BindingInfo,
     attrs: CssFieldAttrs,
-    where_clause: &mut WhereClause,
+    where_clause: &mut Option<WhereClause>,
 ) -> Tokens {
     let mut expr = if attrs.iterable {
         if let Some(if_empty) = attrs.if_empty {
             return quote! {
                 {
                     let mut iter = #field.iter().peekable();
                     if iter.peek().is_none() {
                         writer.item(&::style_traits::values::Verbatim(#if_empty))?;
@@ -172,18 +186,19 @@ fn derive_single_field_expr(
             };
         }
         quote! {
             for item in #field.iter() {
                 writer.item(&item)?;
             }
         }
     } else {
-        if !attrs.ignore_bound {
-            where_clause.add_trait_bound(&field.ast().ty);
+        if attrs.field_bound {
+            let ty = &field.ast().ty;
+            cg::add_predicate(where_clause, parse_quote!(#ty: ::style_traits::ToCss));
         }
         quote! { writer.item(#field)?; }
     };
 
     if let Some(condition) = attrs.skip_if {
         expr = quote! {
             if !#condition(#field) {
                 #expr
@@ -213,13 +228,13 @@ pub struct CssVariantAttrs {
     pub keyword: Option<String>,
     pub aliases: Option<String>,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromField)]
 struct CssFieldAttrs {
     if_empty: Option<String>,
-    ignore_bound: bool,
+    field_bound: bool,
     iterable: bool,
     skip: bool,
     skip_if: Option<Path>,
 }