mono_macro/lib.rs
1//! Mono macro
2//! ==================
3//!
4//! This crate provides the `#[mono]` macro to force a generic function to be monomorphizied with given types.
5//!
6//! Pair with `share-generics` mode in rustc, this can result less code, for details see <https://github.com/rust-lang/rust/pull/48779>.
7//!
8//! ```toml
9//! [dependencies]
10//! mono-macro = "1.0"
11//! ```
12//!
13//! <br>
14//!
15//! ## Usage
16//!
17//! Since we are monomorphizing ourselves, you are required to spell out the static dispatch manually:
18//!
19//! In a bare function case,
20//! ```rust
21//! use mono_macro::mono;
22//! #[mono(T = i32, U = i64)]
23//! fn func<T, U>(t: T, u: U) {}
24//! ```
25//!
26//! it will be expanded to:
27//! ```rust
28//! pub const _: *const () = (&func::<i32, i64>) as *const _ as _;
29//! fn func<T, U>(t: T, u: U) {}
30//! ```
31//!
32//! For more complicated case, use `mono_macro!` instead:
33//! ```rust
34//! use mono_macro::mono_macro;
35//! trait Tr<T> {
36//! fn foo(&self, _t: T) {}
37//! }
38//!
39//! struct Foo<'a> {
40//! t: &'a str,
41//! }
42//!
43//! impl<'a, T> Tr<T> for Foo<'a> {
44//! fn foo(&self, _t: T) {}
45//! }
46//!
47//! mono_macro!(<Foo<'static> as Tr<i32>>::foo);
48//! ```
49//!
50//! this will expand to:
51//! ```rust
52//! trait Tr<T> {
53//! fn foo(&self, _t: T) {}
54//! }
55//!
56//! struct Foo<'a> {
57//! t: &'a str,
58//! }
59//!
60//! impl<'a, T> Tr<T> for Foo<'a> {
61//! fn foo(&self, _t: T) {}
62//! }
63//!
64//! pub const _: *const () = (&<Foo<'static> as Tr<i32>>::foo) as *const _ as _;
65//! ```
66
67use proc_macro::TokenStream;
68use quote::quote;
69use syn::parse::Parse;
70use syn::parse::ParseStream;
71use syn::parse_macro_input;
72use syn::punctuated::Punctuated;
73use syn::spanned::Spanned;
74use syn::GenericParam;
75use syn::Ident;
76use syn::ItemFn;
77use syn::Lifetime;
78use syn::Token;
79use syn::TypePath;
80
81/// Apply this macro on a generic function will cast the **bare function** pointer with given type into pointer, which forces this function to be monomorphized.
82///
83/// # Example
84/// ```rust,no_run
85/// use mono_macro::mono;
86/// #[mono(T = i32)]
87/// fn foo<T>(t: T) {}
88/// ```
89/// expands to:
90/// ```rust,no_run
91/// pub const _: *const () = &foo::<i32> as *const _ as _;
92/// fn foo<T>(t: T) {}
93/// ```
94///
95#[proc_macro_attribute]
96pub fn mono(attr: TokenStream, func: TokenStream) -> TokenStream {
97 let mono_attr = parse_macro_input!(attr as TypeEqs);
98
99 let input = func.clone();
100 let fn_sig = parse_macro_input!(input as ItemFn).sig;
101 let fn_span = fn_sig.span();
102 let func_ident = fn_sig.ident.clone();
103
104 let mut params = vec![];
105 for g in fn_sig.generics.params.into_iter() {
106 if let Some(t) = mono_attr
107 .eqs
108 .iter()
109 .find(|eq| match (&g, &eq.type_or_lifetime) {
110 // (GenericParam::Lifetime(ld), TypeOrLifetime::Lifetime(l)) => &ld.lifetime == l,
111 (GenericParam::Type(t1), TypeOrLifetime::Type(t2)) => &t1.ident == t2,
112 (_, _) => false,
113 })
114 {
115 params.push(t.param.clone());
116 } else if matches!(g, GenericParam::Type(_)) {
117 let err = syn::Error::new(fn_span, "all the type parameters should be spelled out")
118 .into_compile_error()
119 .into();
120 return err;
121 }
122 }
123
124 let mut expand = TokenStream::from(quote! {
125 pub const _: *const () = (&#func_ident::<#(#params,)*>) as *const _ as _;
126 });
127
128 expand.extend(func);
129 expand
130}
131
132/// Force monomorphizing on a path of function, for the complex functions like impl methods of generic types.
133/// For example,
134/// ```rust,no_run
135/// use mono_macro::mono_macro;
136/// struct Foo<T>(T);
137/// trait Trait<K> {
138/// fn method(&self, k: K);
139/// }
140/// impl<T, K> Trait<K> for Foo<T> {
141/// fn method(&self, k: K) {}
142/// }
143///
144/// mono_macro!(<Foo<i32> as Trait<u8>>::method);
145/// ```
146///
147/// this will expand to:
148/// ```rust,no_run
149/// use mono_macro::mono_macro;
150/// struct Foo<T>(T);
151/// trait Trait<K> {
152/// fn method(&self, k: K);
153/// }
154/// impl<T, K> Trait<K> for Foo<T> {
155/// fn method(&self, k: K) {}
156/// }
157/// pub const _: *const () = (&<Foo<i32> as Trait<u8>>::method) as *const _ as _;
158/// ```
159#[proc_macro]
160pub fn mono_macro(input: TokenStream) -> TokenStream {
161 let path = parse_macro_input!(input as TypePath);
162 TokenStream::from(quote! {
163 pub const _: *const () = (&#path) as *const _ as _;
164 })
165}
166
167// T = i32, U = i64
168struct TypeEqs {
169 eqs: Punctuated<TypeEqTo, Token![,]>,
170}
171
172impl Parse for TypeEqs {
173 fn parse(input: ParseStream) -> syn::Result<Self> {
174 Ok(TypeEqs {
175 eqs: { input.parse_terminated(TypeEqTo::parse)? },
176 })
177 }
178}
179
180// T = i32
181struct TypeEqTo {
182 type_or_lifetime: TypeOrLifetime,
183 #[allow(dead_code)]
184 eq_token: Token![=],
185 param: TypeOrLifetime,
186}
187
188impl Parse for TypeEqTo {
189 fn parse(input: ParseStream) -> syn::Result<Self> {
190 Ok(TypeEqTo {
191 type_or_lifetime: input.parse()?,
192 eq_token: input.parse()?,
193 param: input.parse()?,
194 })
195 }
196}
197
198#[derive(Clone, Debug)]
199enum TypeOrLifetime {
200 Type(Ident),
201 Lifetime(syn::Lifetime),
202}
203
204impl Parse for TypeOrLifetime {
205 fn parse(input: ParseStream) -> syn::Result<Self> {
206 let lookahead = input.lookahead1();
207 if lookahead.peek(Lifetime) {
208 input.parse().map(TypeOrLifetime::Lifetime)
209 } else if lookahead.peek(Ident) {
210 input.parse().map(TypeOrLifetime::Type)
211 } else {
212 Err(lookahead.error())
213 }
214 }
215}
216
217extern crate proc_macro2;
218use proc_macro2::TokenStream as TokenStream2;
219impl quote::ToTokens for TypeOrLifetime {
220 fn to_tokens(&self, tokens: &mut TokenStream2) {
221 match self {
222 TypeOrLifetime::Lifetime(l) => l.to_tokens(tokens),
223 TypeOrLifetime::Type(t) => t.to_tokens(tokens),
224 }
225 }
226}