Skip to content

Commit

Permalink
Alternative implementation
Browse files Browse the repository at this point in the history
This alternative implementation uses iterators and vectors, but does not
require `Copy`.
  • Loading branch information
matthiasgoergens committed Mar 20, 2024
1 parent 9250753 commit ad97466
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 39 deletions.
1 change: 1 addition & 0 deletions impl/src/add_assign_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream {
impl #impl_generics ::derive_more::#trait_ident for #input_type #ty_generics #where_clause {
#[inline]
fn #method_ident(&mut self, rhs: #input_type #ty_generics) {
let lhs: &mut Self = self;
#( #exprs; )*
}
}
Expand Down
33 changes: 14 additions & 19 deletions impl/src/add_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use proc_macro2::{Span, TokenStream};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Field, Ident, Index, Type, TypeArray, TypeTuple};

pub fn tuple_exprs(fields: &[&Field], method_ident: &Ident) -> Vec<TokenStream> {
let fields: Vec<&Type> = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
inner_tuple_exprs(0, &quote! {}, &fields, method_ident)
inner_tuple_exprs(&quote! {}, &fields, method_ident)
}

pub fn struct_exprs(fields: &[&Field], method_ident: &Ident) -> Vec<TokenStream> {
Expand All @@ -13,14 +13,12 @@ pub fn struct_exprs(fields: &[&Field], method_ident: &Ident) -> Vec<TokenStream>
.map(|field| {
// It's safe to unwrap because struct fields always have an identifier
let field_path = field.ident.as_ref().unwrap();
elem_content(0, &quote! { .#field_path }, &field.ty, method_ident)
elem_content(&quote! { .#field_path }, &field.ty, method_ident)
})
.collect()
}

pub fn inner_tuple_exprs(
// `depth` is needed for `index_var` generation for nested arrays
depth: usize,
field_path: &TokenStream,
fields: &[&Type],
method_ident: &Ident,
Expand All @@ -30,40 +28,37 @@ pub fn inner_tuple_exprs(
.enumerate()
.map(|(i, ty)| {
let i = Index::from(i);
elem_content(depth + 1, &quote! { #field_path.#i }, ty, method_ident)
elem_content(&quote! { #field_path.#i }, ty, method_ident)
})
.collect()
}

pub fn elem_content(
depth: usize,
field_path: &TokenStream,
ty: &Type,
method_ident: &Ident,
) -> TokenStream {
match ty {
Type::Array(TypeArray { elem, .. }) => {
let index_var = Ident::new(&format!("i{}", depth), Span::call_site());
let fn_body = elem_content(
depth + 1,
&quote! { #field_path[#index_var] },
elem.as_ref(),
method_ident,
);
let fn_body = elem_content(&quote! {}, elem.as_ref(), method_ident);

// generates `core::array::from_fn(|i0| self.x[i0].add(rhs.x[i0]))`
quote! { core::array::from_fn(|#index_var| #fn_body) }
quote! {
lhs #field_path.into_iter().zip(rhs #field_path.into_iter())
.map(|(lhs, rhs)| #fn_body)
.collect::<Vec<_>>()
.try_into()
.unwrap_or_else(|_| unreachable!("Lengths should always match."))
}
}
Type::Tuple(TypeTuple { elems, .. }) => {
let exprs = inner_tuple_exprs(
depth + 1,
field_path,
&elems.iter().collect::<Vec<_>>(),
method_ident,
);
quote! { (#(#exprs),*) }
}
// generates `self.x.add(rhs.x)`
_ => quote! { self #field_path.#method_ident(rhs #field_path) },
// generates `lhs.x.add(rhs.x)`
_ => quote! { lhs #field_path.#method_ident(rhs #field_path) },
}
}
3 changes: 2 additions & 1 deletion impl/src/add_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub fn expand(input: &DeriveInput, trait_name: &str) -> TokenStream {

#[inline]
fn #method_ident(self, rhs: #input_type #ty_generics) -> #output_type {
let lhs: Self = self;
#block
}
}
Expand Down Expand Up @@ -149,7 +150,7 @@ fn enum_content(
});
}
quote! {
match (self, rhs) {
match (lhs, rhs) {
#(#matches),*
}
}
Expand Down
38 changes: 19 additions & 19 deletions impl/src/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,30 +237,30 @@ impl Parse for FieldAttribute {
}
}

type Untyped = Either<attr::Skip, Either<attr::Empty, ConversionsAttribute>>;
impl From<Untyped> for FieldAttribute {
fn from(v: Untyped) -> Self {
match v {
Untyped::Left(skip) => Self {
skip: Some(skip),
convs: None,
},
Untyped::Right(c) => Self {
skip: None,
convs: Some(match c {
Either::Left(_empty) => ConversionsAttribute::default(),
Either::Right(convs) => convs,
}),
},
}
}
}

impl attr::ParseMultiple for FieldAttribute {
fn parse_attr_with<P: attr::Parser>(
attr: &syn::Attribute,
parser: &P,
) -> syn::Result<Self> {
type Untyped = Either<attr::Skip, Either<attr::Empty, ConversionsAttribute>>;
impl From<Untyped> for FieldAttribute {
fn from(v: Untyped) -> Self {
match v {
Untyped::Left(skip) => Self {
skip: Some(skip),
convs: None,
},
Untyped::Right(c) => Self {
skip: None,
convs: Some(match c {
Either::Left(_empty) => ConversionsAttribute::default(),
Either::Right(convs) => convs,
}),
},
}
}
}

Untyped::parse_attr_with(attr, parser).map(Self::from)
}

Expand Down
5 changes: 5 additions & 0 deletions tests/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ fn test_sanity() {

#[derive(Add)]
struct TupleRecursive((i32, u8), [(i32, u8); 10]);

#[derive(Add)]
pub struct GenericArrayStruct<T> {
pub a: [T; 2],
}

0 comments on commit ad97466

Please sign in to comment.