Skip to content

Commit c08651b

Browse files
Benjamin Woodrufffacebook-github-bot
authored andcommitted
Add type parameter (generics) support to trait_alias crate
Summary: As part of my attempt to use this in D40870195 (abandoned for reasons mentioned in that diff), I extended the `trait_alias` macro to support type parameters. **I no longer have use for this, but I'm publishing the diff just in case anyone else might want it.** There is also some prior art for this in the OSS community, and at some point, it could make sense to just switch to that: https://github.com/popzxc/trait-set (but there is some value in reducing third-party dependencies). Reviewed By: markbt Differential Revision: D40825641 fbshipit-source-id: 3299a3b74f419d4d20c76a6ec599c7474c7bef51
1 parent 0a1dd0e commit c08651b

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

shed/trait_alias/src/lib.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@
1111
1212
extern crate proc_macro;
1313

14+
use proc_macro2::Ident;
15+
use proc_macro2::Span;
1416
use proc_macro2::TokenStream;
1517
use quote::quote;
1618
use syn::parse_macro_input;
1719
use syn::Error;
20+
use syn::GenericParam;
1821
use syn::ItemTraitAlias;
22+
use syn::TypeParam;
1923

2024
/// Implement a trait alias using a subtrait and blanket definition.
2125
///
@@ -66,9 +70,26 @@ fn gen_trait_alias(trait_alias: ItemTraitAlias) -> Result<TokenStream, Error> {
6670
let vis = trait_alias.vis;
6771
let ident = trait_alias.ident;
6872
let bounds = trait_alias.bounds;
73+
let generics = trait_alias.generics;
74+
75+
// Pick a unique name. Ideally this would use `Span::def_site`, but it isn't stable.
76+
let blanket_type_ident = Ident::new("_TraitAliasImplBlanketType", Span::mixed_site());
77+
let mut blanket_type_param = TypeParam::from(blanket_type_ident.clone());
78+
blanket_type_param.bounds = bounds.clone();
79+
80+
// Append the blanket type to the end of the generic params of our `impl` block.
81+
// We'll implement our alias over that blanket type.
82+
let mut generics_with_blanket_type = generics.clone();
83+
generics_with_blanket_type
84+
.params
85+
.push(GenericParam::Type(blanket_type_param));
86+
87+
// declare all the bits needed by quote
88+
let (trait_generics, ty_generics, where_clause) = generics.split_for_impl();
89+
let (impl_generics, _, _) = generics_with_blanket_type.split_for_impl();
6990

7091
Ok(quote! {
71-
#vis trait #ident : #bounds {}
72-
impl<T> #ident for T where T: #bounds {}
92+
#vis trait #ident #trait_generics : #bounds #where_clause {}
93+
impl #impl_generics #ident #ty_generics for #blanket_type_ident #where_clause {}
7394
})
7495
}

shed/trait_alias/test/trait_alias_test.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,24 @@ impl Two for Impl {
3232
}
3333
}
3434

35-
fn test(both: &impl Both) {
35+
#[trait_alias::trait_alias]
36+
trait GenericFn<T: Both> = Fn() -> T;
37+
38+
fn test_both(both: &impl Both) {
3639
assert_eq!(both.one(), 1);
3740
assert_eq!(both.two(), 2);
3841
}
3942

43+
fn test_generic_fn<B>(generic_fn: impl GenericFn<B>)
44+
where
45+
B: Both,
46+
{
47+
assert_eq!((generic_fn)().one(), 1);
48+
assert_eq!((generic_fn)().two(), 2);
49+
}
50+
4051
#[test]
4152
fn main() {
42-
test(&Impl);
53+
test_both(&Impl);
54+
test_generic_fn(|| Impl);
4355
}

0 commit comments

Comments
 (0)