tailcall_impl/
lib.rs

1//! This crate contains the procedural macro implementation for the [tailcall] crate.
2//! It is not designed to be used dierctly.
3//! [tailcall]: https://crates.io/crates/tailcall
4
5#![deny(
6    missing_debug_implementations,
7    missing_copy_implementations,
8    trivial_casts,
9    trivial_numeric_casts,
10    unsafe_code,
11    unstable_features,
12    unused_import_braces,
13    unused_qualifications
14)]
15
16extern crate proc_macro;
17
18mod helpers;
19mod transforms;
20
21use proc_macro::TokenStream;
22use quote::quote;
23use syn::{parse_macro_input, ItemFn};
24
25/// Transforms a [function definition] so that all recursive calls within the body are
26/// guaranteed to use a single stack frame (via [tail recursion]).
27///
28/// [function definition]: https://docs.rs/syn/1.0.9/syn/struct.ItemFn.html
29/// [tail recursion]: https://en.wikipedia.org/wiki/Tail_call
30///
31/// # Example
32///
33/// ```
34/// use tailcall_impl::tailcall;
35///
36/// fn factorial(input: u64) -> u64 {
37///     #[tailcall]
38///     fn factorial_inner(accumulator: u64, input: u64) -> u64 {
39///         if input > 0 {
40///             factorial_inner(accumulator * input, input - 1)
41///         } else {
42///             accumulator
43///         }
44///     }
45///
46///     factorial_inner(1, input)
47/// }
48/// ```
49///
50/// # Requirements
51///
52/// - All recursive calls must be in [tail form]:
53///
54/// ```compile_fail
55/// use tailcall_impl::tailcall;
56///   
57/// #[tailcall]
58/// fn factorial(input: u64) -> u64 {
59///     if input > 0 {
60///         input * factorial(input - 1)
61/// //      ^^^^^^^ This is not allowed.
62///     } else {
63///         1
64///     }
65/// }
66/// ```
67///
68/// - Methods (functions which bind `self` in the arguments list) are not supported:
69///
70/// ```compile_fail
71/// trait Factorialable {
72///     fn factorial(self) -> Self {
73///         self.calc_factorial(1)
74///     }
75///
76///     fn calc_factorial(self, accumulator: u64) -> u64;
77/// }
78///
79/// impl Factorialable for u64 {
80///     #[tailcall]
81///     fn calc_factorial(self, accumulator: u64) -> u64 {
82/// //                    ^^^^ This is not allowed.
83///         if self > 0 {
84///             (self - 1).calc_factorial(self * accumulator)
85///         } else {
86///             accumulator
87///         }
88///     }
89/// }
90/// ```
91///
92/// [tail form]: https://en.wikipedia.org/wiki/Tail_call
93#[proc_macro_attribute]
94pub fn tailcall(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
95    let input = parse_macro_input!(tokens as ItemFn);
96    let output = transforms::apply_fn_tailcall_transform(input);
97
98    TokenStream::from(quote! {
99        #output
100    })
101}