diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5dacc5b --- /dev/null +++ b/CHANGELOG.md @@ -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. diff --git a/Cargo.toml b/Cargo.toml index f939998..71bb96f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trait-set" -version = "0.1.0" +version = "0.2.0" authors = ["Igor Aleksanov "] edition = "2018" repository = "https://github.com/popzxc/trait-set" diff --git a/README.md b/README.md index 046397e..f2a686e 100644 --- a/README.md +++ b/README.md @@ -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 = Iterator; } ``` diff --git a/src/lib.rs b/src/lib.rs index 44e4358..b976128 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. @@ -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 Trait` block there must be no trait bounds in the `` part, + // they must go into `` part only. + // E.g. `impl Trait 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 {} } } } diff --git a/tests/ui/correct/09_generic_param_trait_bounds.rs b/tests/ui/correct/09_generic_param_trait_bounds.rs new file mode 100644 index 0000000..bd018c2 --- /dev/null +++ b/tests/ui/correct/09_generic_param_trait_bounds.rs @@ -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 { + fn new(t: T) -> Self; +} + +impl GenericTrait for u8 { + fn new(t: u8) -> u8 { + t + } +} + +trait_set! { + pub(crate) trait GenericIteratorSendableT = Iterator; +} + +fn test_set>(_arg: T) {} + +fn main() { + test_set([10u8, 20, 30].as_ref().iter().copied()); + test_set(b"abcde".iter().copied()); +}