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
#![no_std]

/// Explicitly precompute a method's arguments before the call so that borrowck sees them the same
/// way that trans does.
///
/// Examples
/// =======
///
/// ```
/// # #[macro_use] extern crate unborrow;
/// # fn main() {
/// let mut v = vec![1, 2, 3];
///
/// // this line would cause an error because borrowck consider `v` borrowed by `reserve`
/// // during its parameter list
/// // v.reserve(v.capacity()); //~ERROR cannot borrow `v`
/// // but wrap the call in unborrow!() and it works!
/// unborrow!(v.reserve(v.capacity()));
/// assert!(v.capacity() >= 6);
/// assert_eq!(v, [1, 2, 3]);
/// 
/// // similar to the above, both v.len()-1 and v[0]+41 require borrowing `v` and we can't
/// // do that while borrowck thinks is is mutably borrowed by `insert`
/// // v.insert(v.len() - 1, v[0] + 41); //~ERROR cannot borrow `v`
/// // but wrap the call in unborrow!() and it works!
/// unborrow!(v.insert(v.len() - 1, v[0] + 41));
/// assert_eq!(v, [1, 2, 42, 3]);
///
/// // it also works for nested objects!
/// struct Wrapper { v: Vec<i32> }
/// let mut w = Wrapper { v: vec![1, 2, 3] };
/// unborrow!(w.v.reserve(w.v.capacity()));
///
/// // ...and with free functions! (the first argument is assumed to be the mutable borrow)
/// use std::mem;
/// unborrow!(mem::replace(&mut v, v.clone()));
///
/// # }
/// ```
#[macro_export]
macro_rules! unborrow {
    // =========================================================================================================
    // PRIVATE RULES

    // This rule fires when we have parsed all the arguments.
    // It just falls through to output stage.
    // (FIXME could fold the output rule into this one to reduce recursion)
    (@parse () -> ($names:tt $lets:tt) $($thru:tt)*) => {
        unborrow!(@out $names $lets $($thru)*)
    };

    // Parse an argument and continue parsing
    // This is the key rule, assigning a name for the argument and generating the let statement.
    (@parse ($arg:expr, $($rest:tt)*) -> ([$($names:ident),*] [$($lets:stmt);*]) $($thru:tt)*) => {
        unborrow!(@parse ($($rest)*) -> ([$($names,)* arg] [$($lets;)* let arg = $arg]) $($thru)*)
        //                                            ^                    ^
        // Right here an ident is created out of thin air using hygiene.
        // Every time the macro recurses, we get a new syntax context, so "arg" is actually a new identifier!
    };

    // Output stage for free functions.
    // Assembles the let statements and variable names into a block which computes the arguments,
    // calls the method, and returns its result.
    (@out [$($names:ident),*] [$($lets:stmt);*] ($($meth:ident)::+) $arg1:expr) => {{
        $($lets;)*
        $($meth)::+($arg1, $($names),*)
    }};
    
    // Output stage for object methods.
    (@out [$($names:ident),*] [$($lets:stmt);*] $($obj:ident).+) => {{
        $($lets;)*
        $($obj).+($($names),*)
    }};

    // =========================================================================================================
    // PUBLIC RULES

    // Macro entry point for object methods.
    ($($obj:ident).+ ($($args:expr),*)) => {
        unborrow!(@parse ($($args,)*) -> ([] []) $($obj).+)
        //                |               |  |   ^ info about the method call, saved for later
        //                |               |  ^ generated let statements
        //                |               ^ generated argument names
        //                ^ arguments to be parsed
    };

    // Macro entry point for free functions.
    ($($meth:ident)::+ ($arg1:expr, $($args:expr),*)) => {
        unborrow!(@parse ($($args,)*) -> ([] []) ($($meth)::+) $arg1)
    };
}