Browse Source

Remove the `nborrow` dependency and give the macro its own module.

To avoid linking hell.
ticki 8 years ago
parent
commit
afaad8d303
2 changed files with 92 additions and 3 deletions
  1. 1 3
      src/lib.rs
  2. 91 0
      src/unborrow.rs

+ 1 - 3
src/lib.rs

@@ -23,9 +23,6 @@
         invalid_upcast_comparisons, mutex_integer, nonminimal_bool, shadow_same, shadow_unrelated,
         single_match_else, string_add, string_add_assign, wrong_pub_self_convention)]
 
-#[macro_use]
-#[no_link]
-extern crate unborrow;
 extern crate ralloc_shim as shim;
 
 #[macro_use]
@@ -47,6 +44,7 @@ mod leak;
 mod prelude;
 mod ptr;
 mod sync;
+mod unborrow;
 mod vec;
 
 pub use allocator::{alloc, free, realloc, realloc_inplace};

+ 91 - 0
src/unborrow.rs

@@ -0,0 +1,91 @@
+//! Durka's `unborrow!` macro.
+
+/// 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)
+    };
+}