Skip to content

Commit

Permalink
Merge pull request #3 from popzxc/types-bounds
Browse files Browse the repository at this point in the history
Allow trait bounds on generic params in the trait alias
  • Loading branch information
popzxc authored May 25, 2021
2 parents ce9a778 + 581b648 commit 4f34975
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 5 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Changelog

## Version 0.2.0 (202x-xx-xx)

- Support for trait bounds on generic params for the alias was added.

## Version 0.1.0 (2021-04-18)

- Initial crate implementation.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "trait-set"
version = "0.1.0"
version = "0.2.0"
authors = ["Igor Aleksanov <[email protected]>"]
edition = "2018"
repository = "https://github.com/popzxc/trait-set"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ trait_set! {

// Lifetime as a generic parameter.
pub trait SerdeLifetimeTemplate<'de> = Serialize + Deserialize<'de>;

// Trait bounds on generic parameters for an alias.
pub trait GenericIteratorSendableT<T: Send> = Iterator<Item = T>;
}
```

Expand Down
23 changes: 19 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use syn::{
parse_macro_input,
punctuated::Punctuated,
spanned::Spanned,
Generics, Ident, Result, Token, TypeTraitObject, Visibility,
GenericParam, Generics, Ident, Result, Token, TypeTraitObject, Visibility,
};

/// Represents one trait alias.
Expand Down Expand Up @@ -82,14 +82,29 @@ impl TraitSet {
let visibility = self.visibility;
let alias_name = self.alias_name;
let bounds = self.traits.bounds;
let generics = self.generics.params;

// We differentiate `generics` and `bound_generics` because in the
// `impl<X> Trait<Y>` block there must be no trait bounds in the `<Y>` part,
// they must go into `<X>` part only.
// E.g. `impl<X: Send, _INNER> Trait<X> for _INNER`.
let mut unbound_generics = self.generics.clone();
for param in unbound_generics.params.iter_mut() {
if let GenericParam::Type(ty) = param {
if !ty.bounds.is_empty() {
ty.bounds.clear();
}
}
}
let unbound_generics = unbound_generics.params;
let bound_generics = self.generics.params;

// Note that it's important for `_INNER` to go *after* user-defined
// generics, because generics can contain lifetimes, and lifetimes
// should always go first.
quote! {
#visibility trait #alias_name<#generics>: #bounds {}
#visibility trait #alias_name<#bound_generics>: #bounds {}

impl<#generics, _INNER> #alias_name<#generics> for _INNER where _INNER: #bounds {}
impl<#bound_generics, _INNER> #alias_name<#unbound_generics> for _INNER where _INNER: #bounds {}
}
}
}
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/correct/09_generic_param_trait_bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Checks that trait bounds can be applied to the generic arguments
//! of an alias.
use trait_set::trait_set;

pub trait GenericTrait<T> {
fn new(t: T) -> Self;
}

impl GenericTrait<u8> for u8 {
fn new(t: u8) -> u8 {
t
}
}

trait_set! {
pub(crate) trait GenericIteratorSendableT<T: Send> = Iterator<Item = T>;
}

fn test_set<T: GenericIteratorSendableT<u8>>(_arg: T) {}

fn main() {
test_set([10u8, 20, 30].as_ref().iter().copied());
test_set(b"abcde".iter().copied());
}

0 comments on commit 4f34975

Please sign in to comment.