Skip to content

Commit

Permalink
Use optimized output for Clone and PartialOrd with custom bounds (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda authored Dec 15, 2023
1 parent a47e8ed commit 567f20b
Show file tree
Hide file tree
Showing 18 changed files with 209 additions and 84 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed
- Use the `Copy` implementation for `Clone` and the `Ord` implementation for
`PartialOrd` when custom bounds are present.

## [1.2.7] - 2023-12-14

### Fixed
Expand Down
13 changes: 5 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ fn generate_impl(
let mut where_clause = where_clause.map(Cow::Borrowed);
derive_where.where_clause(&mut where_clause, trait_, item);

let body = generate_body(derive_where, &derive_where.traits, trait_, item, generics);
let body = generate_body(derive_where, trait_, item, generics);

let ident = item.ident();
let path = trait_.impl_path(trait_);
Expand Down Expand Up @@ -666,25 +666,22 @@ fn generate_impl(
/// Generate implementation method body for a [`Trait`].
fn generate_body(
derive_where: &DeriveWhere,
traits: &[DeriveTrait],
trait_: &DeriveTrait,
item: &Item,
generics: &SplitGenerics<'_>,
) -> TokenStream {
let any_bound = !derive_where.generics.is_empty();

match &item {
Item::Item(data) => {
let body = trait_.build_body(any_bound, traits, trait_, data);
trait_.build_signature(any_bound, item, generics, traits, trait_, &body)
let body = trait_.build_body(derive_where, trait_, data);
trait_.build_signature(derive_where, item, generics, trait_, &body)
}
Item::Enum { variants, .. } => {
let body: TokenStream = variants
.iter()
.map(|data| trait_.build_body(any_bound, traits, trait_, data))
.map(|data| trait_.build_body(derive_where, trait_, data))
.collect();

trait_.build_signature(any_bound, item, generics, traits, trait_, &body)
trait_.build_signature(derive_where, item, generics, trait_, &body)
}
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/test/bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use super::test_derive;
fn bound() -> Result<()> {
test_derive(
quote! {
#[derive_where(Clone; T)]
#[derive_where(Clone, Copy; T)]
struct Test<T, U>(T, std::marker::PhantomData<U>);
},
quote! {
Expand All @@ -22,6 +22,11 @@ fn bound() -> Result<()> {
}
}
}

#[automatically_derived]
impl<T, U> ::core::marker::Copy for Test<T, U>
where T: ::core::marker::Copy
{ }
},
)
}
Expand All @@ -30,7 +35,7 @@ fn bound() -> Result<()> {
fn bound_multiple() -> Result<()> {
test_derive(
quote! {
#[derive_where(Clone; T, U)]
#[derive_where(Clone, Copy; T, U)]
struct Test<T, U, V>((T, U), std::marker::PhantomData<V>);
},
quote! {
Expand All @@ -47,6 +52,13 @@ fn bound_multiple() -> Result<()> {
}
}
}

#[automatically_derived]
impl<T, U, V> ::core::marker::Copy for Test<T, U, V>
where
T: ::core::marker::Copy,
U: ::core::marker::Copy
{ }
},
)
}
Expand Down
51 changes: 51 additions & 0 deletions src/test/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,59 @@ fn union_() -> Result<()> {
fn clone(&self) -> Self {
struct __AssertCopy<__T: ::core::marker::Copy + ?::core::marker::Sized>(::core::marker::PhantomData<__T>);
let _: __AssertCopy<Self>;
*self
}
}
},
)
}

#[test]
fn no_bound() -> Result<()> {
test_derive(
quote! {
#[derive_where(Clone, Copy)]
struct Test<T>(std::marker::PhantomData<T>);
},
quote! {
#[automatically_derived]
impl<T> ::core::clone::Clone for Test<T>
{
#[inline]
fn clone(&self) -> Self {
*self
}
}

#[automatically_derived]
impl<T> ::core::marker::Copy for Test<T>
{ }
},
)
}

#[test]
fn custom_bound() -> Result<()> {
test_derive(
quote! {
#[derive_where(Clone, Copy; T: Trait)]
struct Test<T>(T);
},
quote! {
#[automatically_derived]
impl<T> ::core::clone::Clone for Test<T>
where T: Trait
{
#[inline]
fn clone(&self) -> Self {
*self
}
}

#[automatically_derived]
impl<T> ::core::marker::Copy for Test<T>
where T: Trait
{ }
},
)
}
1 change: 1 addition & 0 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod basic;
mod bound;
mod clone;
mod discriminant;
mod enum_;
#[cfg(not(any(feature = "nightly", feature = "safe")))]
Expand Down
70 changes: 70 additions & 0 deletions src/test/partial_ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,73 @@ fn bound() -> Result<()> {
},
)
}

#[test]
fn no_bound() -> Result<()> {
test_derive(
quote! {
#[derive_where(Ord, PartialOrd)]
struct Test<T>(std::marker::PhantomData<T>);
},
quote! {
#[automatically_derived]
impl<T> ::core::cmp::Ord for Test<T> {
#[inline]
fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering {
match (self, __other) {
(Test(ref __field_0), Test(ref __other_field_0)) =>
match ::core::cmp::Ord::cmp(__field_0, __other_field_0) {
::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal,
__cmp => __cmp,
},
}
}
}

#[automatically_derived]
impl<T> ::core::cmp::PartialOrd for Test<T> {
#[inline]
fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, __other))
}
}
},
)
}

#[test]
fn custom_bound() -> Result<()> {
test_derive(
quote! {
#[derive_where(Ord, PartialOrd; T: TestTrait)]
struct Test<T>(std::marker::PhantomData<T>);
},
quote! {
#[automatically_derived]
impl<T> ::core::cmp::Ord for Test<T>
where T: TestTrait
{
#[inline]
fn cmp(&self, __other: &Self) -> ::core::cmp::Ordering {
match (self, __other) {
(Test(ref __field_0), Test(ref __other_field_0)) =>
match ::core::cmp::Ord::cmp(__field_0, __other_field_0) {
::core::cmp::Ordering::Equal => ::core::cmp::Ordering::Equal,
__cmp => __cmp,
},
}
}
}

#[automatically_derived]
impl<T> ::core::cmp::PartialOrd for Test<T>
where T: TestTrait
{
#[inline]
fn partial_cmp(&self, __other: &Self) -> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, __other))
}
}
},
)
}
19 changes: 7 additions & 12 deletions src/trait_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod zeroize_on_drop;
use proc_macro2::{Span, TokenStream};
use syn::{punctuated::Punctuated, spanned::Spanned, Meta, Path, Result, Token, TypeParamBound};

use crate::{Data, DeriveTrait, Error, Item, SplitGenerics};
use crate::{Data, DeriveTrait, DeriveWhere, Error, Item, SplitGenerics};

/// Type implementing [`TraitImpl`] for every trait.
#[derive(Clone, Copy, Eq, PartialEq)]
Expand Down Expand Up @@ -133,26 +133,23 @@ impl TraitImpl for Trait {

fn build_signature(
&self,
any_bound: bool,
derive_where: &DeriveWhere,
item: &Item,
generics: &SplitGenerics<'_>,
traits: &[DeriveTrait],
trait_: &DeriveTrait,
body: &TokenStream,
) -> TokenStream {
self.implementation()
.build_signature(any_bound, item, generics, traits, trait_, body)
.build_signature(derive_where, item, generics, trait_, body)
}

fn build_body(
&self,
any_bound: bool,
traits: &[DeriveTrait],
derive_where: &DeriveWhere,
trait_: &DeriveTrait,
data: &Data,
) -> TokenStream {
self.implementation()
.build_body(any_bound, traits, trait_, data)
self.implementation().build_body(derive_where, trait_, data)
}
}

Expand Down Expand Up @@ -199,10 +196,9 @@ pub trait TraitImpl {
/// Build method signature for this [`Trait`].
fn build_signature(
&self,
_any_bound: bool,
_derive_where: &DeriveWhere,
_item: &Item,
_generics: &SplitGenerics<'_>,
_traits: &[DeriveTrait],
_trait_: &DeriveTrait,
_body: &TokenStream,
) -> TokenStream {
Expand All @@ -212,8 +208,7 @@ pub trait TraitImpl {
/// Build method body for this [`Trait`].
fn build_body(
&self,
_any_bound: bool,
_traits: &[DeriveTrait],
_derive_where: &DeriveWhere,
_trait_: &DeriveTrait,
_data: &Data,
) -> TokenStream {
Expand Down
18 changes: 11 additions & 7 deletions src/trait_/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::{TraitBound, TraitBoundModifier, TypeParamBound};

use crate::{Data, DataType, DeriveTrait, Item, SimpleType, SplitGenerics, Trait, TraitImpl};
use crate::{
Data, DataType, DeriveTrait, DeriveWhere, Item, SimpleType, SplitGenerics, Trait, TraitImpl,
};

/// Dummy-struct implement [`Trait`] for [`Clone`](trait@std::clone::Clone).
pub struct Clone;
Expand Down Expand Up @@ -42,15 +44,16 @@ impl TraitImpl for Clone {

fn build_signature(
&self,
any_bound: bool,
derive_where: &DeriveWhere,
item: &Item,
_generics: &SplitGenerics<'_>,
traits: &[DeriveTrait],
_trait_: &DeriveTrait,
body: &TokenStream,
) -> TokenStream {
// Special implementation for items also implementing `Copy`.
if !any_bound && traits.iter().any(|trait_| trait_ == Trait::Copy) {
if (derive_where.generics.is_empty() || derive_where.any_custom_bound())
&& derive_where.contains(Trait::Copy)
{
return quote! {
#[inline]
fn clone(&self) -> Self { *self }
Expand Down Expand Up @@ -85,12 +88,13 @@ impl TraitImpl for Clone {

fn build_body(
&self,
any_bound: bool,
traits: &[DeriveTrait],
derive_where: &DeriveWhere,
trait_: &DeriveTrait,
data: &Data,
) -> TokenStream {
if !any_bound && traits.iter().any(|trait_| trait_ == Trait::Copy) {
if (derive_where.generics.is_empty() || derive_where.any_custom_bound())
&& derive_where.contains(Trait::Copy)
{
return TokenStream::new();
}

Expand Down
12 changes: 6 additions & 6 deletions src/trait_/common_ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ use syn::{parse_quote, Expr, ExprLit, LitInt, Path};

#[cfg(not(feature = "nightly"))]
use crate::{item::Representation, Discriminant, Trait};
use crate::{Data, DeriveTrait, Item, SimpleType, SplitGenerics};
use crate::{Data, DeriveTrait, DeriveWhere, Item, SimpleType, SplitGenerics};

/// Build signature for [`PartialOrd`] and [`Ord`].
pub fn build_ord_signature(
item: &Item,
#[cfg_attr(feature = "nightly", allow(unused_variables))] generics: &SplitGenerics<'_>,
#[cfg_attr(feature = "nightly", allow(unused_variables))] traits: &[DeriveTrait],
#[cfg_attr(feature = "nightly", allow(unused_variables))] derive_where: &DeriveWhere,
trait_: &DeriveTrait,
body: &TokenStream,
) -> TokenStream {
Expand Down Expand Up @@ -173,13 +173,13 @@ pub fn build_ord_signature(
}
});

if traits.iter().any(|trait_| trait_ == Trait::Copy) {
if derive_where.contains(Trait::Copy) {
quote! {
#validate

#path::#method(&(*self as isize), &(*__other as isize))
}
} else if traits.iter().any(|trait_| trait_ == Trait::Clone) {
} else if derive_where.contains(Trait::Clone) {
let clone = DeriveTrait::Clone.path();
quote! {
#validate
Expand Down Expand Up @@ -215,11 +215,11 @@ pub fn build_ord_signature(
)
}
Discriminant::UnitRepr(repr) => {
if traits.iter().any(|trait_| trait_ == Trait::Copy) {
if derive_where.contains(Trait::Copy) {
quote! {
#path::#method(&(*self as #repr), &(*__other as #repr))
}
} else if traits.iter().any(|trait_| trait_ == Trait::Clone) {
} else if derive_where.contains(Trait::Clone) {
let clone = DeriveTrait::Clone.path();
quote! {
#path::#method(&(#clone::clone(self) as #repr), &(#clone::clone(__other) as #repr))
Expand Down
Loading

0 comments on commit 567f20b

Please sign in to comment.