servo: Merge #20275 - Finish cleaning up trait bounds generation in style_derive (from servo:derive-all-the-things); r=emilio

Source-Repo: https://github.com/servo/servo
Source-Revision: 9d4cdfcfc31e4d3fca16e074c7481211f880be9f

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : d9b39c62cbcc55b8ae6297c250a12cab2fb2f046
This commit is contained in:
Anthony Ramine 2018-03-12 04:52:02 -04:00
parent 84157e8157
commit 2f3c2434f4
9 changed files with 134 additions and 208 deletions

View File

@ -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<Self, ()>;
@ -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

View File

@ -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<SquaredDistance, ()>;

View File

@ -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<BasicShape, ReferenceBox, ImageOrUrl> {
#[animation(error)]

View File

@ -15,6 +15,7 @@ use values::distance::{ComputeSquaredDistance, SquaredDistance};
/// An SVG paint value
///
/// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>
#[animation(no_bound(UrlPaintServer))]
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq)]
#[derive(ToAnimatedValue, ToComputedValue, ToCss)]
pub struct SVGPaint<ColorType, UrlPaintServer> {
@ -29,6 +30,7 @@ pub struct SVGPaint<ColorType, UrlPaintServer> {
/// 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<ColorType, UrlPaintServer> {
@ -203,7 +205,11 @@ pub enum SVGLength<LengthType> {
pub enum SVGStrokeDashArray<LengthType> {
/// `[ <length> | <percentage> | <number> ]#`
#[css(comma)]
Values(#[css(if_empty = "none", iterable)] Vec<LengthType>),
Values(
#[css(if_empty = "none", iterable)]
#[distance(field_bound)]
Vec<LengthType>,
),
/// `context-value`
ContextValue,
}

View File

@ -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::<AnimationInputAttrs>(&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(&param.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::<AnimateInputAttrs>(&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::<AnimateInputAttrs>(&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<Tokens, ()> {
fn derive_variant_arm(variant: &VariantInfo) -> Result<Tokens, ()> {
let variant_attrs = cg::parse_variant_attrs::<AnimationVariantAttrs>(&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<Path>,
}
#[darling(attributes(animation), default)]
#[derive(Default, FromDeriveInput)]
pub struct AnimationInputAttrs {
pub no_bound: Option<IdentList>,
}
#[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<IdentList>,
}
#[darling(attributes(animation), default)]

View File

@ -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<syn::WhereClause>,
pub params: Vec<&'input TypeParam>,
trait_path: &'path Path,
trait_output: Option<Ident>,
bounded_types: HashSet<Type>,
}
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<syn::WhereClause>,
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<Ident>>,
) -> bool {
struct IsParameterized<'a, 'b> {
params: &'a [&'a TypeParam],
has_free: bool,
found: Option<&'b mut HashSet<Ident>>,
}
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<F>(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::<Vec<&TypeParam>>(),
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::<Vec<&PathSegment>>();
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

View File

@ -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::<DistanceInputAttrs>(&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::<AnimationVariantAttrs>(&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::<AnimationInputAttrs>(&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(&param.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::<AnimationVariantAttrs>(&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::<DistanceFieldAttrs>(&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::<DistanceInputAttrs>(&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<Path>,
}
#[darling(attributes(distance), default)]
#[derive(Default, FromField)]
struct DistanceFieldAttrs {
field_bound: bool,
}

View File

@ -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()

View File

@ -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::<AnimationInputAttrs>(&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(&param.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::<AnimationVariantAttrs>(&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::<AnimationFieldAttrs>(&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 {