1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
//! This crate contains the procedural macro implementation for the [tailcall] crate.
//! It is not designed to be used dierctly.
//! [tailcall]: https://crates.io/crates/tailcall

#![deny(
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

extern crate proc_macro;

mod helpers;
mod transforms;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};

/// Transforms a [function definition] so that all recursive calls within the body are
/// guaranteed to use a single stack frame (via [tail recursion]).
///
/// [function definition]: https://docs.rs/syn/1.0.9/syn/struct.ItemFn.html
/// [tail recursion]: https://en.wikipedia.org/wiki/Tail_call
///
/// # Example
///
/// ```
/// use tailcall::tailcall;
///
/// fn factorial(input: u64) -> u64 {
///     #[tailcall]
///     fn factorial_inner(accumulator: u64, input: u64) -> u64 {
///         if input > 0 {
///             factorial_inner(accumulator * input, input - 1)
///         } else {
///             accumulator
///         }
///     }
///
///     factorial_inner(1, input)
/// }
/// ```
///
/// # Requirements
///
/// - All recursive calls must be in [tail form]:
///
/// ```compile_fail
/// use tailcall::tailcall;
///   
/// #[tailcall]
/// fn factorial(input: u64) -> u64 {
///     if input > 0 {
///         input * factorial(input - 1)
/// //      ^^^^^^^ This is not allowed.
///     } else {
///         1
///     }
/// }
/// ```
///
/// - Methods (functions which bind `self` in the arguments list) are not supported:
///
/// ```compile_fail
/// trait Factorialable {
///     fn factorial(self) -> Self {
///         self.calc_factorial(1)
///     }
///
///     fn calc_factorial(self, accumulator: u64) -> u64;
/// }
///
/// impl Factorialable for u64 {
///     #[tailcall]
///     fn calc_factorial(self, accumulator: u64) -> u64 {
/// //                    ^^^^ This is not allowed.
///         if self > 0 {
///             (self - 1).calc_factorial(self * accumulator)
///         } else {
///             accumulator
///         }
///     }
/// }
/// ```
///
/// [tail form]: https://en.wikipedia.org/wiki/Tail_call
#[proc_macro_attribute]
pub fn tailcall(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
    let input = parse_macro_input!(tokens as ItemFn);
    let output = transforms::apply_fn_tailcall_transform(input);

    TokenStream::from(quote! {
        #output
    })
}