diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index ad81a4ecc4a..9e0653d8a00 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -35,6 +35,9 @@ pub mod effects; /// /// If the two values are not similar, an error is returned unless a fallback /// function has been specified through `#[animate(fallback)]`. +/// +/// Trait bounds for type parameter `Foo` can be opted out of with +/// `#[animation(no_bound(Foo))]` on the type definition. pub trait Animate: Sized { /// Animate a value towards another one, given an animation procedure. fn animate(&self, other: &Self, procedure: Procedure) -> Result; @@ -78,6 +81,9 @@ pub trait ToAnimatedValue { /// /// If a variant is annotated with `#[animation(error)]`, the corresponding /// `match` arm is not generated. +/// +/// Trait bounds for type parameter `Foo` can be opted out of with +/// `#[animation(no_bound(Foo))]` on the type definition. pub trait ToAnimatedZero: Sized { /// Returns a value that, when added with an underlying value, will produce the underlying /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from diff --git a/components/style/values/distance.rs b/components/style/values/distance.rs index f17b9931dda..5f84f17caad 100644 --- a/components/style/values/distance.rs +++ b/components/style/values/distance.rs @@ -21,6 +21,10 @@ use std::ops::Add; /// /// If the two values are not similar, an error is returned unless a fallback /// function has been specified through `#[distance(fallback)]`. +/// +/// Trait bounds for type parameter `Foo` can be opted out of with +/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for +/// fields can be opted into with `#[distance(field_bound)]` on the field. pub trait ComputeSquaredDistance { /// Computes the squared distance between two animatable values. fn compute_squared_distance(&self, other: &Self) -> Result; diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index ef9eac14ec7..8c798b468ae 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -43,6 +43,7 @@ pub enum ShapeBox { /// A shape source, for some reference box. #[allow(missing_docs)] +#[animation(no_bound(ImageOrUrl))] #[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] pub enum ShapeSource { #[animation(error)] diff --git a/components/style/values/generics/svg.rs b/components/style/values/generics/svg.rs index 6d9928ec1af..a856633ff47 100644 --- a/components/style/values/generics/svg.rs +++ b/components/style/values/generics/svg.rs @@ -15,6 +15,7 @@ use values::distance::{ComputeSquaredDistance, SquaredDistance}; /// An SVG paint value /// /// +#[animation(no_bound(UrlPaintServer))] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)] #[derive(ToAnimatedValue, ToComputedValue, ToCss)] pub struct SVGPaint { @@ -29,6 +30,7 @@ pub struct SVGPaint { /// Whereas the spec only allows PaintServer /// to have a fallback, Gecko lets the context /// properties have a fallback as well. +#[animation(no_bound(UrlPaintServer))] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)] #[derive(ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)] pub enum SVGPaintKind { @@ -203,7 +205,11 @@ pub enum SVGLength { pub enum SVGStrokeDashArray { /// `[ | | ]#` #[css(comma)] - Values(#[css(if_empty = "none", iterable)] Vec), + Values( + #[css(if_empty = "none", iterable)] + #[distance(field_bound)] + Vec, + ), /// `context-value` ContextValue, } diff --git a/components/style_derive/animate.rs b/components/style_derive/animate.rs index 912f7dbc9f2..e54f1819c99 100644 --- a/components/style_derive/animate.rs +++ b/components/style_derive/animate.rs @@ -2,23 +2,31 @@ * 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::IdentList; use quote::Tokens; use syn::{DeriveInput, Path}; use synstructure::{Structure, VariantInfo}; -pub fn derive(input: DeriveInput) -> Tokens { - let name = &input.ident; - let trait_path = parse_quote!(values::animated::Animate); - let (impl_generics, ty_generics, mut where_clause) = - cg::trait_parts(&input, &trait_path); +pub fn derive(mut input: DeriveInput) -> Tokens { + let animation_input_attrs = cg::parse_input_attrs::(&input); + let no_bound = animation_input_attrs.no_bound.unwrap_or_default(); + let mut where_clause = input.generics.where_clause.take(); + for param in input.generics.type_params() { + if !no_bound.contains(¶m.ident) { + cg::add_predicate( + &mut where_clause, + parse_quote!(#param: ::values::animated::Animate), + ); + } + } + input.generics.where_clause = where_clause; - let input_attrs = cg::parse_input_attrs::(&input); let s = Structure::new(&input); let mut append_error_clause = s.variants().len() > 1; let mut match_body = s.variants().iter().fold(quote!(), |body, variant| { - let arm = match derive_variant_arm(variant, &mut where_clause) { + let arm = match derive_variant_arm(variant) { Ok(arm) => arm, Err(()) => { append_error_clause = true; @@ -29,6 +37,7 @@ pub fn derive(input: DeriveInput) -> Tokens { }); if append_error_clause { + let input_attrs = cg::parse_input_attrs::(&input); if let Some(fallback) = input_attrs.fallback { match_body.append_all(quote! { (this, other) => #fallback(this, other, procedure) @@ -38,6 +47,9 @@ pub fn derive(input: DeriveInput) -> Tokens { } } + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + quote! { impl #impl_generics ::values::animated::Animate for #name #ty_generics #where_clause { #[allow(unused_variables, unused_imports)] @@ -55,10 +67,7 @@ pub fn derive(input: DeriveInput) -> Tokens { } } -fn derive_variant_arm( - variant: &VariantInfo, - where_clause: &mut WhereClause, -) -> Result { +fn derive_variant_arm(variant: &VariantInfo) -> Result { let variant_attrs = cg::parse_variant_attrs::(&variant.ast()); if variant_attrs.error { return Err(()); @@ -78,7 +87,6 @@ fn derive_variant_arm( let #result = ::std::clone::Clone::clone(#this); } } else { - where_clause.add_trait_bound(&result.ast().ty); quote! { let #result = ::values::animated::Animate::animate(#this, #other, procedure)?; @@ -99,10 +107,19 @@ struct AnimateInputAttrs { fallback: Option, } +#[darling(attributes(animation), default)] +#[derive(Default, FromDeriveInput)] +pub struct AnimationInputAttrs { + pub no_bound: Option, +} + #[darling(attributes(animation), default)] #[derive(Default, FromVariant)] pub struct AnimationVariantAttrs { pub error: bool, + // Only here because of structs, where the struct definition acts as a + // variant itself. + pub no_bound: Option, } #[darling(attributes(animation), default)] diff --git a/components/style_derive/cg.rs b/components/style_derive/cg.rs index 694973103ce..ff391c79180 100644 --- a/components/style_derive/cg.rs +++ b/components/style_derive/cg.rs @@ -3,83 +3,14 @@ * 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 quote::Tokens; use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field}; -use syn::{GenericArgument, GenericParam, Ident, ImplGenerics, Path}; -use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGenerics}; +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 syn::visit::{self, Visit}; use synstructure::{self, BindingInfo, BindStyle, VariantAst, VariantInfo}; -pub struct WhereClause<'input, 'path> { - pub inner: Option, - pub params: Vec<&'input TypeParam>, - trait_path: &'path Path, - trait_output: Option, - bounded_types: HashSet, -} - -impl<'input, 'path> ToTokens for WhereClause<'input, 'path> { - fn to_tokens(&self, tokens: &mut Tokens) { - self.inner.to_tokens(tokens); - } -} - -impl<'input, 'path> WhereClause<'input, 'path> { - pub fn add_trait_bound(&mut self, ty: &Type) { - let trait_path = self.trait_path; - let mut found = self.trait_output.map(|_| HashSet::new()); - if self.bounded_types.contains(&ty) { - return; - } - 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 { - 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() { - add_predicate(&mut self.inner, where_predicate(ty.clone(), trait_path, None)); - return; - } - } - - let output_type = map_type_params(ty, &self.params, &mut |ident| { - parse_quote!(<#ident as ::#trait_path>::#output) - }); - - let pred = where_predicate( - ty.clone(), - trait_path, - Some((output, output_type)), - ); - - 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()); - add_predicate( - &mut self.inner, - where_predicate(ty, trait_path, None), - ); - }; - } - } - } -} - pub fn add_predicate( where_clause: &mut Option, pred: WherePredicate, @@ -139,36 +70,6 @@ pub fn fmap_trait_output( segment.into() } -pub fn is_parameterized( - ty: &Type, - params: &[&TypeParam], - found: Option<&mut HashSet>, -) -> bool { - struct IsParameterized<'a, 'b> { - params: &'a [&'a TypeParam], - has_free: bool, - found: Option<&'b mut HashSet>, - } - - impl<'a, 'b, 'ast> Visit<'ast> for IsParameterized<'a, 'b> { - fn visit_path(&mut self, path: &'ast Path) { - if let Some(ident) = path_to_ident(path) { - if self.params.iter().any(|param| param.ident == ident) { - self.has_free = true; - if let Some(ref mut found) = self.found { - found.insert(ident.clone()); - } - } - } - visit::visit_path(self, path); - } - } - - let mut visitor = IsParameterized { params, has_free: false, found }; - visitor.visit_type(ty); - visitor.has_free -} - pub fn map_type_params(ty: &Type, params: &[&TypeParam], f: &mut F) -> Type where F: FnMut(&Ident) -> Type, @@ -314,33 +215,6 @@ pub fn ref_pattern<'a>( (v.pat(), v.bindings().iter().cloned().collect()) } -pub fn trait_parts<'input, 'path>( - input: &'input DeriveInput, - trait_path: &'path Path, -) -> (ImplGenerics<'input>, TypeGenerics<'input>, WhereClause<'input, 'path>) { - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let where_clause = WhereClause { - inner: where_clause.cloned(), - params: input.generics.type_params().into_iter().collect::>(), - trait_path, - trait_output: None, - bounded_types: HashSet::new() - }; - (impl_generics, ty_generics, where_clause) -} - -fn trait_ref(path: &Path, output: Option<(Ident, Type)>) -> Path { - let segments = path.segments.iter().collect::>(); - let (name, parent) = segments.split_last().unwrap(); - - let last_segment: PathSegment = if let Some((param, ty)) = output { - parse_quote!(#name<#param = #ty>) - } else { - parse_quote!(#name) - }; - parse_quote!(::#(#parent::)*#last_segment) -} - pub fn value<'a>( variant: &'a VariantInfo, prefix: &str, @@ -351,15 +225,6 @@ pub fn value<'a>( (v.pat(), v.bindings().iter().cloned().collect()) } -pub fn where_predicate( - bounded_ty: Type, - trait_path: &Path, - trait_output: Option<(Ident, Type)>, -) -> WherePredicate { - let trait_ref = trait_ref(trait_path, trait_output); - parse_quote!(#bounded_ty: #trait_ref) -} - /// Transforms "FooBar" to "foo-bar". /// /// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string diff --git a/components/style_derive/compute_squared_distance.rs b/components/style_derive/compute_squared_distance.rs index 6254340e358..07408bbcddb 100644 --- a/components/style_derive/compute_squared_distance.rs +++ b/components/style_derive/compute_squared_distance.rs @@ -2,52 +2,71 @@ * 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 animate::AnimationVariantAttrs; +use animate::{AnimationInputAttrs, AnimationVariantAttrs}; use cg; use quote::Tokens; use syn::{DeriveInput, Path}; use synstructure; -pub fn derive(input: DeriveInput) -> Tokens { - let name = &input.ident; - let trait_path = parse_quote!(values::distance::ComputeSquaredDistance); - let (impl_generics, ty_generics, mut where_clause) = - cg::trait_parts(&input, &trait_path); - - let input_attrs = cg::parse_input_attrs::(&input); - let s = synstructure::Structure::new(&input); - let mut append_error_clause = s.variants().len() > 1; - - let mut match_body = s.variants().iter().fold(quote!(), |body, variant| { - let attrs = cg::parse_variant_attrs::(&variant.ast()); - if attrs.error { - append_error_clause = true; - return body; +pub fn derive(mut input: DeriveInput) -> Tokens { + let animation_input_attrs = cg::parse_input_attrs::(&input); + let no_bound = animation_input_attrs.no_bound.unwrap_or_default(); + let mut where_clause = input.generics.where_clause.take(); + for param in input.generics.type_params() { + if !no_bound.contains(¶m.ident) { + cg::add_predicate( + &mut where_clause, + parse_quote!(#param: ::values::distance::ComputeSquaredDistance), + ); } + } - let (this_pattern, this_info) = cg::ref_pattern(&variant, "this"); - let (other_pattern, other_info) = cg::ref_pattern(&variant, "other"); - let sum = if this_info.is_empty() { - quote! { ::values::distance::SquaredDistance::from_sqrt(0.) } - } else { - let mut sum = quote!(); - sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| { - where_clause.add_trait_bound(&this.ast().ty); - quote! { - ::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)? - } - }), quote!(+)); - sum - }; - quote! { - #body - (&#this_pattern, &#other_pattern) => { - Ok(#sum) + let (mut match_body, append_error_clause) = { + let s = synstructure::Structure::new(&input); + let mut append_error_clause = s.variants().len() > 1; + + let match_body = s.variants().iter().fold(quote!(), |body, variant| { + let attrs = cg::parse_variant_attrs::(&variant.ast()); + if attrs.error { + append_error_clause = true; + return body; } - } - }); + + let (this_pattern, this_info) = cg::ref_pattern(&variant, "this"); + let (other_pattern, other_info) = cg::ref_pattern(&variant, "other"); + let sum = if this_info.is_empty() { + quote! { ::values::distance::SquaredDistance::from_sqrt(0.) } + } else { + let mut sum = quote!(); + sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| { + let field_attrs = cg::parse_field_attrs::(&this.ast()); + if field_attrs.field_bound { + let ty = &this.ast().ty; + cg::add_predicate( + &mut where_clause, + parse_quote!(#ty: ::values::distance::ComputeSquaredDistance), + ); + } + quote! { + ::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)? + } + }), quote!(+)); + sum + }; + quote! { + #body + (&#this_pattern, &#other_pattern) => { + Ok(#sum) + } + } + }); + + (match_body, append_error_clause) + }; + input.generics.where_clause = where_clause; if append_error_clause { + let input_attrs = cg::parse_input_attrs::(&input); if let Some(fallback) = input_attrs.fallback { match_body.append_all(quote! { (this, other) => #fallback(this, other) @@ -57,6 +76,9 @@ pub fn derive(input: DeriveInput) -> Tokens { } } + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + quote! { impl #impl_generics ::values::distance::ComputeSquaredDistance for #name #ty_generics #where_clause { #[allow(unused_variables, unused_imports)] @@ -78,3 +100,9 @@ pub fn derive(input: DeriveInput) -> Tokens { struct DistanceInputAttrs { fallback: Option, } + +#[darling(attributes(distance), default)] +#[derive(Default, FromField)] +struct DistanceFieldAttrs { + field_bound: bool, +} diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs index e414a3bd3f2..e9d799fe9a9 100644 --- a/components/style_derive/lib.rs +++ b/components/style_derive/lib.rs @@ -45,7 +45,7 @@ pub fn derive_parse(stream: TokenStream) -> TokenStream { parse::derive(input).into() } -#[proc_macro_derive(ToAnimatedZero, attributes(animation))] +#[proc_macro_derive(ToAnimatedZero, attributes(animation, zero))] pub fn derive_to_animated_zero(stream: TokenStream) -> TokenStream { let input = syn::parse(stream).unwrap(); to_animated_zero::derive(input).into() diff --git a/components/style_derive/to_animated_zero.rs b/components/style_derive/to_animated_zero.rs index 8094e46874d..9ee786f7ec8 100644 --- a/components/style_derive/to_animated_zero.rs +++ b/components/style_derive/to_animated_zero.rs @@ -2,20 +2,26 @@ * 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 animate::{AnimationVariantAttrs, AnimationFieldAttrs}; +use animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantAttrs}; use cg; use quote; use syn; use synstructure; -pub fn derive(input: syn::DeriveInput) -> quote::Tokens { - let name = &input.ident; - let trait_path = parse_quote!(values::animated::ToAnimatedZero); - let (impl_generics, ty_generics, mut where_clause) = - cg::trait_parts(&input, &trait_path); +pub fn derive(mut input: syn::DeriveInput) -> quote::Tokens { + let animation_input_attrs = cg::parse_input_attrs::(&input); + let no_bound = animation_input_attrs.no_bound.unwrap_or_default(); + let mut where_clause = input.generics.where_clause.take(); + for param in input.generics.type_params() { + if !no_bound.contains(¶m.ident) { + cg::add_predicate( + &mut where_clause, + parse_quote!(#param: ::values::animated::ToAnimatedZero), + ); + } + } - let s = synstructure::Structure::new(&input); - let to_body = s.each_variant(|variant| { + let to_body = synstructure::Structure::new(&input).each_variant(|variant| { let attrs = cg::parse_variant_attrs::(&variant.ast()); if attrs.error { return Some(quote! { Err(()) }); @@ -26,21 +32,10 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| { let field_attrs = cg::parse_field_attrs::(&binding.ast()); if field_attrs.constant { - if cg::is_parameterized(&binding.ast().ty, &where_clause.params, 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 = ::values::animated::ToAnimatedZero::to_animated_zero(#binding)?; @@ -50,6 +45,10 @@ pub fn derive(input: syn::DeriveInput) -> quote::Tokens { computations.append_all(quote! { Ok(#mapped) }); Some(computations) }); + input.generics.where_clause = where_clause; + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); quote! { impl #impl_generics ::values::animated::ToAnimatedZero for #name #ty_generics #where_clause {