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}