diff --git a/Makefile.in b/Makefile.in
index 33077872c1696..d531b9879a9a8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -110,6 +110,9 @@ endif
 ifdef SAVE_TEMPS
   CFG_RUSTC_FLAGS += --save-temps
 endif
+ifdef ASM_COMMENTS
+  CFG_RUSTC_FLAGS += -z asm-comments
+endif
 ifdef TIME_PASSES
   CFG_RUSTC_FLAGS += -Z time-passes
 endif
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index 27e03d2bf3103..959defeec0413 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -42,7 +42,7 @@ pub fn empty_cell<T>() -> Cell<T> {
 pub impl<T> Cell<T> {
     /// Yields the value, failing if the cell is empty.
     fn take(&self) -> T {
-        let mut self = unsafe { transmute_mut(self) };
+        let self = unsafe { transmute_mut(self) };
         if self.is_empty() {
             fail!(~"attempt to take an empty cell");
         }
@@ -54,7 +54,7 @@ pub impl<T> Cell<T> {
 
     /// Returns the value, failing if the cell is full.
     fn put_back(&self, value: T) {
-        let mut self = unsafe { transmute_mut(self) };
+        let self = unsafe { transmute_mut(self) };
         if !self.is_empty() {
             fail!(~"attempt to put a value back into a full cell");
         }
diff --git a/src/libcore/cleanup.rs b/src/libcore/cleanup.rs
index f34f5218f5feb..424cc3483092d 100644
--- a/src/libcore/cleanup.rs
+++ b/src/libcore/cleanup.rs
@@ -15,6 +15,7 @@ use ptr::mut_null;
 use repr::BoxRepr;
 use sys::TypeDesc;
 use cast::transmute;
+#[cfg(notest)] use unstable::lang::clear_task_borrow_list;
 
 #[cfg(notest)] use ptr::to_unsafe_ptr;
 
@@ -179,6 +180,10 @@ pub unsafe fn annihilate() {
         n_bytes_freed: 0
     };
 
+    // Quick hack: we need to free this list upon task exit, and this
+    // is a convenient place to do it.
+    clear_task_borrow_list();
+
     // Pass 1: Make all boxes immortal.
     //
     // In this pass, nothing gets freed, so it does not matter whether
diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs
index 50a3bba049bbb..d075ff08bb7eb 100644
--- a/src/libcore/comm.rs
+++ b/src/libcore/comm.rs
@@ -205,8 +205,8 @@ impl<T: Owned> Selectable for Port<T> {
     fn header(&self) -> *PacketHeader {
         unsafe {
             match self.endp {
-              Some(ref endp) => endp.header(),
-              None => fail!(~"peeking empty stream")
+                Some(ref endp) => endp.header(),
+                None => fail!(~"peeking empty stream")
             }
         }
     }
diff --git a/src/libcore/flate.rs b/src/libcore/flate.rs
index c18273955adab..ba10f97e626c4 100644
--- a/src/libcore/flate.rs
+++ b/src/libcore/flate.rs
@@ -28,13 +28,13 @@ pub mod rustrt {
     pub extern {
         unsafe fn tdefl_compress_mem_to_heap(psrc_buf: *const c_void,
                                              src_buf_len: size_t,
-                                             pout_len: *size_t,
+                                             pout_len: *mut size_t,
                                              flags: c_int)
                                           -> *c_void;
 
         unsafe fn tinfl_decompress_mem_to_heap(psrc_buf: *const c_void,
                                                src_buf_len: size_t,
-                                               pout_len: *size_t,
+                                               pout_len: *mut size_t,
                                                flags: c_int)
                                             -> *c_void;
     }
@@ -52,11 +52,11 @@ pub fn deflate_bytes(bytes: &const [u8]) -> ~[u8] {
             let res =
                 rustrt::tdefl_compress_mem_to_heap(b as *c_void,
                                                    len as size_t,
-                                                   &outsz,
+                                                   &mut outsz,
                                                    lz_norm);
             assert!(res as int != 0);
             let out = vec::raw::from_buf_raw(res as *u8,
-                                            outsz as uint);
+                                             outsz as uint);
             libc::free(res);
             out
         }
@@ -66,11 +66,11 @@ pub fn deflate_bytes(bytes: &const [u8]) -> ~[u8] {
 pub fn inflate_bytes(bytes: &const [u8]) -> ~[u8] {
     do vec::as_const_buf(bytes) |b, len| {
         unsafe {
-            let outsz : size_t = 0;
+            let mut outsz : size_t = 0;
             let res =
                 rustrt::tinfl_decompress_mem_to_heap(b as *c_void,
                                                      len as size_t,
-                                                     &outsz,
+                                                     &mut outsz,
                                                      0);
             assert!(res as int != 0);
             let out = vec::raw::from_buf_raw(res as *u8,
diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs
index 392ad38e20931..9b01c1dad06e9 100644
--- a/src/libcore/hashmap.rs
+++ b/src/libcore/hashmap.rs
@@ -25,6 +25,7 @@ use rand;
 use uint;
 use vec;
 use util::unreachable;
+use kinds::Copy;
 
 static INITIAL_CAPACITY: uint = 32u; // 2^5
 
@@ -529,6 +530,18 @@ pub impl<K: Hash + Eq, V> HashMap<K, V> {
     }
 }
 
+pub impl<K: Hash + Eq, V: Copy> HashMap<K, V> {
+    /// Like `find`, but returns a copy of the value.
+    fn find_copy(&self, k: &K) -> Option<V> {
+        self.find(k).map_consume(|v| copy *v)
+    }
+
+    /// Like `get`, but returns a copy of the value.
+    fn get_copy(&self, k: &K) -> V {
+        copy *self.get(k)
+    }
+}
+
 impl<K:Hash + Eq,V:Eq> Eq for HashMap<K, V> {
     fn eq(&self, other: &HashMap<K, V>) -> bool {
         if self.len() != other.len() { return false; }
diff --git a/src/libcore/io.rs b/src/libcore/io.rs
index 185b84a09c518..460fd60d4c56b 100644
--- a/src/libcore/io.rs
+++ b/src/libcore/io.rs
@@ -1022,7 +1022,7 @@ pub enum WriterType { Screen, File }
 pub trait Writer {
 
     /// Write all of the given bytes.
-    fn write(&self, v: &const [u8]);
+    fn write(&self, v: &[u8]);
 
     /// Move the current position within the stream. The second parameter
     /// determines the position that the first parameter is relative to.
@@ -1039,7 +1039,7 @@ pub trait Writer {
 }
 
 impl Writer for @Writer {
-    fn write(&self, v: &const [u8]) { self.write(v) }
+    fn write(&self, v: &[u8]) { self.write(v) }
     fn seek(&self, a: int, b: SeekStyle) { self.seek(a, b) }
     fn tell(&self) -> uint { self.tell() }
     fn flush(&self) -> int { self.flush() }
@@ -1047,7 +1047,7 @@ impl Writer for @Writer {
 }
 
 impl<W:Writer,C> Writer for Wrapper<W, C> {
-    fn write(&self, bs: &const [u8]) { self.base.write(bs); }
+    fn write(&self, bs: &[u8]) { self.base.write(bs); }
     fn seek(&self, off: int, style: SeekStyle) { self.base.seek(off, style); }
     fn tell(&self) -> uint { self.base.tell() }
     fn flush(&self) -> int { self.base.flush() }
@@ -1055,7 +1055,7 @@ impl<W:Writer,C> Writer for Wrapper<W, C> {
 }
 
 impl Writer for *libc::FILE {
-    fn write(&self, v: &const [u8]) {
+    fn write(&self, v: &[u8]) {
         unsafe {
             do vec::as_const_buf(v) |vbuf, len| {
                 let nout = libc::fwrite(vbuf as *c_void,
@@ -1105,7 +1105,7 @@ pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> @Writer {
 }
 
 impl Writer for fd_t {
-    fn write(&self, v: &const [u8]) {
+    fn write(&self, v: &[u8]) {
         unsafe {
             let mut count = 0u;
             do vec::as_const_buf(v) |vbuf, len| {
@@ -1262,7 +1262,7 @@ pub fn u64_to_be_bytes<T>(n: u64, size: uint,
     }
 }
 
-pub fn u64_from_be_bytes(data: &const [u8],
+pub fn u64_from_be_bytes(data: &[u8],
                          start: uint,
                          size: uint)
                       -> u64 {
@@ -1497,7 +1497,7 @@ pub struct BytesWriter {
 }
 
 impl Writer for BytesWriter {
-    fn write(&self, v: &const [u8]) {
+    fn write(&self, v: &[u8]) {
         let v_len = v.len();
         let bytes_len = vec::uniq_len(&const self.bytes);
 
diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs
index 59b06faf5a251..6fb4572913d2e 100644
--- a/src/libcore/libc.rs
+++ b/src/libcore/libc.rs
@@ -268,8 +268,7 @@ pub mod types {
                 pub type ssize_t = i32;
             }
             pub mod posix01 {
-                use libc::types::os::arch::c95::{c_int, c_short, c_long,
-                                                 time_t};
+                use libc::types::os::arch::c95::{c_short, c_long, time_t};
                 use libc::types::os::arch::posix88::{dev_t, gid_t, ino_t};
                 use libc::types::os::arch::posix88::{mode_t, off_t};
                 use libc::types::os::arch::posix88::{uid_t};
diff --git a/src/libcore/os.rs b/src/libcore/os.rs
index 7b68e6597a179..42c77a687e5fd 100644
--- a/src/libcore/os.rs
+++ b/src/libcore/os.rs
@@ -352,13 +352,13 @@ pub fn fsync_fd(fd: c_int, _l: io::fsync::Level) -> c_int {
     }
 }
 
-pub struct Pipe { mut in: c_int, mut out: c_int }
+pub struct Pipe { in: c_int, out: c_int }
 
 #[cfg(unix)]
 pub fn pipe() -> Pipe {
     unsafe {
         let mut fds = Pipe {in: 0 as c_int,
-                        out: 0 as c_int };
+                            out: 0 as c_int };
         assert!((libc::pipe(&mut fds.in) == (0 as c_int)));
         return Pipe {in: fds.in, out: fds.out};
     }
@@ -1025,10 +1025,10 @@ pub fn last_os_error() -> ~str {
         #[cfg(target_os = "macos")]
         #[cfg(target_os = "android")]
         #[cfg(target_os = "freebsd")]
-        fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int {
+        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
             #[nolink]
             extern {
-                unsafe fn strerror_r(errnum: c_int, buf: *c_char,
+                unsafe fn strerror_r(errnum: c_int, buf: *mut c_char,
                                      buflen: size_t) -> c_int;
             }
             unsafe {
@@ -1040,10 +1040,10 @@ pub fn last_os_error() -> ~str {
         // and requires macros to instead use the POSIX compliant variant.
         // So we just use __xpg_strerror_r which is always POSIX compliant
         #[cfg(target_os = "linux")]
-        fn strerror_r(errnum: c_int, buf: *c_char, buflen: size_t) -> c_int {
+        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: size_t) -> c_int {
             #[nolink]
             extern {
-                unsafe fn __xpg_strerror_r(errnum: c_int, buf: *c_char,
+                unsafe fn __xpg_strerror_r(errnum: c_int, buf: *mut c_char,
                                            buflen: size_t) -> c_int;
             }
             unsafe {
@@ -1053,7 +1053,7 @@ pub fn last_os_error() -> ~str {
 
         let mut buf = [0 as c_char, ..TMPBUF_SZ];
         unsafe {
-            let err = strerror_r(errno() as c_int, &buf[0],
+            let err = strerror_r(errno() as c_int, &mut buf[0],
                                  TMPBUF_SZ as size_t);
             if err < 0 {
                 fail!(~"strerror_r failure");
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs
index 86b36834bbd6e..85e46a0feff6b 100644
--- a/src/libcore/ptr.rs
+++ b/src/libcore/ptr.rs
@@ -296,34 +296,34 @@ impl<T> Ord for *const T {
 
 // Equality for region pointers
 #[cfg(notest)]
-impl<'self,T:Eq> Eq for &'self const T {
+impl<'self,T:Eq> Eq for &'self T {
     #[inline(always)]
-    fn eq(&self, other: & &'self const T) -> bool {
+    fn eq(&self, other: & &'self T) -> bool {
         return *(*self) == *(*other);
     }
     #[inline(always)]
-    fn ne(&self, other: & &'self const T) -> bool {
+    fn ne(&self, other: & &'self T) -> bool {
         return *(*self) != *(*other);
     }
 }
 
 // Comparison for region pointers
 #[cfg(notest)]
-impl<'self,T:Ord> Ord for &'self const T {
+impl<'self,T:Ord> Ord for &'self T {
     #[inline(always)]
-    fn lt(&self, other: & &'self const T) -> bool {
+    fn lt(&self, other: & &'self T) -> bool {
         *(*self) < *(*other)
     }
     #[inline(always)]
-    fn le(&self, other: & &'self const T) -> bool {
+    fn le(&self, other: & &'self T) -> bool {
         *(*self) <= *(*other)
     }
     #[inline(always)]
-    fn ge(&self, other: & &'self const T) -> bool {
+    fn ge(&self, other: & &'self T) -> bool {
         *(*self) >= *(*other)
     }
     #[inline(always)]
-    fn gt(&self, other: & &'self const T) -> bool {
+    fn gt(&self, other: & &'self T) -> bool {
         *(*self) > *(*other)
     }
 }
diff --git a/src/libcore/rt/env.rs b/src/libcore/rt/env.rs
index 92e2ec51306e2..1d7ff17314901 100644
--- a/src/libcore/rt/env.rs
+++ b/src/libcore/rt/env.rs
@@ -31,8 +31,10 @@ pub struct Environment {
     argc: c_int,
     /// The argv value passed to main
     argv: **c_char,
-    /// Print GC debugging info
-    debug_mem: bool
+    /// Print GC debugging info (true if env var RUST_DEBUG_MEM is set)
+    debug_mem: bool,
+    /// Print GC debugging info (true if env var RUST_DEBUG_BORROW is set)
+    debug_borrow: bool,
 }
 
 /// Get the global environment settings
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index d7543ae138c40..663fe3e62d010 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -137,7 +137,6 @@ pub impl Scheduler {
     /// Called by a running task to end execution, after which it will
     /// be recycled by the scheduler for reuse in a new task.
     fn terminate_current_task(~self) {
-        let mut self = self;
         assert!(self.in_task_context());
 
         rtdebug!("ending running task");
@@ -153,7 +152,6 @@ pub impl Scheduler {
     }
 
     fn schedule_new_task(~self, task: ~Task) {
-        let mut self = self;
         assert!(self.in_task_context());
 
         do self.switch_running_tasks_and_then(task) |last_task| {
@@ -305,7 +303,7 @@ pub impl Scheduler {
         unsafe {
             let last_task = transmute::<Option<&Task>, Option<&mut Task>>(last_task);
             let last_task_context = match last_task {
-                Some(ref t) => Some(&mut t.saved_context), None => None
+                Some(t) => Some(&mut t.saved_context), None => None
             };
             let next_task_context = match self.current_task {
                 Some(ref mut t) => Some(&mut t.saved_context), None => None
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index f3181b26bd7e0..a41c99b266b11 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -2472,9 +2472,6 @@ pub trait StrSlice<'self> {
     fn any(&self, it: &fn(char) -> bool) -> bool;
     fn contains<'a>(&self, needle: &'a str) -> bool;
     fn contains_char(&self, needle: char) -> bool;
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    #[cfg(stage3)]
     fn char_iter(&self) -> StrCharIterator<'self>;
     fn each(&self, it: &fn(u8) -> bool);
     fn eachi(&self, it: &fn(uint, u8) -> bool);
@@ -2536,9 +2533,6 @@ impl<'self> StrSlice<'self> for &'self str {
         contains_char(*self, needle)
     }
 
-    #[cfg(stage1)]
-    #[cfg(stage2)]
-    #[cfg(stage3)]
     #[inline]
     fn char_iter(&self) -> StrCharIterator<'self> {
         StrCharIterator {
@@ -2732,17 +2726,11 @@ impl Clone for ~str {
     }
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 pub struct StrCharIterator<'self> {
     priv index: uint,
     priv string: &'self str,
 }
 
-#[cfg(stage1)]
-#[cfg(stage2)]
-#[cfg(stage3)]
 impl<'self> Iterator<char> for StrCharIterator<'self> {
     #[inline]
     fn next(&mut self) -> Option<char> {
diff --git a/src/libcore/to_bytes.rs b/src/libcore/to_bytes.rs
index e563a304f0988..9e4da7ab48868 100644
--- a/src/libcore/to_bytes.rs
+++ b/src/libcore/to_bytes.rs
@@ -19,7 +19,7 @@ use io::Writer;
 use option::{None, Option, Some};
 use str;
 
-pub type Cb<'self> = &'self fn(buf: &const [u8]) -> bool;
+pub type Cb<'self> = &'self fn(buf: &[u8]) -> bool;
 
 /**
  * A trait to implement in order to make a type hashable;
diff --git a/src/libcore/unstable/extfmt.rs b/src/libcore/unstable/extfmt.rs
index f65b1e6726ed4..258da9ff38310 100644
--- a/src/libcore/unstable/extfmt.rs
+++ b/src/libcore/unstable/extfmt.rs
@@ -501,7 +501,7 @@ pub mod rt {
     pub fn conv_int(cv: Conv, i: int, buf: &mut ~str) {
         let radix = 10;
         let prec = get_int_precision(cv);
-        let mut s : ~str = uint_to_str_prec(int::abs(i) as uint, radix, prec);
+        let s : ~str = uint_to_str_prec(int::abs(i) as uint, radix, prec);
 
         let head = if i >= 0 {
             if have_flag(cv.flags, flag_sign_always) {
@@ -516,7 +516,7 @@ pub mod rt {
     }
     pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) {
         let prec = get_int_precision(cv);
-        let mut rs =
+        let rs =
             match cv.ty {
               TyDefault => uint_to_str_prec(u, 10, prec),
               TyHexLower => uint_to_str_prec(u, 16, prec),
@@ -559,7 +559,7 @@ pub mod rt {
               CountIs(c) => (float::to_str_exact, c as uint),
               CountImplied => (float::to_str_digits, 6u)
         };
-        let mut s = to_str(f, digits);
+        let s = to_str(f, digits);
         let head = if 0.0 <= f {
             if have_flag(cv.flags, flag_sign_always) {
                 Some('+')
diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs
index 7f6faa81012e8..8153c2d43d998 100644
--- a/src/libcore/unstable/lang.rs
+++ b/src/libcore/unstable/lang.rs
@@ -10,8 +10,9 @@
 
 //! Runtime calls emitted by the compiler.
 
+use uint;
 use cast::transmute;
-use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
+use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO};
 use managed::raw::BoxRepr;
 use str;
 use sys;
@@ -19,17 +20,19 @@ use unstable::exchange_alloc;
 use cast::transmute;
 use rt::{context, OldTaskContext};
 use rt::local_services::borrow_local_services;
+use option::{Option, Some, None};
+use io;
 
 #[allow(non_camel_case_types)]
 pub type rust_task = c_void;
 
-#[cfg(target_word_size = "32")]
-pub static FROZEN_BIT: uint = 0x80000000;
-#[cfg(target_word_size = "64")]
-pub static FROZEN_BIT: uint = 0x8000000000000000;
+pub static FROZEN_BIT: uint = 1 << (uint::bits - 1);
+pub static MUT_BIT: uint = 1 << (uint::bits - 2);
+static ALL_BITS: uint = FROZEN_BIT | MUT_BIT;
 
 pub mod rustrt {
-    use libc::{c_char, uintptr_t};
+    use unstable::lang::rust_task;
+    use libc::{c_void, c_char, uintptr_t};
 
     pub extern {
         #[rust_stack]
@@ -45,6 +48,17 @@ pub mod rustrt {
 
         #[fast_ffi]
         unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
+
+        #[rust_stack]
+        fn rust_take_task_borrow_list(task: *rust_task) -> *c_void;
+
+        #[rust_stack]
+        fn rust_set_task_borrow_list(task: *rust_task, map: *c_void);
+
+        #[rust_stack]
+        fn rust_try_get_task() -> *rust_task;
+
+        fn rust_dbg_breakpoint();
     }
 }
 
@@ -55,7 +69,7 @@ pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
 
 #[lang="fail_bounds_check"]
 pub fn fail_bounds_check(file: *c_char, line: size_t,
-                                index: size_t, len: size_t) {
+                         index: size_t, len: size_t) {
     let msg = fmt!("index out of bounds: the len is %d but the index is %d",
                     len as int, index as int);
     do str::as_buf(msg) |p, _len| {
@@ -63,11 +77,74 @@ pub fn fail_bounds_check(file: *c_char, line: size_t,
     }
 }
 
-pub fn fail_borrowed() {
-    let msg = "borrowed";
-    do str::as_buf(msg) |msg_p, _| {
-        do str::as_buf("???") |file_p, _| {
-            fail_(msg_p as *c_char, file_p as *c_char, 0);
+#[deriving(Eq)]
+struct BorrowRecord {
+    box: *mut BoxRepr,
+    file: *c_char,
+    line: size_t
+}
+
+fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> {
+    unsafe {
+        let cur_task: *rust_task = rustrt::rust_try_get_task();
+        if cur_task.is_not_null() {
+            let ptr = rustrt::rust_take_task_borrow_list(cur_task);
+            if ptr.is_null() {
+                None
+            } else {
+                let v: ~[BorrowRecord] = transmute(ptr);
+                Some(v)
+            }
+        } else {
+            None
+        }
+    }
+}
+
+fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) {
+    unsafe {
+        let cur_task: *rust_task = rustrt::rust_try_get_task();
+        if cur_task.is_not_null() {
+            let mut borrow_list: ~[BorrowRecord] = {
+                let ptr = rustrt::rust_take_task_borrow_list(cur_task);
+                if ptr.is_null() { ~[] } else { transmute(ptr) }
+            };
+            borrow_list = f(borrow_list);
+            rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list));
+        }
+    }
+}
+
+pub unsafe fn clear_task_borrow_list() {
+    // pub because it is used by the box annihilator.
+    let _ = try_take_task_borrow_list();
+}
+
+unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) {
+    debug_borrow("fail_borrowed: ", box, 0, 0, file, line);
+
+    match try_take_task_borrow_list() {
+        None => { // not recording borrows
+            let msg = "borrowed";
+            do str::as_buf(msg) |msg_p, _| {
+                fail_(msg_p as *c_char, file, line);
+            }
+        }
+        Some(borrow_list) => { // recording borrows
+            let mut msg = ~"borrowed";
+            let mut sep = " at ";
+            for borrow_list.each_reverse |entry| {
+                if entry.box == box {
+                    str::push_str(&mut msg, sep);
+                    let filename = str::raw::from_c_str(entry.file);
+                    str::push_str(&mut msg, filename);
+                    str::push_str(&mut msg, fmt!(":%u", entry.line as uint));
+                    sep = " and at ";
+                }
+            }
+            do str::as_buf(msg) |msg_p, _| {
+                fail_(msg_p as *c_char, file, line)
+            }
         }
     }
 }
@@ -79,6 +156,77 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
     transmute(exchange_alloc::malloc(transmute(td), transmute(size)))
 }
 
+/// Because this code is so perf. sensitive, use a static constant so that
+/// debug printouts are compiled out most of the time.
+static ENABLE_DEBUG: bool = false;
+
+#[inline]
+unsafe fn debug_borrow<T>(tag: &'static str,
+                          p: *const T,
+                          old_bits: uint,
+                          new_bits: uint,
+                          filename: *c_char,
+                          line: size_t) {
+    //! A useful debugging function that prints a pointer + tag + newline
+    //! without allocating memory.
+
+    if ENABLE_DEBUG && ::rt::env::get().debug_borrow {
+        debug_borrow_slow(tag, p, old_bits, new_bits, filename, line);
+    }
+
+    unsafe fn debug_borrow_slow<T>(tag: &'static str,
+                                   p: *const T,
+                                   old_bits: uint,
+                                   new_bits: uint,
+                                   filename: *c_char,
+                                   line: size_t) {
+        let dbg = STDERR_FILENO as io::fd_t;
+        dbg.write_str(tag);
+        dbg.write_hex(p as uint);
+        dbg.write_str(" ");
+        dbg.write_hex(old_bits);
+        dbg.write_str(" ");
+        dbg.write_hex(new_bits);
+        dbg.write_str(" ");
+        dbg.write_cstr(filename);
+        dbg.write_str(":");
+        dbg.write_hex(line as uint);
+        dbg.write_str("\n");
+    }
+}
+
+trait DebugPrints {
+    fn write_hex(&self, val: uint);
+    unsafe fn write_cstr(&self, str: *c_char);
+}
+
+impl DebugPrints for io::fd_t {
+    fn write_hex(&self, mut i: uint) {
+        let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
+                       '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+        static uint_nibbles: uint = ::uint::bytes << 1;
+        let mut buffer = [0_u8, ..uint_nibbles+1];
+        let mut c = uint_nibbles;
+        while c > 0 {
+            c -= 1;
+            buffer[c] = letters[i & 0xF] as u8;
+            i >>= 4;
+        }
+        self.write(buffer.slice(0, uint_nibbles));
+    }
+
+    unsafe fn write_cstr(&self, p: *c_char) {
+        use libc::strlen;
+        use vec;
+
+        let len = strlen(p);
+        let p: *u8 = transmute(p);
+        do vec::raw::buf_as_slice(p, len as uint) |s| {
+            self.write(s);
+        }
+    }
+}
+
 // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
 // inside a landing pad may corrupt the state of the exception handler. If a
 // problem occurs, call exit instead.
@@ -121,6 +269,7 @@ pub unsafe fn local_free(ptr: *c_char) {
     }
 }
 
+#[cfg(stage0)]
 #[lang="borrow_as_imm"]
 #[inline(always)]
 pub unsafe fn borrow_as_imm(a: *u8) {
@@ -128,6 +277,86 @@ pub unsafe fn borrow_as_imm(a: *u8) {
     (*a).header.ref_count |= FROZEN_BIT;
 }
 
+#[cfg(not(stage0))]
+#[lang="borrow_as_imm"]
+#[inline(always)]
+pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint {
+    let a: *mut BoxRepr = transmute(a);
+    let old_ref_count = (*a).header.ref_count;
+    let new_ref_count = old_ref_count | FROZEN_BIT;
+
+    debug_borrow("borrow_as_imm:", a, old_ref_count, new_ref_count, file, line);
+
+    if (old_ref_count & MUT_BIT) != 0 {
+        fail_borrowed(a, file, line);
+    }
+
+    (*a).header.ref_count = new_ref_count;
+
+    old_ref_count
+}
+
+#[cfg(not(stage0))]
+#[lang="borrow_as_mut"]
+#[inline(always)]
+pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint {
+    let a: *mut BoxRepr = transmute(a);
+    let old_ref_count = (*a).header.ref_count;
+    let new_ref_count = old_ref_count | MUT_BIT | FROZEN_BIT;
+
+    debug_borrow("borrow_as_mut:", a, old_ref_count, new_ref_count, file, line);
+
+    if (old_ref_count & (MUT_BIT|FROZEN_BIT)) != 0 {
+        fail_borrowed(a, file, line);
+    }
+
+    (*a).header.ref_count = new_ref_count;
+
+    old_ref_count
+}
+
+
+#[cfg(not(stage0))]
+#[lang="record_borrow"]
+pub unsafe fn record_borrow(a: *u8, old_ref_count: uint,
+                            file: *c_char, line: size_t) {
+    if (old_ref_count & ALL_BITS) == 0 {
+        // was not borrowed before
+        let a: *mut BoxRepr = transmute(a);
+        debug_borrow("record_borrow:", a, old_ref_count, 0, file, line);
+        do swap_task_borrow_list |borrow_list| {
+            let mut borrow_list = borrow_list;
+            borrow_list.push(BorrowRecord {box: a, file: file, line: line});
+            borrow_list
+        }
+    }
+}
+
+#[cfg(not(stage0))]
+#[lang="unrecord_borrow"]
+pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint,
+                              file: *c_char, line: size_t) {
+    if (old_ref_count & ALL_BITS) == 0 {
+        // was not borrowed before, so we should find the record at
+        // the end of the list
+        let a: *mut BoxRepr = transmute(a);
+        debug_borrow("unrecord_borrow:", a, old_ref_count, 0, file, line);
+        do swap_task_borrow_list |borrow_list| {
+            let mut borrow_list = borrow_list;
+            assert!(!borrow_list.is_empty());
+            let br = borrow_list.pop();
+            if br.box != a || br.file != file || br.line != line {
+                let err = fmt!("wrong borrow found, br=%?", br);
+                do str::as_buf(err) |msg_p, _| {
+                    fail_(msg_p as *c_char, file, line)
+                }
+            }
+            borrow_list
+        }
+    }
+}
+
+#[cfg(stage0)]
 #[lang="return_to_mut"]
 #[inline(always)]
 pub unsafe fn return_to_mut(a: *u8) {
@@ -139,12 +368,49 @@ pub unsafe fn return_to_mut(a: *u8) {
     }
 }
 
+#[cfg(not(stage0))]
+#[lang="return_to_mut"]
+#[inline(always)]
+pub unsafe fn return_to_mut(a: *u8, orig_ref_count: uint,
+                            file: *c_char, line: size_t) {
+    // Sometimes the box is null, if it is conditionally frozen.
+    // See e.g. #4904.
+    if !a.is_null() {
+        let a: *mut BoxRepr = transmute(a);
+        let old_ref_count = (*a).header.ref_count;
+        let new_ref_count =
+            (old_ref_count & !ALL_BITS) | (orig_ref_count & ALL_BITS);
+
+        debug_borrow("return_to_mut:",
+                     a, old_ref_count, new_ref_count, file, line);
+
+        (*a).header.ref_count = new_ref_count;
+    }
+}
+
+#[cfg(stage0)]
 #[lang="check_not_borrowed"]
 #[inline(always)]
 pub unsafe fn check_not_borrowed(a: *u8) {
     let a: *mut BoxRepr = transmute(a);
     if ((*a).header.ref_count & FROZEN_BIT) != 0 {
-        fail_borrowed();
+        do str::as_buf("XXX") |file_p, _| {
+            fail_borrowed(a, file_p as *c_char, 0);
+        }
+    }
+}
+
+#[cfg(not(stage0))]
+#[lang="check_not_borrowed"]
+#[inline(always)]
+pub unsafe fn check_not_borrowed(a: *u8,
+                                 file: *c_char,
+                                 line: size_t) {
+    let a: *mut BoxRepr = transmute(a);
+    let ref_count = (*a).header.ref_count;
+    debug_borrow("check_not_borrowed:", a, ref_count, 0, file, line);
+    if (ref_count & FROZEN_BIT) != 0 {
+        fail_borrowed(a, file, line);
     }
 }
 
diff --git a/src/libcore/util.rs b/src/libcore/util.rs
index a08e38c021fad..43616ebfd3032 100644
--- a/src/libcore/util.rs
+++ b/src/libcore/util.rs
@@ -26,19 +26,20 @@ pub fn ignore<T>(_x: T) { }
 
 /// Sets `*ptr` to `new_value`, invokes `op()`, and then restores the
 /// original value of `*ptr`.
+///
+/// NB: This function accepts `@mut T` and not `&mut T` to avoid
+/// an obvious borrowck hazard. Typically passing in `&mut T` will
+/// cause borrow check errors because it freezes whatever location
+/// that `&mut T` is stored in (either statically or dynamically).
 #[inline(always)]
-pub fn with<T:Copy,R>(
-    ptr: &mut T,
-    new_value: T,
+pub fn with<T,R>(
+    ptr: @mut T,
+    mut value: T,
     op: &fn() -> R) -> R
 {
-    // NDM: if swap operator were defined somewhat differently,
-    // we wouldn't need to copy...
-
-    let old_value = *ptr;
-    *ptr = new_value;
+    value <-> *ptr;
     let result = op();
-    *ptr = old_value;
+    *ptr = value;
     return result;
 }
 
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 6ffb0b30917a3..ced3c300a359a 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -1821,17 +1821,14 @@ pub trait CopyableVector<T> {
 }
 
 /// Extension methods for vectors
-impl<'self,T:Copy> CopyableVector<T> for &'self const [T] {
+impl<'self,T:Copy> CopyableVector<T> for &'self [T] {
     /// Returns a copy of `v`.
     #[inline]
     fn to_owned(&self) -> ~[T] {
         let mut result = ~[];
-        // FIXME: #4568
-        unsafe {
-            reserve(&mut result, self.len());
-            for self.each |e| {
-                result.push(copy *e);
-            }
+        reserve(&mut result, self.len());
+        for self.each |e| {
+            result.push(copy *e);
         }
         result
 
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index de6469e81807d..c34c7fe303ee2 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -788,7 +788,7 @@ pub fn link_binary(sess: Session,
     };
 
     debug!("output: %s", output.to_str());
-    let mut cc_args = link_args(sess, obj_filename, out_filename, lm);
+    let cc_args = link_args(sess, obj_filename, out_filename, lm);
     debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" "));
     // We run 'cc' here
     let prog = run::program_output(cc_prog, cc_args);
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 62ff58b44816a..5e8dab0f77287 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -262,7 +262,7 @@ pub fn compile_rest(sess: Session,
              middle::check_loop::check_crate(ty_cx, crate));
 
         let middle::moves::MoveMaps {moves_map, variable_moves_map,
-                                     capture_map} =
+                                     moved_variables_set, capture_map} =
             time(time_passes, ~"compute moves", ||
                  middle::moves::compute_moves(ty_cx, method_map, crate));
 
@@ -270,20 +270,19 @@ pub fn compile_rest(sess: Session,
              middle::check_match::check_crate(ty_cx, method_map,
                                               moves_map, crate));
 
-        let last_use_map =
-            time(time_passes, ~"liveness checking", ||
-                 middle::liveness::check_crate(ty_cx, method_map,
-                                               variable_moves_map,
-                                               capture_map, crate));
+        time(time_passes, ~"liveness checking", ||
+             middle::liveness::check_crate(ty_cx, method_map,
+                                           variable_moves_map,
+                                           capture_map, crate));
 
-        let (root_map, mutbl_map, write_guard_map) =
+        let (root_map, write_guard_map) =
             time(time_passes, ~"borrow checking", ||
                  middle::borrowck::check_crate(ty_cx, method_map,
-                                               moves_map, capture_map,
-                                               crate));
+                                               moves_map, moved_variables_set,
+                                               capture_map, crate));
 
         time(time_passes, ~"kind checking", ||
-             kind::check_crate(ty_cx, method_map, last_use_map, crate));
+             kind::check_crate(ty_cx, method_map, crate));
 
         time(time_passes, ~"lint checking", ||
              lint::check_crate(ty_cx, crate));
@@ -291,9 +290,7 @@ pub fn compile_rest(sess: Session,
         if upto == cu_no_trans { return (crate, Some(ty_cx)); }
 
         let maps = astencode::Maps {
-            mutbl_map: mutbl_map,
             root_map: root_map,
-            last_use_map: last_use_map,
             method_map: method_map,
             vtable_map: vtable_map,
             write_guard_map: write_guard_map,
@@ -607,11 +604,6 @@ pub fn build_session_options(binary: @~str,
     let target_opt = getopts::opt_maybe_str(matches, ~"target");
     let target_feature_opt = getopts::opt_maybe_str(matches, ~"target-feature");
     let save_temps = getopts::opt_present(matches, ~"save-temps");
-    match output_type {
-      // unless we're emitting huamn-readable assembly, omit comments.
-      link::output_type_llvm_assembly | link::output_type_assembly => (),
-      _ => debugging_opts |= session::no_asm_comments
-    }
     let opt_level = {
         if (debugging_opts & session::no_opt) != 0 {
             No
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index c7b336de09112..582e1d606bca6 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -45,7 +45,7 @@ pub static time_passes: uint = 1 << 1;
 pub static count_llvm_insns: uint = 1 << 2;
 pub static time_llvm_passes: uint = 1 << 3;
 pub static trans_stats: uint = 1 << 4;
-pub static no_asm_comments: uint = 1 << 5;
+pub static asm_comments: uint = 1 << 5;
 pub static no_verify: uint = 1 << 6;
 pub static trace: uint = 1 << 7;
 pub static coherence: uint = 1 << 8;
@@ -73,7 +73,7 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
      (~"time-llvm-passes", ~"measure time of each LLVM pass",
       time_llvm_passes),
      (~"trans-stats", ~"gather trans statistics", trans_stats),
-     (~"no-asm-comments", ~"omit comments when using -S", no_asm_comments),
+     (~"asm-comments", ~"generate comments into the assembly (may change behavior)", asm_comments),
      (~"no-verify", ~"skip LLVM verification", no_verify),
      (~"trace", ~"emit trace logs", trace),
      (~"coherence", ~"perform coherence checking", coherence),
@@ -188,6 +188,9 @@ pub impl Session_ {
     fn err(@self, msg: &str) {
         self.span_diagnostic.handler().err(msg)
     }
+    fn err_count(@self) -> uint {
+        self.span_diagnostic.handler().err_count()
+    }
     fn has_errors(@self) -> bool {
         self.span_diagnostic.handler().has_errors()
     }
@@ -263,7 +266,7 @@ pub impl Session_ {
     }
     fn trans_stats(@self) -> bool { self.debugging_opt(trans_stats) }
     fn meta_stats(@self) -> bool { self.debugging_opt(meta_stats) }
-    fn no_asm_comments(@self) -> bool { self.debugging_opt(no_asm_comments) }
+    fn asm_comments(@self) -> bool { self.debugging_opt(asm_comments) }
     fn no_verify(@self) -> bool { self.debugging_opt(no_verify) }
     fn trace(@self) -> bool { self.debugging_opt(trace) }
     fn coherence(@self) -> bool { self.debugging_opt(coherence) }
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index ddd702e7d697d..0646afa126283 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -69,7 +69,8 @@ fn generate_test_harness(sess: session::Session,
         testfns: ~[]
     };
 
-    cx.ext_cx.bt_push(ExpandedFrom(CallInfo {
+    let ext_cx = cx.ext_cx;
+    ext_cx.bt_push(ExpandedFrom(CallInfo {
         call_site: dummy_sp(),
         callee: NameAndSpan {
             name: ~"test",
@@ -84,7 +85,7 @@ fn generate_test_harness(sess: session::Session,
 
     let fold = fold::make_fold(precursor);
     let res = @fold.fold_crate(&*crate);
-    cx.ext_cx.bt_pop();
+    ext_cx.bt_pop();
     return res;
 }
 
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 1347fec5d667a..1e6bb39706844 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -244,8 +244,8 @@ fn doc_transformed_self_ty(doc: ebml::Doc,
     }
 }
 
-pub fn item_type(_: ast::def_id, item: ebml::Doc, tcx: ty::ctxt, cdata: cmd)
-                 -> ty::t {
+pub fn item_type(_item_id: ast::def_id, item: ebml::Doc,
+                 tcx: ty::ctxt, cdata: cmd) -> ty::t {
     doc_type(item, tcx, cdata)
 }
 
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 7db362c692030..ccf3ffcdfffcd 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -198,7 +198,7 @@ fn encode_type_param_bounds(ebml_w: &mut writer::Encoder,
                             ecx: @EncodeContext,
                             params: &OptVec<TyParam>) {
     let ty_param_defs =
-        @params.map_to_vec(|param| *ecx.tcx.ty_param_defs.get(&param.id));
+        @params.map_to_vec(|param| ecx.tcx.ty_param_defs.get_copy(&param.id));
     encode_ty_type_param_defs(ebml_w, ecx, ty_param_defs,
                               tag_items_data_item_ty_param_bounds);
 }
@@ -288,7 +288,7 @@ fn encode_discriminant(ecx: @EncodeContext,
                        ebml_w: &mut writer::Encoder,
                        id: node_id) {
     ebml_w.start_tag(tag_items_data_item_symbol);
-    ebml_w.writer.write(str::to_bytes(**ecx.discrim_symbols.get(&id)));
+    ebml_w.writer.write(str::to_bytes(*ecx.discrim_symbols.get_copy(&id)));
     ebml_w.end_tag();
 }
 
@@ -1036,7 +1036,7 @@ fn encode_info_for_items(ecx: @EncodeContext,
             let ebml_w = copy *ebml_w;
             |i, cx, v| {
                 visit::visit_item(i, cx, v);
-                match *ecx.tcx.items.get(&i.id) {
+                match ecx.tcx.items.get_copy(&i.id) {
                     ast_map::node_item(_, pt) => {
                         let mut ebml_w = copy ebml_w;
                         encode_info_for_item(ecx, &mut ebml_w, i, index, *pt);
@@ -1049,7 +1049,7 @@ fn encode_info_for_items(ecx: @EncodeContext,
             let ebml_w = copy *ebml_w;
             |ni, cx, v| {
                 visit::visit_foreign_item(ni, cx, v);
-                match *ecx.tcx.items.get(&ni.id) {
+                match ecx.tcx.items.get_copy(&ni.id) {
                     ast_map::node_foreign_item(_, abi, _, pt) => {
                         let mut ebml_w = copy ebml_w;
                         encode_info_for_foreign_item(ecx,
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index b12db430afc31..02acafbd98099 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -245,6 +245,9 @@ fn parse_region(st: @mut PState) -> ty::Region {
       't' => {
         ty::re_static
       }
+      'e' => {
+        ty::re_static
+      }
       _ => fail!(~"parse_region: bad input")
     }
 }
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 0548204b154e0..c44a8e74130fd 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -71,30 +71,29 @@ pub fn enc_ty(w: @io::Writer, cx: @ctxt, t: ty::t) {
         w.write_str(result_str);
       }
       ac_use_abbrevs(abbrevs) => {
-        match abbrevs.find(&t) {
-          Some(a) => { w.write_str(*a.s); return; }
-          None => {
-            let pos = w.tell();
-            enc_sty(w, cx, /*bad*/copy ty::get(t).sty);
-            let end = w.tell();
-            let len = end - pos;
-            fn estimate_sz(u: uint) -> uint {
-                let mut n = u;
-                let mut len = 0u;
-                while n != 0u { len += 1u; n = n >> 4u; }
-                return len;
-            }
-            let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len);
-            if abbrev_len < len {
-                // I.e. it's actually an abbreviation.
-                let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" +
-                    uint::to_str_radix(len, 16u) + ~"#";
-                let a = ty_abbrev { pos: pos, len: len, s: @s };
-                abbrevs.insert(t, a);
-            }
-            return;
+          match abbrevs.find(&t) {
+              Some(a) => { w.write_str(*a.s); return; }
+              None => {}
           }
-        }
+          let pos = w.tell();
+          enc_sty(w, cx, /*bad*/copy ty::get(t).sty);
+          let end = w.tell();
+          let len = end - pos;
+          fn estimate_sz(u: uint) -> uint {
+              let mut n = u;
+              let mut len = 0u;
+              while n != 0u { len += 1u; n = n >> 4u; }
+              return len;
+          }
+          let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len);
+          if abbrev_len < len {
+              // I.e. it's actually an abbreviation.
+              let s = ~"#" + uint::to_str_radix(pos, 16u) + ~":" +
+                  uint::to_str_radix(len, 16u) + ~"#";
+              let a = ty_abbrev { pos: pos, len: len, s: @s };
+              abbrevs.insert(t, a);
+          }
+          return;
       }
     }
 }
@@ -152,6 +151,9 @@ fn enc_region(w: @io::Writer, cx: @ctxt, r: ty::Region) {
       ty::re_static => {
         w.write_char('t');
       }
+      ty::re_empty => {
+        w.write_char('e');
+      }
       ty::re_infer(_) => {
         // these should not crop up after typeck
         cx.diag.handler().bug(~"Cannot encode region variables");
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index c6f01153a906e..f3eac7995e8a1 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -44,9 +44,7 @@ use writer = std::ebml::writer;
 
 // Auxiliary maps of things to be encoded
 pub struct Maps {
-    mutbl_map: middle::borrowck::mutbl_map,
     root_map: middle::borrowck::root_map,
-    last_use_map: middle::liveness::last_use_map,
     method_map: middle::typeck::method_map,
     vtable_map: middle::typeck::vtable_map,
     write_guard_map: middle::borrowck::write_guard_map,
@@ -152,7 +150,7 @@ pub fn decode_inlined_item(cdata: @cstore::crate_metadata,
 fn reserve_id_range(sess: Session,
                     from_id_range: ast_util::id_range) -> ast_util::id_range {
     // Handle the case of an empty range:
-    if ast_util::empty(from_id_range) { return from_id_range; }
+    if from_id_range.empty() { return from_id_range; }
     let cnt = from_id_range.max - from_id_range.min;
     let to_id_min = sess.parse_sess.next_id;
     let to_id_max = sess.parse_sess.next_id + cnt;
@@ -163,7 +161,6 @@ fn reserve_id_range(sess: Session,
 pub impl ExtendedDecodeContext {
     fn tr_id(&self, id: ast::node_id) -> ast::node_id {
         /*!
-         *
          * Translates an internal id, meaning a node id that is known
          * to refer to some part of the item currently being inlined,
          * such as a local variable or argument.  All naked node-ids
@@ -174,12 +171,11 @@ pub impl ExtendedDecodeContext {
          */
 
         // from_id_range should be non-empty
-        assert!(!ast_util::empty(self.from_id_range));
+        assert!(!self.from_id_range.empty());
         (id - self.from_id_range.min + self.to_id_range.min)
     }
     fn tr_def_id(&self, did: ast::def_id) -> ast::def_id {
         /*!
-         *
          * Translates an EXTERNAL def-id, converting the crate number
          * from the one used in the encoded data to the current crate
          * numbers..  By external, I mean that it be translated to a
@@ -204,7 +200,6 @@ pub impl ExtendedDecodeContext {
     }
     fn tr_intern_def_id(&self, did: ast::def_id) -> ast::def_id {
         /*!
-         *
          * Translates an INTERNAL def-id, meaning a def-id that is
          * known to refer to some part of the item currently being
          * inlined.  In that case, we want to convert the def-id to
@@ -435,11 +430,7 @@ impl tr for ty::AutoAdjustment {
 
 impl tr for ty::AutoRef {
     fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::AutoRef {
-        ty::AutoRef {
-            kind: self.kind,
-            region: self.region.tr(xcx),
-            mutbl: self.mutbl,
-        }
+        self.map_region(|r| r.tr(xcx))
     }
 }
 
@@ -448,7 +439,7 @@ impl tr for ty::Region {
         match *self {
             ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
             ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
-            ty::re_static | ty::re_infer(*) => *self,
+            ty::re_empty | ty::re_static | ty::re_infer(*) => *self,
             ty::re_free(ref fr) => {
                 ty::re_free(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id),
                                             bound_region: fr.bound_region.tr(xcx)})
@@ -724,7 +715,7 @@ trait ebml_writer_helpers {
     fn emit_arg(&mut self, ecx: @e::EncodeContext, arg: ty::arg);
     fn emit_ty(&mut self, ecx: @e::EncodeContext, ty: ty::t);
     fn emit_vstore(&mut self, ecx: @e::EncodeContext, vstore: ty::vstore);
-    fn emit_tys(&mut self, ecx: @e::EncodeContext, tys: ~[ty::t]);
+    fn emit_tys(&mut self, ecx: @e::EncodeContext, tys: &[ty::t]);
     fn emit_type_param_def(&mut self,
                            ecx: @e::EncodeContext,
                            type_param_def: &ty::TypeParameterDef);
@@ -752,7 +743,7 @@ impl ebml_writer_helpers for writer::Encoder {
         }
     }
 
-    fn emit_tys(&mut self, ecx: @e::EncodeContext, tys: ~[ty::t]) {
+    fn emit_tys(&mut self, ecx: @e::EncodeContext, tys: &[ty::t]) {
         do self.emit_from_vec(tys) |this, ty| {
             this.emit_ty(ecx, *ty)
         }
@@ -859,9 +850,7 @@ fn encode_side_tables_for_id(ecx: @e::EncodeContext,
         do ebml_w.tag(c::tag_table_node_type_subst) |ebml_w| {
             ebml_w.id(id);
             do ebml_w.tag(c::tag_table_val) |ebml_w| {
-                // FIXME(#5562): removing this copy causes a segfault
-                //               before stage2
-                ebml_w.emit_tys(ecx, /*bad*/copy **tys)
+                ebml_w.emit_tys(ecx, **tys)
             }
         }
     }
@@ -896,23 +885,6 @@ fn encode_side_tables_for_id(ecx: @e::EncodeContext,
         }
     }
 
-    if maps.mutbl_map.contains(&id) {
-        do ebml_w.tag(c::tag_table_mutbl) |ebml_w| {
-            ebml_w.id(id);
-        }
-    }
-
-    for maps.last_use_map.find(&id).each |&m| {
-        do ebml_w.tag(c::tag_table_last_use) |ebml_w| {
-            ebml_w.id(id);
-            do ebml_w.tag(c::tag_table_val) |ebml_w| {
-                do ebml_w.emit_from_vec(/*bad*/ copy **m) |ebml_w, id| {
-                    id.encode(ebml_w);
-                }
-            }
-        }
-    }
-
     for maps.method_map.find(&id).each |&mme| {
         do ebml_w.tag(c::tag_table_method_map) |ebml_w| {
             ebml_w.id(id);
@@ -1113,9 +1085,7 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
                 found for id %d (orig %d)",
                tag, id, id0);
 
-        if tag == (c::tag_table_mutbl as uint) {
-            dcx.maps.mutbl_map.insert(id);
-        } else if tag == (c::tag_table_moves_map as uint) {
+        if tag == (c::tag_table_moves_map as uint) {
             dcx.maps.moves_map.insert(id);
         } else {
             let val_doc = entry_doc.get(c::tag_table_val as uint);
@@ -1144,11 +1114,6 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
             } else if tag == (c::tag_table_param_defs as uint) {
                 let bounds = val_dsr.read_type_param_def(xcx);
                 dcx.tcx.ty_param_defs.insert(id, bounds);
-            } else if tag == (c::tag_table_last_use as uint) {
-                let ids = val_dsr.read_to_vec(|val_dsr| {
-                    xcx.tr_id(val_dsr.read_int())
-                });
-                dcx.maps.last_use_map.insert(id, @mut ids);
             } else if tag == (c::tag_table_method_map as uint) {
                 dcx.maps.method_map.insert(
                     id,
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 4ab24a17d31b2..ba719fe34d719 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -18,284 +18,143 @@
 // 4. moves do not affect things loaned out in any way
 
 use middle::moves;
-use middle::typeck::check::PurityState;
-use middle::borrowck::{Loan, bckerr, BorrowckCtxt, inherent_mutability};
-use middle::borrowck::{ReqMaps, root_map_key, save_and_restore_managed};
-use middle::borrowck::{MoveError, MoveOk, MoveFromIllegalCmt};
-use middle::borrowck::{MoveWhileBorrowed};
-use middle::mem_categorization::{cat_arg, cat_comp, cat_deref};
-use middle::mem_categorization::{cat_local, cat_rvalue, cat_self};
-use middle::mem_categorization::{cat_special, cmt, gc_ptr, loan_path, lp_arg};
-use middle::mem_categorization::{lp_comp, lp_deref, lp_local};
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
 use middle::ty;
-use util::ppaux::ty_to_str;
-
+use util::ppaux::Repr;
 use core::hashmap::HashSet;
-use core::util::with;
-use syntax::ast::m_mutbl;
+use syntax::ast::{m_mutbl, m_imm, m_const};
 use syntax::ast;
 use syntax::ast_util;
-use syntax::codemap::span;
-use syntax::print::pprust;
 use syntax::visit;
+use syntax::codemap::span;
 
-struct CheckLoanCtxt {
+struct CheckLoanCtxt<'self> {
     bccx: @BorrowckCtxt,
-    req_maps: ReqMaps,
-
-    reported: HashSet<ast::node_id>,
-
-    declared_purity: @mut PurityState,
-    fn_args: @mut @~[ast::node_id]
-}
-
-// if we are enforcing purity, why are we doing so?
-#[deriving(Eq)]
-enum purity_cause {
-    // enforcing purity because fn was declared pure:
-    pc_pure_fn,
-
-    // enforce purity because we need to guarantee the
-    // validity of some alias; `bckerr` describes the
-    // reason we needed to enforce purity.
-    pc_cmt(bckerr)
-}
-
-// if we're not pure, why?
-#[deriving(Eq)]
-enum impurity_cause {
-    // some surrounding block was marked as 'unsafe'
-    pc_unsafe,
-
-    // nothing was unsafe, and nothing was pure
-    pc_default,
+    dfcx: &'self LoanDataFlow,
+    all_loans: &'self [Loan],
+    reported: @mut HashSet<ast::node_id>,
 }
 
 pub fn check_loans(bccx: @BorrowckCtxt,
-                   req_maps: ReqMaps,
-                   crate: @ast::crate) {
+                   dfcx: &LoanDataFlow,
+                   all_loans: &[Loan],
+                   body: &ast::blk) {
+    debug!("check_loans(body id=%?)", body.node.id);
+
     let clcx = @mut CheckLoanCtxt {
         bccx: bccx,
-        req_maps: req_maps,
-        reported: HashSet::new(),
-        declared_purity: @mut PurityState::function(ast::impure_fn, 0),
-        fn_args: @mut @~[]
+        dfcx: dfcx,
+        all_loans: all_loans,
+        reported: @mut HashSet::new(),
     };
+
     let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr,
                                            visit_local: check_loans_in_local,
                                            visit_block: check_loans_in_block,
+                                           visit_pat: check_loans_in_pat,
                                            visit_fn: check_loans_in_fn,
                                            .. *visit::default_visitor()});
-    visit::visit_crate(crate, clcx, vt);
+    (vt.visit_block)(body, clcx, vt);
 }
 
-#[deriving(Eq)]
-enum assignment_type {
-    at_straight_up,
-    at_swap
-}
-
-pub impl assignment_type {
-    fn checked_by_liveness(&self) -> bool {
-        // the liveness pass guarantees that immutable local variables
-        // are only assigned once; but it doesn't consider &mut
-        match *self {
-          at_straight_up => true,
-          at_swap => true
-        }
-    }
-    fn ing_form(&self, desc: ~str) -> ~str {
-        match *self {
-          at_straight_up => ~"assigning to " + desc,
-          at_swap => ~"swapping to and from " + desc
-        }
-    }
+enum MoveError {
+    MoveOk,
+    MoveFromIllegalCmt(mc::cmt),
+    MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
 }
 
-pub impl CheckLoanCtxt {
+pub impl<'self> CheckLoanCtxt<'self> {
     fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
 
-    fn purity(&mut self, scope_id: ast::node_id)
-                -> Either<purity_cause, impurity_cause>
+    fn each_issued_loan(&self,
+                        scope_id: ast::node_id,
+                        op: &fn(&Loan) -> bool)
     {
-        let default_purity = match self.declared_purity.purity {
-          // an unsafe declaration overrides all
-          ast::unsafe_fn => return Right(pc_unsafe),
-
-          // otherwise, remember what was declared as the
-          // default, but we must scan for requirements
-          // imposed by the borrow check
-          ast::pure_fn => Left(pc_pure_fn),
-          ast::extern_fn | ast::impure_fn => Right(pc_default)
-        };
-
-        // scan to see if this scope or any enclosing scope requires
-        // purity.  if so, that overrides the declaration.
-
-        let mut scope_id = scope_id;
-        loop {
-            match self.req_maps.pure_map.find(&scope_id) {
-              None => (),
-              Some(e) => return Left(pc_cmt(*e))
-            }
-
-            match self.tcx().region_maps.opt_encl_scope(scope_id) {
-              None => return default_purity,
-              Some(next_scope_id) => scope_id = next_scope_id
+        //! Iterates over each loan that that has been issued
+        //! on entrance to `scope_id`, regardless of whether it is
+        //! actually *in scope* at that point.  Sometimes loans
+        //! are issued for future scopes and thus they may have been
+        //! *issued* but not yet be in effect.
+
+        for self.dfcx.each_bit_on_entry(scope_id) |loan_index| {
+            let loan = &self.all_loans[loan_index];
+            if !op(loan) {
+                return;
             }
         }
     }
 
-    fn walk_loans(&self,
-                  mut scope_id: ast::node_id,
-                  f: &fn(v: &Loan) -> bool) {
-
-        loop {
-            for self.req_maps.req_loan_map.find(&scope_id).each |loans| {
-                for loans.each |loan| {
-                    if !f(loan) { return; }
-                }
-            }
-
-            match self.tcx().region_maps.opt_encl_scope(scope_id) {
-              None => return,
-              Some(next_scope_id) => scope_id = next_scope_id,
-            }
-        }
-    }
-
-    fn walk_loans_of(&mut self,
-                     scope_id: ast::node_id,
-                     lp: @loan_path,
-                     f: &fn(v: &Loan) -> bool) {
-        for self.walk_loans(scope_id) |loan| {
-            if loan.lp == lp {
-                if !f(loan) { return; }
-            }
-        }
-    }
+    fn each_in_scope_loan(&self,
+                          scope_id: ast::node_id,
+                          op: &fn(&Loan) -> bool)
+    {
+        //! Like `each_issued_loan()`, but only considers loans that are
+        //! currently in scope.
 
-    // when we are in a pure context, we check each call to ensure
-    // that the function which is invoked is itself pure.
-    //
-    // note: we take opt_expr and expr_id separately because for
-    // overloaded operators the callee has an id but no expr.
-    // annoying.
-    fn check_pure_callee_or_arg(&mut self,
-                                pc: Either<purity_cause, impurity_cause>,
-                                opt_expr: Option<@ast::expr>,
-                                callee_id: ast::node_id,
-                                callee_span: span) {
-        let tcx = self.tcx();
-
-        debug!("check_pure_callee_or_arg(pc=%?, expr=%?, \
-                callee_id=%d, ty=%s)",
-               pc,
-               opt_expr.map(|e| pprust::expr_to_str(*e, tcx.sess.intr()) ),
-               callee_id,
-               ty_to_str(self.tcx(), ty::node_id_to_type(tcx, callee_id)));
-
-        // Purity rules: an expr B is a legal callee or argument to a
-        // call within a pure function A if at least one of the
-        // following holds:
-        //
-        // (a) A was declared pure and B is one of its arguments;
-        // (b) B is a stack closure;
-        // (c) B is a pure fn;
-        // (d) B is not a fn.
-
-        match opt_expr {
-          Some(expr) => {
-            match expr.node {
-              ast::expr_path(_) if pc == Left(pc_pure_fn) => {
-                let def = *self.tcx().def_map.get(&expr.id);
-                let did = ast_util::def_id_of_def(def);
-                let is_fn_arg =
-                    did.crate == ast::local_crate &&
-                    (*self.fn_args).contains(&(did.node));
-                if is_fn_arg { return; } // case (a) above
-              }
-              ast::expr_fn_block(*) | ast::expr_loop_body(*) |
-              ast::expr_do_body(*) => {
-                if self.is_stack_closure(expr.id) {
-                    // case (b) above
+        let region_maps = self.tcx().region_maps;
+        for self.each_issued_loan(scope_id) |loan| {
+            if region_maps.is_subscope_of(scope_id, loan.kill_scope) {
+                if !op(loan) {
                     return;
                 }
-              }
-              _ => ()
             }
-          }
-          None => ()
         }
+    }
 
-        let callee_ty = ty::node_id_to_type(tcx, callee_id);
-        match ty::get(callee_ty).sty {
-            ty::ty_bare_fn(ty::BareFnTy {purity: purity, _}) |
-            ty::ty_closure(ty::ClosureTy {purity: purity, _}) => {
-                match purity {
-                    ast::pure_fn => return, // case (c) above
-                    ast::impure_fn | ast::unsafe_fn | ast::extern_fn => {
-                        self.report_purity_error(
-                            pc, callee_span,
-                            fmt!("access to %s function",
-                                 purity.to_str()));
+    fn each_in_scope_restriction(&self,
+                                 scope_id: ast::node_id,
+                                 loan_path: @LoanPath,
+                                 op: &fn(&Loan, &Restriction) -> bool)
+    {
+        //! Iterates through all the in-scope restrictions for the
+        //! given `loan_path`
+
+        for self.each_in_scope_loan(scope_id) |loan| {
+            for loan.restrictions.each |restr| {
+                if restr.loan_path == loan_path {
+                    if !op(loan, restr) {
+                        return;
                     }
                 }
             }
-            _ => return, // case (d) above
         }
     }
 
-    // True if the expression with the given `id` is a stack closure.
-    // The expression must be an expr_fn_block(*)
-    fn is_stack_closure(&mut self, id: ast::node_id) -> bool {
-        let fn_ty = ty::node_id_to_type(self.tcx(), id);
-        match ty::get(fn_ty).sty {
-            ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
-                                          _}) => true,
-            _ => false
-        }
-    }
+    fn loans_generated_by(&self, scope_id: ast::node_id) -> ~[uint] {
+        //! Returns a vector of the loans that are generated as
+        //! we encounter `scope_id`.
 
-    fn is_allowed_pure_arg(&mut self, expr: @ast::expr) -> bool {
-        return match expr.node {
-          ast::expr_path(_) => {
-            let def = *self.tcx().def_map.get(&expr.id);
-            let did = ast_util::def_id_of_def(def);
-            did.crate == ast::local_crate &&
-                (*self.fn_args).contains(&(did.node))
-          }
-          ast::expr_fn_block(*) => self.is_stack_closure(expr.id),
-          _ => false,
-        };
+        let mut result = ~[];
+        for self.dfcx.each_gen_bit(scope_id) |loan_index| {
+            result.push(loan_index);
+        }
+        return result;
     }
 
     fn check_for_conflicting_loans(&mut self, scope_id: ast::node_id) {
-        debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
-
-        let new_loans = match self.req_maps.req_loan_map.find(&scope_id) {
-            None => return,
-            Some(&loans) => loans
-        };
-        let new_loans: &mut ~[Loan] = new_loans;
+        //! Checks to see whether any of the loans that are issued
+        //! by `scope_id` conflict with loans that have already been
+        //! issued when we enter `scope_id` (for example, we do not
+        //! permit two `&mut` borrows of the same variable).
 
-        debug!("new_loans has length %?", new_loans.len());
+        debug!("check_for_conflicting_loans(scope_id=%?)", scope_id);
 
-        let par_scope_id = self.tcx().region_maps.encl_scope(scope_id);
-        for self.walk_loans(par_scope_id) |old_loan| {
-            debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan));
+        let new_loan_indices = self.loans_generated_by(scope_id);
+        debug!("new_loan_indices = %?", new_loan_indices);
 
-            for new_loans.each |new_loan| {
-                self.report_error_if_loans_conflict(old_loan, new_loan);
+        for self.each_issued_loan(scope_id) |issued_loan| {
+            for new_loan_indices.each |&new_loan_index| {
+                let new_loan = &self.all_loans[new_loan_index];
+                self.report_error_if_loans_conflict(issued_loan, new_loan);
             }
         }
 
-        let len = new_loans.len();
-        for uint::range(0, len) |i| {
-            let loan_i = new_loans[i];
-            for uint::range(i+1, len) |j| {
-                let loan_j = new_loans[j];
-                self.report_error_if_loans_conflict(&loan_i, &loan_j);
+        for uint::range(0, new_loan_indices.len()) |i| {
+            let old_loan = &self.all_loans[new_loan_indices[i]];
+            for uint::range(i+1, new_loan_indices.len()) |j| {
+                let new_loan = &self.all_loans[new_loan_indices[j]];
+                self.report_error_if_loans_conflict(old_loan, new_loan);
             }
         }
     }
@@ -303,219 +162,366 @@ pub impl CheckLoanCtxt {
     fn report_error_if_loans_conflict(&self,
                                       old_loan: &Loan,
                                       new_loan: &Loan) {
-        if old_loan.lp != new_loan.lp {
-            return;
-        }
+        //! Checks whether `old_loan` and `new_loan` can safely be issued
+        //! simultaneously.
+
+        debug!("report_error_if_loans_conflict(old_loan=%s, new_loan=%s)",
+               old_loan.repr(self.tcx()),
+               new_loan.repr(self.tcx()));
+
+        // Should only be called for loans that are in scope at the same time.
+        let region_maps = self.tcx().region_maps;
+        assert!(region_maps.scopes_intersect(old_loan.kill_scope,
+                                             new_loan.kill_scope));
+
+        self.report_error_if_loan_conflicts_with_restriction(
+            old_loan, new_loan, old_loan, new_loan) &&
+        self.report_error_if_loan_conflicts_with_restriction(
+            new_loan, old_loan, old_loan, new_loan);
+    }
 
-        match (old_loan.kind, new_loan.kind) {
-            (PartialFreeze, PartialTake) | (PartialTake, PartialFreeze) |
-            (TotalFreeze, PartialFreeze) | (PartialFreeze, TotalFreeze) |
-            (Immobile, _) | (_, Immobile) |
-            (PartialFreeze, PartialFreeze) |
-            (PartialTake, PartialTake) |
-            (TotalFreeze, TotalFreeze) => {
-                /* ok */
-            }
+    fn report_error_if_loan_conflicts_with_restriction(&self,
+                                                       loan1: &Loan,
+                                                       loan2: &Loan,
+                                                       old_loan: &Loan,
+                                                       new_loan: &Loan) -> bool {
+        //! Checks whether the restrictions introduced by `loan1` would
+        //! prohibit `loan2`. Returns false if an error is reported.
+
+        debug!("report_error_if_loan_conflicts_with_restriction(\
+                loan1=%s, loan2=%s)",
+               loan1.repr(self.tcx()),
+               loan2.repr(self.tcx()));
+
+        // Restrictions that would cause the new loan to be immutable:
+        let illegal_if = match loan2.mutbl {
+            m_mutbl => RESTR_ALIAS | RESTR_FREEZE | RESTR_MUTATE,
+            m_imm =>   RESTR_ALIAS | RESTR_FREEZE,
+            m_const => RESTR_ALIAS,
+        };
+        debug!("illegal_if=%?", illegal_if);
 
-            (PartialTake, TotalFreeze) | (TotalFreeze, PartialTake) |
-            (TotalTake, TotalFreeze) | (TotalFreeze, TotalTake) |
-            (TotalTake, PartialFreeze) | (PartialFreeze, TotalTake) |
-            (TotalTake, PartialTake) | (PartialTake, TotalTake) |
-            (TotalTake, TotalTake) => {
-                self.bccx.span_err(
-                    new_loan.cmt.span,
-                    fmt!("loan of %s as %s \
-                          conflicts with prior loan",
-                         self.bccx.cmt_to_str(new_loan.cmt),
-                         self.bccx.loan_kind_to_str(new_loan.kind)));
-                self.bccx.span_note(
-                    old_loan.cmt.span,
-                    fmt!("prior loan as %s granted here",
-                         self.bccx.loan_kind_to_str(old_loan.kind)));
+        for loan1.restrictions.each |restr| {
+            if !restr.set.intersects(illegal_if) { loop; }
+            if restr.loan_path != loan2.loan_path { loop; }
+
+            match (new_loan.mutbl, old_loan.mutbl) {
+                (m_mutbl, m_mutbl) => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        fmt!("cannot borrow `%s` as mutable \
+                              more than once at at a time",
+                             self.bccx.loan_path_to_str(new_loan.loan_path)));
+                    self.bccx.span_note(
+                        old_loan.span,
+                        fmt!("second borrow of `%s` as mutable occurs here",
+                             self.bccx.loan_path_to_str(new_loan.loan_path)));
+                    return false;
+                }
+
+                _ => {
+                    self.bccx.span_err(
+                        new_loan.span,
+                        fmt!("cannot borrow `%s` as %s because \
+                              it is also borrowed as %s"
+                             self.bccx.loan_path_to_str(new_loan.loan_path),
+                             self.bccx.mut_to_str(new_loan.mutbl),
+                             self.bccx.mut_to_str(old_loan.mutbl)));
+                    self.bccx.span_note(
+                        old_loan.span,
+                        fmt!("second borrow of `%s` occurs here",
+                             self.bccx.loan_path_to_str(new_loan.loan_path)));
+                    return false;
+                }
             }
         }
+
+        true
     }
 
-    fn is_local_variable(&self, cmt: cmt) -> bool {
+    fn is_local_variable(&self, cmt: mc::cmt) -> bool {
         match cmt.cat {
-          cat_local(_) => true,
+          mc::cat_local(_) => true,
           _ => false
         }
     }
 
-    fn check_assignment(&mut self, at: assignment_type, ex: @ast::expr) {
+    fn check_assignment(&self, expr: @ast::expr) {
         // We don't use cat_expr() here because we don't want to treat
         // auto-ref'd parameters in overloaded operators as rvalues.
-        let cmt = match self.bccx.tcx.adjustments.find(&ex.id) {
-            None => self.bccx.cat_expr_unadjusted(ex),
-            Some(&adj) => self.bccx.cat_expr_autoderefd(ex, adj)
+        let cmt = match self.bccx.tcx.adjustments.find(&expr.id) {
+            None => self.bccx.cat_expr_unadjusted(expr),
+            Some(&adj) => self.bccx.cat_expr_autoderefd(expr, adj)
         };
 
-        debug!("check_assignment(cmt=%s)",
-               self.bccx.cmt_to_repr(cmt));
-
-        if self.is_local_variable(cmt) && at.checked_by_liveness() {
-            // liveness guarantees that immutable local variables
-            // are only assigned once
-        } else {
-            match cmt.mutbl {
-                McDeclared | McInherited => {
-                    // Ok, but if this loan is a mutable loan, then mark the
-                    // loan path (if it exists) as being used. This is similar
-                    // to the check performed in loan.rs in issue_loan(). This
-                    // type of use of mutable is different from issuing a loan,
-                    // however.
-                    for cmt.lp.each |lp| {
-                        for lp.node_id().each |&id| {
-                            self.tcx().used_mut_nodes.insert(id);
-                        }
+        debug!("check_assignment(cmt=%s)", cmt.repr(self.tcx()));
+
+        // check that the value being assigned is declared as mutable
+        // and report an error otherwise.
+        match cmt.mutbl {
+            mc::McDeclared => {
+                // OK, but we have to mark arguments as requiring mut
+                // if they are assigned (other cases are handled by liveness,
+                // since we need to distinguish local variables assigned
+                // once vs those assigned multiple times)
+                match cmt.cat {
+                    mc::cat_self(*) |
+                    mc::cat_arg(*) => {
+                        mark_variable_as_used_mut(self, cmt);
                     }
+                    _ => {}
                 }
-                McReadOnly | McImmutable => {
+            }
+            mc::McInherited => {
+                // OK, but we may have to add an entry to `used_mut_nodes`
+                mark_variable_as_used_mut(self, cmt);
+            }
+            mc::McReadOnly | mc::McImmutable => {
+                // Subtle: liveness guarantees that immutable local
+                // variables are only assigned once, so no need to
+                // report an error for an assignment to a local
+                // variable (note also that it is not legal to borrow
+                // for a local variable before it has been assigned
+                // for the first time).
+                if !self.is_local_variable(cmt) {
                     self.bccx.span_err(
-                        ex.span,
-                        at.ing_form(self.bccx.cmt_to_str(cmt)));
-                    return;
+                        expr.span,
+                        fmt!("cannot assign to %s %s"
+                             cmt.mutbl.to_user_str(),
+                             self.bccx.cmt_to_str(cmt)));
                 }
+                return;
             }
         }
 
-        // if this is a pure function, only loan-able state can be
-        // assigned, because it is uniquely tied to this function and
-        // is not visible from the outside
-        let purity = self.purity(ex.id);
-        match purity {
-          Right(_) => (),
-          Left(pc_cmt(_)) => {
-            // Subtle: Issue #3162.  If we are enforcing purity
-            // because there is a reference to aliasable, mutable data
-            // that we require to be immutable, we can't allow writes
-            // even to data owned by the current stack frame.  This is
-            // because that aliasable data might have been located on
-            // the current stack frame, we don't know.
-            self.report_purity_error(
-                purity,
-                ex.span,
-                at.ing_form(self.bccx.cmt_to_str(cmt)));
-          }
-          Left(pc_pure_fn) => {
-            if cmt.lp.is_none() {
-                self.report_purity_error(
-                    purity, ex.span,
-                    at.ing_form(self.bccx.cmt_to_str(cmt)));
-            }
-          }
+        if check_for_aliasable_mutable_writes(self, expr, cmt) {
+            check_for_assignment_to_restricted_or_frozen_location(
+                self, expr, cmt);
         }
 
-        // check for a conflicting loan as well, except in the case of
-        // taking a mutable ref.  that will create a loan of its own
-        // which will be checked for compat separately in
-        // check_for_conflicting_loans()
-        for cmt.lp.each |lp| {
-            self.check_for_loan_conflicting_with_assignment(
-                at, ex, cmt, *lp);
-        }
+        fn mark_variable_as_used_mut(self: &CheckLoanCtxt,
+                                     cmt: mc::cmt) {
+            //! If the mutability of the `cmt` being written is inherited
+            //! from a local variable, liveness will
+            //! not have been able to detect that this variable's mutability
+            //! is important, so we must add the variable to the
+            //! `used_mut_nodes` table here.
+
+            let mut cmt = cmt;
+            loop {
+                debug!("mark_writes_through_upvars_as_used_mut(cmt=%s)",
+                       cmt.repr(self.tcx()));
+                match cmt.cat {
+                    mc::cat_local(id) |
+                    mc::cat_arg(id) |
+                    mc::cat_self(id) => {
+                        self.tcx().used_mut_nodes.insert(id);
+                        return;
+                    }
 
-        self.bccx.add_to_mutbl_map(cmt);
+                    mc::cat_stack_upvar(b) => {
+                        cmt = b;
+                    }
 
-        // Check for and insert write guards as necessary.
-        self.add_write_guards_if_necessary(cmt);
-    }
+                    mc::cat_rvalue |
+                    mc::cat_static_item |
+                    mc::cat_implicit_self |
+                    mc::cat_copied_upvar(*) |
+                    mc::cat_deref(_, _, mc::unsafe_ptr(*)) |
+                    mc::cat_deref(_, _, mc::gc_ptr(*)) |
+                    mc::cat_deref(_, _, mc::region_ptr(*)) => {
+                        assert_eq!(cmt.mutbl, mc::McDeclared);
+                        return;
+                    }
 
-    fn add_write_guards_if_necessary(&mut self, cmt: cmt) {
-        match cmt.cat {
-            cat_deref(base, deref_count, ptr_kind) => {
-                self.add_write_guards_if_necessary(base);
-
-                match ptr_kind {
-                    gc_ptr(ast::m_mutbl) => {
-                        let key = root_map_key {
-                            id: base.id,
-                            derefs: deref_count
-                        };
-                        self.bccx.write_guard_map.insert(key);
+                    mc::cat_discr(b, _) |
+                    mc::cat_deref(b, _, mc::uniq_ptr(*)) => {
+                        assert_eq!(cmt.mutbl, mc::McInherited);
+                        cmt = b;
+                    }
+
+                    mc::cat_interior(b, _) => {
+                        if cmt.mutbl == mc::McInherited {
+                            cmt = b;
+                        } else {
+                            return; // field declared as mutable or some such
+                        }
                     }
-                    _ => {}
                 }
             }
-            cat_comp(base, _) => {
-                self.add_write_guards_if_necessary(base);
-            }
-            _ => {}
         }
-    }
 
-    fn check_for_loan_conflicting_with_assignment(&mut self,
-                                                  at: assignment_type,
-                                                  ex: @ast::expr,
-                                                  cmt: cmt,
-                                                  lp: @loan_path) {
-        for self.walk_loans_of(ex.id, lp) |loan| {
-            match loan.kind {
-                Immobile => { /* ok */ }
-                TotalFreeze | PartialFreeze |
-                TotalTake | PartialTake => {
-                    self.bccx.span_err(
-                        ex.span,
-                        fmt!("%s prohibited due to outstanding loan",
-                             at.ing_form(self.bccx.cmt_to_str(cmt))));
-                    self.bccx.span_note(
-                        loan.cmt.span,
-                        fmt!("loan of %s granted here",
-                             self.bccx.cmt_to_str(loan.cmt)));
-                    return;
+        fn check_for_aliasable_mutable_writes(self: &CheckLoanCtxt,
+                                              expr: @ast::expr,
+                                              cmt: mc::cmt) -> bool {
+            //! Safety checks related to writes to aliasable, mutable locations
+
+            let guarantor = cmt.guarantor();
+            debug!("check_for_aliasable_mutable_writes(cmt=%s, guarantor=%s)",
+                   cmt.repr(self.tcx()), guarantor.repr(self.tcx()));
+            match guarantor.cat {
+                mc::cat_deref(b, _, mc::region_ptr(m_mutbl, _)) => {
+                    // Statically prohibit writes to `&mut` when aliasable
+
+                    match b.freely_aliasable() {
+                        None => {}
+                        Some(cause) => {
+                            self.bccx.report_aliasability_violation(
+                                expr.span,
+                                MutabilityViolation,
+                                cause);
+                        }
+                    }
+                }
+
+                mc::cat_deref(_, deref_count, mc::gc_ptr(ast::m_mutbl)) => {
+                    // Dynamically check writes to `@mut`
+
+                    let key = root_map_key {
+                        id: guarantor.id,
+                        derefs: deref_count
+                    };
+                    debug!("Inserting write guard at %?", key);
+                    self.bccx.write_guard_map.insert(key);
                 }
+
+                _ => {}
             }
-        }
 
-        // Subtle: if the mutability of the component being assigned
-        // is inherited from the thing that the component is embedded
-        // within, then we have to check whether that thing has been
-        // loaned out as immutable!  An example:
-        //    let mut x = {f: Some(3)};
-        //    let y = &x; // x loaned out as immutable
-        //    x.f = none; // changes type of y.f, which appears to be imm
-        match *lp {
-          lp_comp(lp_base, ck) if inherent_mutability(ck) != m_mutbl => {
-            self.check_for_loan_conflicting_with_assignment(
-                at, ex, cmt, lp_base);
-          }
-          lp_comp(*) | lp_self | lp_local(*) | lp_arg(*) | lp_deref(*) => ()
+            return true; // no errors reported
         }
-    }
 
-    fn report_purity_error(&mut self, pc: Either<purity_cause, impurity_cause>,
-                           sp: span, msg: ~str) {
-        match pc {
-          Right(pc_default) => { fail!(~"pc_default should be filtered sooner") }
-          Right(pc_unsafe) => {
-            // this error was prevented by being marked as unsafe, so flag the
-            // definition as having contributed to the validity of the program
-            let def = self.declared_purity.def;
-            debug!("flagging %? as a used unsafe source", def);
-            self.tcx().used_unsafe.insert(def);
-          }
-          Left(pc_pure_fn) => {
-            self.tcx().sess.span_err(
-                sp,
-                fmt!("%s prohibited in pure context", msg));
-          }
-          Left(pc_cmt(ref e)) => {
-            if self.reported.insert((*e).cmt.id) {
-                self.tcx().sess.span_err(
-                    (*e).cmt.span,
-                    fmt!("illegal borrow unless pure: %s",
-                         self.bccx.bckerr_to_str((*e))));
-                self.bccx.note_and_explain_bckerr((*e));
-                self.tcx().sess.span_note(
-                    sp,
-                    fmt!("impure due to %s", msg));
+        fn check_for_assignment_to_restricted_or_frozen_location(
+            self: &CheckLoanCtxt,
+            expr: @ast::expr,
+            cmt: mc::cmt) -> bool
+        {
+            //! Check for assignments that violate the terms of an
+            //! outstanding loan.
+
+            let loan_path = match opt_loan_path(cmt) {
+                Some(lp) => lp,
+                None => { return true; /* no loan path, can't be any loans */ }
+            };
+
+            // Start by searching for an assignment to a *restricted*
+            // location. Here is one example of the kind of error caught
+            // by this check:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &v;
+            //    v = ~[4];
+            //
+            // In this case, creating `p` triggers a RESTR_MUTATE
+            // restriction on the path `v`.
+            //
+            // Here is a second, more subtle example:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &const v[0];
+            //    v[0] = 4;                   // OK
+            //    v[1] = 5;                   // OK
+            //    v = ~[4, 5, 3];             // Error
+            //
+            // In this case, `p` is pointing to `v[0]`, and it is a
+            // `const` pointer in any case. So the first two
+            // assignments are legal (and would be permitted by this
+            // check). However, the final assignment (which is
+            // logically equivalent) is forbidden, because it would
+            // cause the existing `v` array to be freed, thus
+            // invalidating `p`. In the code, this error results
+            // because `gather_loans::restrictions` adds a
+            // `RESTR_MUTATE` restriction whenever the contents of an
+            // owned pointer are borrowed, and hence while `v[*]` is not
+            // restricted from being written, `v` is.
+            for self.each_in_scope_restriction(expr.id, loan_path)
+                |loan, restr|
+            {
+                if restr.set.intersects(RESTR_MUTATE) {
+                    self.report_illegal_mutation(expr, loan_path, loan);
+                    return false;
+                }
+            }
+
+            // The previous code handled assignments to paths that
+            // have been restricted. This covers paths that have been
+            // directly lent out and their base paths, but does not
+            // cover random extensions of those paths. For example,
+            // the following program is not declared illegal by the
+            // previous check:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &v;
+            //    v[0] = 4; // declared error by loop below, not code above
+            //
+            // The reason that this passes the previous check whereas
+            // an assignment like `v = ~[4]` fails is because the assignment
+            // here is to `v[*]`, and the existing restrictions were issued
+            // for `v`, not `v[*]`.
+            //
+            // So in this loop, we walk back up the loan path so long
+            // as the mutability of the path is dependent on a super
+            // path, and check that the super path was not lent out as
+            // mutable or immutable (a const loan is ok).
+            //
+            // Note that we are *not* checking for any and all
+            // restrictions.  We are only interested in the pointers
+            // that the user created, whereas we add restrictions for
+            // all kinds of paths that are not directly aliased. If we checked
+            // for all restrictions, and not just loans, then the following
+            // valid program would be considered illegal:
+            //
+            //    let mut v = ~[1, 2, 3];
+            //    let p = &const v[0];
+            //    v[1] = 5; // ok
+            //
+            // Here the restriction that `v` not be mutated would be misapplied
+            // to block the subpath `v[1]`.
+            let full_loan_path = loan_path;
+            let mut loan_path = loan_path;
+            loop {
+                match *loan_path {
+                    // Peel back one layer if `loan_path` has
+                    // inherited mutability
+                    LpExtend(lp_base, mc::McInherited, _) => {
+                        loan_path = lp_base;
+                    }
+
+                    // Otherwise stop iterating
+                    LpExtend(_, mc::McDeclared, _) |
+                    LpExtend(_, mc::McImmutable, _) |
+                    LpExtend(_, mc::McReadOnly, _) |
+                    LpVar(_) => {
+                        return true;
+                    }
+                }
+
+                // Check for a non-const loan of `loan_path`
+                for self.each_in_scope_loan(expr.id) |loan| {
+                    if loan.loan_path == loan_path && loan.mutbl != m_const {
+                        self.report_illegal_mutation(expr, full_loan_path, loan);
+                        return false;
+                    }
+                }
             }
-          }
         }
     }
 
-    fn check_move_out_from_expr(@mut self, ex: @ast::expr) {
+    fn report_illegal_mutation(&self,
+                               expr: @ast::expr,
+                               loan_path: &LoanPath,
+                               loan: &Loan) {
+        self.bccx.span_err(
+            expr.span,
+            fmt!("cannot assign to `%s` because it is borrowed",
+                 self.bccx.loan_path_to_str(loan_path)));
+        self.bccx.span_note(
+            loan.span,
+            fmt!("borrow of `%s` occurs here",
+                 self.bccx.loan_path_to_str(loan_path)));
+    }
+
+    fn check_move_out_from_expr(&self, ex: @ast::expr) {
         match ex.node {
             ast::expr_paren(*) => {
                 /* In the case of an expr_paren(), the expression inside
@@ -529,52 +535,57 @@ pub impl CheckLoanCtxt {
                     MoveFromIllegalCmt(_) => {
                         self.bccx.span_err(
                             cmt.span,
-                            fmt!("moving out of %s",
+                            fmt!("cannot move out of %s",
                                  self.bccx.cmt_to_str(cmt)));
                     }
-                    MoveWhileBorrowed(_, loan_cmt) => {
+                    MoveWhileBorrowed(loan_path, loan_span) => {
                         self.bccx.span_err(
                             cmt.span,
-                            fmt!("moving out of %s prohibited \
-                                  due to outstanding loan",
-                                 self.bccx.cmt_to_str(cmt)));
+                            fmt!("cannot move out of `%s` \
+                                  because it is borrowed",
+                                 self.bccx.loan_path_to_str(loan_path)));
                         self.bccx.span_note(
-                            loan_cmt.span,
-                            fmt!("loan of %s granted here",
-                                 self.bccx.cmt_to_str(loan_cmt)));
+                            loan_span,
+                            fmt!("borrow of `%s` occurs here",
+                                 self.bccx.loan_path_to_str(loan_path)));
                     }
                 }
             }
         }
     }
 
-    fn analyze_move_out_from_cmt(&mut self, cmt: cmt) -> MoveError {
-        debug!("check_move_out_from_cmt(cmt=%s)",
-               self.bccx.cmt_to_repr(cmt));
+    fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
+        debug!("check_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
 
         match cmt.cat {
-          // Rvalues, locals, and arguments can be moved:
-          cat_rvalue | cat_local(_) | cat_arg(_) | cat_self(_) => {}
-
-          // We allow moving out of static items because the old code
-          // did.  This seems consistent with permitting moves out of
-          // rvalues, I guess.
-          cat_special(sk_static_item) => {}
-
-          cat_deref(_, _, unsafe_ptr) => {}
-
-          // Nothing else.
-          _ => {
-              return MoveFromIllegalCmt(cmt);
-          }
+            // Rvalues, locals, and arguments can be moved:
+            mc::cat_rvalue | mc::cat_local(_) |
+            mc::cat_arg(_) | mc::cat_self(_) => {}
+
+            // It seems strange to allow a move out of a static item,
+            // but what happens in practice is that you have a
+            // reference to a constant with a type that should be
+            // moved, like `None::<~int>`.  The type of this constant
+            // is technically `Option<~int>`, which moves, but we know
+            // that the content of static items will never actually
+            // contain allocated pointers, so we can just memcpy it.
+            mc::cat_static_item => {}
+
+            mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {}
+
+            // Nothing else.
+            _ => {
+                return MoveFromIllegalCmt(cmt);
+            }
         }
 
-        self.bccx.add_to_mutbl_map(cmt);
+        // FIXME(#4384) inadequare if/when we permit `move a.b`
 
         // check for a conflicting loan:
-        for cmt.lp.each |lp| {
-            for self.walk_loans_of(cmt.id, *lp) |loan| {
-                return MoveWhileBorrowed(cmt, loan.cmt);
+        for opt_loan_path(cmt).each |&lp| {
+            for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
+                // Any restriction prevents moves.
+                return MoveWhileBorrowed(loan.loan_path, loan.span);
             }
         }
 
@@ -582,103 +593,45 @@ pub impl CheckLoanCtxt {
     }
 
     fn check_call(&mut self,
-                  expr: @ast::expr,
-                  callee: Option<@ast::expr>,
-                  callee_id: ast::node_id,
-                  callee_span: span,
-                  args: &[@ast::expr]) {
-        let pc = self.purity(expr.id);
-        match pc {
-            // no purity, no need to check for anything
-            Right(pc_default) => return,
-
-            // some form of purity, definitely need to check
-            Left(_) => (),
-
-            // Unsafe trumped. To see if the unsafe is necessary, see what the
-            // purity would have been without a trump, and if it's some form
-            // of purity then we need to go ahead with the check
-            Right(pc_unsafe) => {
-                match do with(&mut self.declared_purity.purity,
-                              ast::impure_fn) { self.purity(expr.id) } {
-                    Right(pc_unsafe) => fail!(~"unsafe can't trump twice"),
-                    Right(pc_default) => return,
-                    Left(_) => ()
-                }
-            }
-
-        }
-        self.check_pure_callee_or_arg(
-            pc, callee, callee_id, callee_span);
-        for args.each |arg| {
-            self.check_pure_callee_or_arg(
-                pc, Some(*arg), arg.id, arg.span);
-        }
+                  _expr: @ast::expr,
+                  _callee: Option<@ast::expr>,
+                  _callee_id: ast::node_id,
+                  _callee_span: span,
+                  _args: &[@ast::expr])
+    {
+        // NB: This call to check for conflicting loans is not truly
+        // necessary, because the callee_id never issues new loans.
+        // However, I added it for consistency and lest the system
+        // should change in the future.
+        //
+        // FIXME(#6268) nested method calls
+        // self.check_for_conflicting_loans(callee_id);
     }
 }
 
-fn check_loans_in_fn(fk: &visit::fn_kind,
-                     decl: &ast::fn_decl,
-                     body: &ast::blk,
-                     sp: span,
-                     id: ast::node_id,
-                     self: @mut CheckLoanCtxt,
-                     visitor: visit::vt<@mut CheckLoanCtxt>) {
-    let is_stack_closure = self.is_stack_closure(id);
-    let fty = ty::node_id_to_type(self.tcx(), id);
-
-    let declared_purity, src;
+fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
+                         decl: &ast::fn_decl,
+                         body: &ast::blk,
+                         sp: span,
+                         id: ast::node_id,
+                         self: @mut CheckLoanCtxt<'a>,
+                         visitor: visit::vt<@mut CheckLoanCtxt<'a>>) {
     match *fk {
-        visit::fk_item_fn(*) | visit::fk_method(*) => {
-            declared_purity = ty::ty_fn_purity(fty);
-            src = id;
+        visit::fk_item_fn(*) |
+        visit::fk_method(*) => {
+            // Don't process nested items.
+            return;
         }
 
-        visit::fk_anon(*) | visit::fk_fn_block(*) => {
+        visit::fk_anon(*) |
+        visit::fk_fn_block(*) => {
+            let fty = ty::node_id_to_type(self.tcx(), id);
             let fty_sigil = ty::ty_closure_sigil(fty);
             check_moves_from_captured_variables(self, id, fty_sigil);
-            let pair = ty::determine_inherited_purity(
-                (self.declared_purity.purity, self.declared_purity.def),
-                (ty::ty_fn_purity(fty), id),
-                fty_sigil);
-            declared_purity = pair.first();
-            src = pair.second();
         }
     }
 
-    debug!("purity on entry=%?", copy self.declared_purity);
-    do save_and_restore_managed(self.declared_purity) {
-        do save_and_restore_managed(self.fn_args) {
-            self.declared_purity = @mut PurityState::function(declared_purity, src);
-
-            match *fk {
-                visit::fk_anon(*) |
-                visit::fk_fn_block(*) if is_stack_closure => {
-                    // inherits the fn_args from enclosing ctxt
-                }
-                visit::fk_anon(*) | visit::fk_fn_block(*) |
-                visit::fk_method(*) | visit::fk_item_fn(*) => {
-                    let mut fn_args = ~[];
-                    for decl.inputs.each |input| {
-                        // For the purposes of purity, only consider function-
-                        // typed bindings in trivial patterns to be function
-                        // arguments. For example, do not allow `f` and `g` in
-                        // (f, g): (&fn(), &fn()) to be called.
-                        match input.pat.node {
-                            ast::pat_ident(_, _, None) => {
-                                fn_args.push(input.pat.id);
-                            }
-                            _ => {} // Ignore this argument.
-                        }
-                    }
-                    *self.fn_args = @fn_args;
-                }
-            }
-
-            visit::visit_fn(fk, decl, body, sp, id, self, visitor);
-        }
-    }
-    debug!("purity on exit=%?", copy self.declared_purity);
+    visit::visit_fn(fk, decl, body, sp, id, self, visitor);
 
     fn check_moves_from_captured_variables(self: @mut CheckLoanCtxt,
                                            id: ast::node_id,
@@ -704,16 +657,16 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
                                 fmt!("illegal by-move capture of %s",
                                      self.bccx.cmt_to_str(move_cmt)));
                         }
-                        MoveWhileBorrowed(move_cmt, loan_cmt) => {
+                        MoveWhileBorrowed(loan_path, loan_span) => {
                             self.bccx.span_err(
                                 cap_var.span,
-                                fmt!("by-move capture of %s prohibited \
-                                      due to outstanding loan",
-                                     self.bccx.cmt_to_str(move_cmt)));
+                                fmt!("cannot move `%s` into closure \
+                                      because it is borrowed",
+                                     self.bccx.loan_path_to_str(loan_path)));
                             self.bccx.span_note(
-                                loan_cmt.span,
-                                fmt!("loan of %s granted here",
-                                     self.bccx.cmt_to_str(loan_cmt)));
+                                loan_span,
+                                fmt!("borrow of `%s` occurs here",
+                                     self.bccx.loan_path_to_str(loan_path)));
                         }
                     }
                 }
@@ -724,17 +677,19 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
     }
 }
 
-fn check_loans_in_local(local: @ast::local,
-                        self: @mut CheckLoanCtxt,
-                        vt: visit::vt<@mut CheckLoanCtxt>) {
+fn check_loans_in_local<'a>(local: @ast::local,
+                            self: @mut CheckLoanCtxt<'a>,
+                            vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
     visit::visit_local(local, self, vt);
 }
 
-fn check_loans_in_expr(expr: @ast::expr,
-                       self: @mut CheckLoanCtxt,
-                       vt: visit::vt<@mut CheckLoanCtxt>) {
-    debug!("check_loans_in_expr(expr=%?/%s)",
-           expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr()));
+fn check_loans_in_expr<'a>(expr: @ast::expr,
+                           self: @mut CheckLoanCtxt<'a>,
+                           vt: visit::vt<@mut CheckLoanCtxt<'a>>) {
+    debug!("check_loans_in_expr(expr=%s)",
+           expr.repr(self.tcx()));
+
+    visit::visit_expr(expr, self, vt);
 
     self.check_for_conflicting_loans(expr.id);
 
@@ -744,12 +699,12 @@ fn check_loans_in_expr(expr: @ast::expr,
 
     match expr.node {
       ast::expr_swap(l, r) => {
-        self.check_assignment(at_swap, l);
-        self.check_assignment(at_swap, r);
+        self.check_assignment(l);
+        self.check_assignment(r);
       }
       ast::expr_assign(dest, _) |
       ast::expr_assign_op(_, dest, _) => {
-        self.check_assignment(at_straight_up, dest);
+        self.check_assignment(dest);
       }
       ast::expr_call(f, ref args, _) => {
         self.check_call(expr, Some(f), f.id, f.span, *args);
@@ -774,31 +729,34 @@ fn check_loans_in_expr(expr: @ast::expr,
                         expr.span,
                         ~[]);
       }
-      ast::expr_match(*) => {
-          // Note: moves out of pattern bindings are not checked by
-          // the borrow checker, at least not directly.  What happens
-          // is that if there are any moved bindings, the discriminant
-          // will be considered a move, and this will be checked as
-          // normal.  Then, in `middle::check_match`, we will check
-          // that no move occurs in a binding that is underneath an
-          // `@` or `&`.  Together these give the same guarantees as
-          // `check_move_out_from_expr()` without requiring us to
-          // rewalk the patterns and rebuild the pattern
-          // categorizations.
-      }
       _ => { }
     }
-
-    visit::visit_expr(expr, self, vt);
 }
 
-fn check_loans_in_block(blk: &ast::blk,
-                        self: @mut CheckLoanCtxt,
-                        vt: visit::vt<@mut CheckLoanCtxt>) {
-    do save_and_restore_managed(self.declared_purity) {
-        self.check_for_conflicting_loans(blk.node.id);
+fn check_loans_in_pat<'a>(pat: @ast::pat,
+                          self: @mut CheckLoanCtxt<'a>,
+                          vt: visit::vt<@mut CheckLoanCtxt<'a>>)
+{
+    self.check_for_conflicting_loans(pat.id);
+
+    // Note: moves out of pattern bindings are not checked by
+    // the borrow checker, at least not directly.  What happens
+    // is that if there are any moved bindings, the discriminant
+    // will be considered a move, and this will be checked as
+    // normal.  Then, in `middle::check_match`, we will check
+    // that no move occurs in a binding that is underneath an
+    // `@` or `&`.  Together these give the same guarantees as
+    // `check_move_out_from_expr()` without requiring us to
+    // rewalk the patterns and rebuild the pattern
+    // categorizations.
+
+    visit::visit_pat(pat, self, vt);
+}
 
-        *self.declared_purity = self.declared_purity.recurse(blk);
-        visit::visit_block(blk, self, vt);
-    }
+fn check_loans_in_block<'a>(blk: &ast::blk,
+                            self: @mut CheckLoanCtxt<'a>,
+                            vt: visit::vt<@mut CheckLoanCtxt<'a>>)
+{
+    visit::visit_block(blk, self, vt);
+    self.check_for_conflicting_loans(blk.node.id);
 }
diff --git a/src/librustc/middle/borrowck/doc.rs b/src/librustc/middle/borrowck/doc.rs
new file mode 100644
index 0000000000000..1e09fbe71843c
--- /dev/null
+++ b/src/librustc/middle/borrowck/doc.rs
@@ -0,0 +1,750 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+# The Borrow Checker
+
+This pass has the job of enforcing memory safety. This is a subtle
+topic. The only way I know how to explain it is terms of a formal
+model, so that's what I'll do.
+
+# Formal model
+
+Let's consider a simple subset of Rust in which you can only borrow
+from lvalues like so:
+
+    LV = x | LV.f | *LV
+
+Here `x` represents some variable, `LV.f` is a field reference,
+and `*LV` is a pointer dereference. There is no auto-deref or other
+niceties. This means that if you have a type like:
+
+    struct S { f: uint }
+
+and a variable `a: ~S`, then the rust expression `a.f` would correspond
+to an `LV` of `(*a).f`.
+
+Here is the formal grammar for the types we'll consider:
+
+    TY = () | S<'LT...> | ~TY | & 'LT MQ TY | @ MQ TY
+    MQ = mut | imm | const
+
+Most of these types should be pretty self explanatory. Here `S` is a
+struct name and we assume structs are declared like so:
+
+    SD = struct S<'LT...> { (f: TY)... }
+
+# An intuitive explanation
+
+## Issuing loans
+
+Now, imagine we had a program like this:
+
+    struct Foo { f: uint, g: uint }
+    ...
+    'a: {
+      let mut x: ~Foo = ...;
+      let y = &mut (*x).f;
+      x = ...;
+    }
+
+This is of course dangerous because mutating `x` will free the old
+value and hence invalidate `y`. The borrow checker aims to prevent
+this sort of thing.
+
+### Loans
+
+The way the borrow checker works is that it analyzes each borrow
+expression (in our simple model, that's stuff like `&LV`, though in
+real life there are a few other cases to consider). For each borrow
+expression, it computes a vector of loans:
+
+    LOAN = (LV, LT, PT, LK)
+    PT = Partial | Total
+    LK = MQ | RESERVE
+
+Each `LOAN` tuple indicates some sort of restriction on what can be
+done to the lvalue `LV`; `LV` will always be a path owned by the
+current stack frame. These restrictions are called "loans" because
+they are always the result of a borrow expression.
+
+Every loan has a lifetime `LT` during which those restrictions are in
+effect.  The indicator `PT` distinguishes between *total* loans, in
+which the LV itself was borrowed, and *partial* loans, which means
+that some content ownwed by LV was borrowed.
+
+The final element in the loan tuple is the *loan kind* `LK`.  There
+are four kinds: mutable, immutable, const, and reserve:
+
+- A "mutable" loan means that LV may be written to through an alias, and
+  thus LV cannot be written to directly or immutably aliased (remember
+  that we preserve the invariant that any given value can only be
+  written to through one path at a time; hence if there is a mutable
+  alias to LV, then LV cannot be written directly until this alias is
+  out of scope).
+
+- An "immutable" loan means that LV must remain immutable.  Hence it
+  cannot be written, but other immutable aliases are permitted.
+
+- A "const" loan means that an alias to LV exists.  LV may still be
+  written or frozen.
+
+- A "reserve" loan is the strongest case.  It prevents both mutation
+  and aliasing of any kind, including `&const` loans.  Reserve loans
+  are a side-effect of borrowing an `&mut` loan.
+
+In addition to affecting mutability, a loan of any kind implies that
+LV cannot be moved.
+
+### Example
+
+To give you a better feeling for what a loan is, let's look at three
+loans that would be issued as a result of the borrow `&(*x).f` in the
+example above:
+
+    ((*x).f, Total, mut, 'a)
+    (*x, Partial, mut, 'a)
+    (x, Partial, mut, 'a)
+
+The first loan states that the expression `(*x).f` has been loaned
+totally as mutable for the lifetime `'a`. This first loan would
+prevent an assignment `(*x).f = ...` from occurring during the
+lifetime `'a`.
+
+Now let's look at the second loan. You may have expected that each
+borrow would result in only one loan. But this is not the case.
+Instead, there will be loans for every path where mutation might
+affect the validity of the borrowed pointer that is created (in some
+cases, there can even be multiple loans per path, see the section on
+"Borrowing in Calls" below for the gory details). The reason for this
+is to prevent actions that would indirectly affect the borrowed path.
+In this case, we wish to ensure that `(*x).f` is not mutated except
+through the mutable alias `y`.  Therefore, we must not only prevent an
+assignment to `(*x).f` but also an assignment like `*x = Foo {...}`,
+as this would also mutate the field `f`.  To do so, we issue a
+*partial* mutable loan for `*x` (the loan is partial because `*x`
+itself was not borrowed).  This partial loan will cause any attempt to
+assign to `*x` to be flagged as an error.
+
+Because both partial and total loans prevent assignments, you may
+wonder why we bother to distinguish between them.  The reason for this
+distinction has to do with preventing double borrows. In particular,
+it is legal to borrow both `&mut x.f` and `&mut x.g` simultaneously,
+but it is not legal to borrow `&mut x.f` twice. In the borrow checker,
+the first case would result in two *partial* mutable loans of `x`
+(along with one total mutable loan of `x.f` and one of `x.g) whereas
+the second would result in two *total* mutable loans of `x.f` (along
+with two partial mutable loans of `x`).  Multiple *total mutable* loan
+for the same path are not permitted, but multiple *partial* loans (of
+any mutability) are permitted.
+
+Finally, we come to the third loan. This loan is a partial mutable
+loan of `x`.  This loan prevents us from reassigning `x`, which would
+be bad for two reasons.  First, it would change the value of `(*x).f`
+but, even worse, it would cause the pointer `y` to become a dangling
+pointer.  Bad all around.
+
+## Checking for illegal assignments, moves, and reborrows
+
+Once we have computed the loans introduced by each borrow, the borrow
+checker will determine the full set of loans in scope at each
+expression and use that to decide whether that expression is legal.
+Remember that the scope of loan is defined by its lifetime LT.  We
+sometimes say that a loan which is in-scope at a particular point is
+an "outstanding loan".
+
+The kinds of expressions which in-scope loans can render illegal are
+*assignments*, *moves*, and *borrows*.
+
+An assignments to an lvalue LV is illegal if there is in-scope mutable
+or immutable loan for LV.  Assignment with an outstanding mutable loan
+is illegal because then the `&mut` pointer is supposed to be the only
+way to mutate the value.  Assignment with an outstanding immutable
+loan is illegal because the value is supposed to be immutable at that
+point.
+
+A move from an lvalue LV is illegal if there is any sort of
+outstanding loan.
+
+A borrow expression may be illegal if any of the loans which it
+produces conflict with other outstanding loans.  Two loans are
+considered compatible if one of the following conditions holds:
+
+- At least one loan is a const loan.
+- Both loans are partial loans.
+- Both loans are immutable.
+
+Any other combination of loans is illegal.
+
+# The set of loans that results from a borrow expression
+
+Here we'll define four functions---MUTATE, FREEZE, ALIAS, and
+TAKE---which are all used to compute the set of LOANs that result
+from a borrow expression.  The first three functions each have
+a similar type signature:
+
+    MUTATE(LV, LT, PT) -> LOANS
+    FREEZE(LV, LT, PT) -> LOANS
+    ALIAS(LV, LT, PT) -> LOANS
+
+MUTATE, FREEZE, and ALIAS are used when computing the loans result
+from mutable, immutable, and const loans respectively.  For example,
+the loans resulting from an expression like `&mut (*x).f` would be
+computed by `MUTATE((*x).f, LT, Total)`, where `LT` is the lifetime of
+the resulting pointer.  Similarly the loans for `&(*x).f` and `&const
+(*x).f` would be computed by `FREEZE((*x).f, LT, Total)` and
+`ALIAS((*x).f, LT, Total)` respectively. (Actually this is a slight
+simplification; see the section below on Borrows in Calls for the full
+gory details)
+
+The names MUTATE, FREEZE, and ALIAS are intended to suggest the
+semantics of `&mut`, `&`, and `&const` borrows respectively.  `&mut`,
+for example, creates a mutable alias of LV.  `&` causes the borrowed
+value to be frozen (immutable).  `&const` does neither but does
+introduce an alias to be the borrowed value.
+
+Each of these three functions is only defined for some inputs.  That
+is, it may occur that some particular borrow is not legal.  For
+example, it is illegal to make an `&mut` loan of immutable data.  In
+that case, the MUTATE() function is simply not defined (in the code,
+it returns a Result<> condition to indicate when a loan would be
+illegal).
+
+The final function, RESERVE, is used as part of borrowing an `&mut`
+pointer.  Due to the fact that it is used for one very particular
+purpose, it has a rather simpler signature than the others:
+
+    RESERVE(LV, LT) -> LOANS
+
+It is explained when we come to that case.
+
+## The function MUTATE()
+
+Here we use [inference rules][ir] to define the MUTATE() function.
+We will go case by case for the various kinds of lvalues that
+can be borrowed.
+
+[ir]: http://en.wikipedia.org/wiki/Rule_of_inference
+
+### Mutating local variables
+
+The rule for mutating local variables is as follows:
+
+    Mutate-Variable:
+      LT <= Scope(x)
+      Mut(x) = Mut
+      --------------------------------------------------
+      MUTATE(x, LT, PT) = (x, LT, PT, mut)
+
+Here `Scope(x)` is the lifetime of the block in which `x` was declared
+and `Mut(x)` indicates the mutability with which `x` was declared.
+This rule simply states that you can only create a mutable alias
+to a variable if it is mutable, and that alias cannot outlive the
+stack frame in which the variable is declared.
+
+### Mutating fields and owned pointers
+
+As it turns out, the rules for mutating fields and mutating owned
+pointers turn out to be quite similar.  The reason is that the
+expressions `LV.f` and `*LV` are both owned by their base expression
+`LV`.  So basically the result of mutating `LV.f` or `*LV` is computed
+by adding a loan for `LV.f` or `*LV` and then the loans for a partial
+take of `LV`:
+
+    Mutate-Field:
+      MUTATE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      MUTATE(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, mut)
+
+    Mutate-Owned-Ptr:
+      Type(LV) = ~Ty
+      MUTATE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      MUTATE(*LV, LT, PT) = LOANS, (*LV, LT, PT, mut)
+
+Note that while our micro-language only has fields, the slight
+variations on the `Mutate-Field` rule are used for any interior content
+that appears in the full Rust language, such as the contents of a
+tuple, fields in a struct, or elements of a fixed-length vector.
+
+### Mutating dereferenced borrowed pointers
+
+The rule for borrowed pointers is by far the most complicated:
+
+    Mutate-Mut-Borrowed-Ptr:
+      Type(LV) = &LT_P mut Ty             // (1)
+      LT <= LT_P                          // (2)
+      RESERVE(LV, LT) = LOANS             // (3)
+      ------------------------------------------------------------
+      MUTATE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Mut)
+
+Condition (1) states that only a mutable borrowed pointer can be
+taken.  Condition (2) states that the lifetime of the alias must be
+less than the lifetime of the borrowed pointer being taken.
+
+Conditions (3) and (4) are where things get interesting.  The intended
+semantics of the borrow is that the new `&mut` pointer is the only one
+which has the right to modify the data; the original `&mut` pointer
+must not be used for mutation.  Because borrowed pointers do not own
+their content nor inherit mutability, we must be particularly cautious
+of aliases, which could permit the original borrowed pointer to be
+reached from another path and thus circumvent our loans.
+
+Here is one example of what could go wrong if we ignore clause (4):
+
+    let x: &mut T;
+    ...
+    let y = &mut *x;   // Only *y should be able to mutate...
+    let z = &const x;
+    **z = ...;         // ...but here **z is still able to mutate!
+
+Another possible error could occur with moves:
+
+    let x: &mut T;
+    ...
+    let y = &mut *x;   // Issues loan: (*x, LT, Total, Mut)
+    let z = x;         // moves from x
+    *z = ...;          // Mutates *y indirectly! Bad.
+
+In both of these cases, the problem is that when creating the alias
+`y` we would only issue a loan preventing assignment through `*x`.
+But this loan can be easily circumvented by moving from `x` or
+aliasing it.  Note that, in the first example, the alias of `x` was
+created using `&const`, which is a particularly weak form of alias.
+
+The danger of aliases can also occur when the `&mut` pointer itself
+is already located in an alias location, as here:
+
+    let x: @mut &mut T; // or &mut &mut T, &&mut T,
+    ...                 // &const &mut T, @&mut T, etc
+    let y = &mut **x;   // Only *y should be able to mutate...
+    let z = x;
+    **z = ...;          // ...but here **z is still able to mutate!
+
+When we cover the rules for RESERVE, we will see that it would
+disallow this case, because MUTATE can only be applied to canonical
+lvalues which are owned by the current stack frame.
+
+It might be the case that if `&const` and `@const` pointers were
+removed, we could do away with RESERVE and simply use MUTATE instead.
+But we have to be careful about the final example in particular, since
+dynamic freezing would not be sufficient to prevent this example.
+Perhaps a combination of MUTATE with a predicate OWNED(LV).
+
+One final detail: unlike every other case, when we calculate the loans
+using RESERVE we do not use the original lifetime `LT` but rather
+`GLB(Scope(LV), LT)`.  What this says is:
+
+### Mutating dereferenced managed pointers
+
+Because the correctness of managed pointer loans is checked dynamically,
+the rule is quite simple:
+
+    Mutate-Mut-Managed-Ptr:
+      Type(LV) = @mut Ty
+      Add ROOT-FREEZE annotation for *LV with lifetime LT
+      ------------------------------------------------------------
+      MUTATE(*LV, LT, Total) = []
+
+No loans are issued.  Instead, we add a side annotation that causes
+`*LV` to be rooted and frozen on entry to LV.  You could rephrase
+these rules as having multiple returns values, or rephrase this as a
+kind of loan, but whatever.
+
+One interesting point is that *partial takes* of `@mut` are forbidden.
+This is not for any soundness reason but just because it is clearer
+for users when `@mut` values are either lent completely or not at all.
+
+## The function FREEZE
+
+The rules for FREEZE are pretty similar to MUTATE.  The first four
+cases I'll just present without discussion, as the reasoning is
+quite analogous to the MUTATE case:
+
+    Freeze-Variable:
+      LT <= Scope(x)
+      --------------------------------------------------
+      FREEZE(x, LT, PT) = (x, LT, PT, imm)
+
+    Freeze-Field:
+      FREEZE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      FREEZE(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, imm)
+
+    Freeze-Owned-Ptr:
+      Type(LV) = ~Ty
+      FREEZE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, imm)
+
+    Freeze-Mut-Borrowed-Ptr:
+      Type(LV) = &LT_P mut Ty
+      LT <= LT_P
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Imm)
+
+    Freeze-Mut-Managed-Ptr:
+      Type(LV) = @mut Ty
+      Add ROOT-FREEZE annotation for *LV with lifetime LT
+      ------------------------------------------------------------
+      Freeze(*LV, LT, Total) = []
+
+The rule to "freeze" an immutable borrowed pointer is quite
+simple, since the content is already immutable:
+
+    Freeze-Imm-Borrowed-Ptr:
+      Type(LV) = &LT_P Ty                 // (1)
+      LT <= LT_P                          // (2)
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = LOANS, (*LV, LT, PT, Mut)
+
+The final two rules pertain to borrows of `@Ty`.  There is a bit of
+subtlety here.  The main problem is that we must guarantee that the
+managed box remains live for the entire borrow.  We can either do this
+dynamically, by rooting it, or (better) statically, and hence there
+are two rules:
+
+    Freeze-Imm-Managed-Ptr-1:
+      Type(LV) = @Ty
+      Add ROOT annotation for *LV
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = []
+
+    Freeze-Imm-Managed-Ptr-2:
+      Type(LV) = @Ty
+      LT <= Scope(LV)
+      Mut(LV) = imm
+      LV is not moved
+      ------------------------------------------------------------
+      FREEZE(*LV, LT, PT) = []
+
+The intention of the second rule is to avoid an extra root if LV
+serves as a root.  In that case, LV must (1) outlive the borrow; (2)
+be immutable; and (3) not be moved.
+
+## The ALIAS function
+
+The function ALIAS is used for `&const` loans but also to handle one
+corner case concerning function arguments (covered in the section
+"Borrows in Calls" below).  It computes the loans that result from
+observing that there is a pointer to `LV` and thus that pointer must
+remain valid.
+
+The first two rules are simple:
+
+    Alias-Variable:
+      LT <= Scope(x)
+      --------------------------------------------------
+      ALIAS(x, LT, PT) = (x, LT, PT, Const)
+
+    Alias-Field:
+      ALIAS(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      ALIAS(LV.f, LT, PT) = LOANS, (LV.F, LT, PT, Const)
+
+### Aliasing owned pointers
+
+The rule for owned pointers is somewhat interesting:
+
+    Alias-Owned-Ptr:
+      Type(LV) = ~Ty
+      FREEZE(LV, LT, Partial) = LOANS
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = LOANS, (*LV, LT, PT, Const)
+
+Here we *freeze* the base `LV`.  The reason is that if an owned
+pointer is mutated it frees its content, which means that the alias to
+`*LV` would become a dangling pointer.
+
+### Aliasing borrowed pointers
+
+The rule for borrowed pointers is quite simple, because borrowed
+pointers do not own their content and thus do not play a role in
+keeping it live:
+
+    Alias-Borrowed-Ptr:
+      Type(LV) = &LT_P MQ Ty
+      LT <= LT_P
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = []
+
+Basically, the existence of a borrowed pointer to some memory with
+lifetime LT_P is proof that the memory can safely be aliased for any
+lifetime LT <= LT_P.
+
+### Aliasing managed pointers
+
+The rules for aliasing managed pointers are similar to those
+used with FREEZE, except that they apply to all manager pointers
+regardles of mutability:
+
+    Alias-Managed-Ptr-1:
+      Type(LV) = @MQ Ty
+      Add ROOT annotation for *LV
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = []
+
+    Alias-Managed-Ptr-2:
+      Type(LV) = @MQ Ty
+      LT <= Scope(LV)
+      Mut(LV) = imm
+      LV is not moved
+      ------------------------------------------------------------
+      ALIAS(*LV, LT, PT) = []
+
+## The RESERVE function
+
+The final function, RESERVE, is used for loans of `&mut` pointers.  As
+discussed in the section on the function MUTATE, we must be quite
+careful when "re-borrowing" an `&mut` pointer to ensure that the original
+`&mut` pointer can no longer be used to mutate.
+
+There are a couple of dangers to be aware of:
+
+- `&mut` pointers do not inherit mutability.  Therefore, if you have
+  an lvalue LV with type `&mut T` and you freeze `LV`, you do *not*
+  freeze `*LV`.  This is quite different from an `LV` with type `~T`.
+
+- Also, because they do not inherit mutability, if the `&mut` pointer
+  lives in an aliased location, then *any alias* can be used to write!
+
+As a consequence of these two rules, RESERVE can only be successfully
+invoked on an lvalue LV that is *owned by the current stack frame*.
+This ensures that there are no aliases that are not visible from the
+outside.  Moreover, Reserve loans are incompatible with all other
+loans, even Const loans.  This prevents any aliases from being created
+within the current function.
+
+### Reserving local variables
+
+The rule for reserving a variable is generally straightforward but
+with one interesting twist:
+
+    Reserve-Variable:
+      --------------------------------------------------
+      RESERVE(x, LT) = (x, LT, Total, Reserve)
+
+The twist here is that the incoming lifetime is not required to
+be a subset of the incoming variable, unlike every other case.  To
+see the reason for this, imagine the following function:
+
+    struct Foo { count: uint }
+    fn count_field(x: &'a mut Foo) -> &'a mut count {
+        &mut (*x).count
+    }
+
+This function consumes one `&mut` pointer and returns another with the
+same lifetime pointing at a particular field.  The borrow for the
+`&mut` expression will result in a call to `RESERVE(x, 'a)`, which is
+intended to guarantee that `*x` is not later aliased or used to
+mutate.  But the lifetime of `x` is limited to the current function,
+which is a sublifetime of the parameter `'a`, so the rules used for
+MUTATE, FREEZE, and ALIAS (which require that the lifetime of the loan
+not exceed the lifetime of the variable) would result in an error.
+
+Nonetheless this function is perfectly legitimate.  After all, the
+caller has moved in an `&mut` pointer with lifetime `'a`, and thus has
+given up their right to mutate the value for the remainder of `'a`.
+So it is fine for us to return a pointer with the same lifetime.
+
+The reason that RESERVE differs from the other functions is that
+RESERVE is not responsible for guaranteeing that the pointed-to data
+will outlive the borrowed pointer being created.  After all, `&mut`
+values do not own the data they point at.
+
+### Reserving owned content
+
+The rules for fields and owned pointers are very straightforward:
+
+    Reserve-Field:
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      RESERVE(LV.f, LT) = LOANS, (LV.F, LT, Total, Reserve)
+
+    Reserve-Owned-Ptr:
+      Type(LV) = ~Ty
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      RESERVE(*LV, LT) = LOANS, (*LV, LT, Total, Reserve)
+
+### Reserving `&mut` borrowed pointers
+
+Unlike other borrowed pointers, `&mut` pointers are unaliasable,
+so we can reserve them like everything else:
+
+    Reserve-Mut-Borrowed-Ptr:
+      Type(LV) = &LT_P mut Ty
+      RESERVE(LV, LT) = LOANS
+      ------------------------------------------------------------
+      RESERVE(*LV, LT) = LOANS, (*LV, LT, Total, Reserve)
+
+## Borrows in calls
+
+Earlier we said that the MUTATE, FREEZE, and ALIAS functions were used
+to compute the loans resulting from a borrow expression.  But this is
+not strictly correct, there is a slight complication that occurs with
+calls by which additional loans may be necessary.  We will explain
+that here and give the full details.
+
+Imagine a call expression `'a: E1(E2, E3)`, where `Ei` are some
+expressions. If we break this down to something a bit lower-level, it
+is kind of short for:
+
+    'a: {
+        'a_arg1: let temp1: ... = E1;
+        'a_arg2: let temp2: ... = E2;
+        'a_arg3: let temp3: ... = E3;
+        'a_call: temp1(temp2, temp3)
+    }
+
+Here the lifetime labels indicate the various lifetimes. As you can
+see there are in fact four relevant lifetimes (only one of which was
+named by the user): `'a` corresponds to the expression `E1(E2, E3)` as
+a whole. `'a_arg1`, `'a_arg2`, and `'a_arg3` correspond to the
+evaluations of `E1`, `E2`, and `E3` respectively. Finally, `'a_call`
+corresponds to the *actual call*, which is the point where the values
+of the parameters will be used.
+
+Now, let's look at a (contrived, but representative) example to see
+why all this matters:
+
+    struct Foo { f: uint, g: uint }
+    ...
+    fn add(p: &mut uint, v: uint) {
+        *p += v;
+    }
+    ...
+    fn inc(p: &mut uint) -> uint {
+        *p += 1; *p
+    }
+    fn weird() {
+        let mut x: ~Foo = ~Foo { ... };
+        'a: add(&mut (*x).f,
+                'b: inc(&mut (*x).f)) // (*)
+    }
+
+The important part is the line marked `(*)` which contains a call to
+`add()`. The first argument is a mutable borrow of the field `f`.
+The second argument *always borrows* the field `f`. Now, if these two
+borrows overlapped in time, this would be illegal, because there would
+be two `&mut` pointers pointing at `f`. And, in a way, they *do*
+overlap in time, since the first argument will be evaluated first,
+meaning that the pointer will exist when the second argument executes.
+But in another important way they do not overlap in time. Let's
+expand out that final call to `add()` as we did before:
+
+    'a: {
+        'a_arg1: let a_temp1: ... = add;
+        'a_arg2: let a_temp2: &'a_call mut uint = &'a_call mut (*x).f;
+        'a_arg3_: let a_temp3: uint = {
+            let b_temp1: ... = inc;
+            let b_temp2: &'b_call = &'b_call mut (*x).f;
+            'b_call: b_temp1(b_temp2)
+        };
+        'a_call: a_temp1(a_temp2, a_temp3)
+    }
+
+When it's written this way, we can see that although there are two
+borrows, the first has lifetime `'a_call` and the second has lifetime
+`'b_call` and in fact these lifetimes do not overlap. So everything
+is fine.
+
+But this does not mean that there isn't reason for caution!  Imagine a
+devious program like *this* one:
+
+    struct Foo { f: uint, g: uint }
+    ...
+    fn add(p: &mut uint, v: uint) {
+        *p += v;
+    }
+    ...
+    fn consume(x: ~Foo) -> uint {
+        x.f + x.g
+    }
+    fn weird() {
+        let mut x: ~Foo = ~Foo { ... };
+        'a: add(&mut (*x).f, consume(x)) // (*)
+    }
+
+In this case, there is only one borrow, but the second argument is
+`consume(x)` instead of a second borrow. Because `consume()` is
+declared to take a `~Foo`, it will in fact free the pointer `x` when
+it has finished executing. If it is not obvious why this is
+troublesome, consider this expanded version of that call:
+
+    'a: {
+        'a_arg1: let a_temp1: ... = add;
+        'a_arg2: let a_temp2: &'a_call mut uint = &'a_call mut (*x).f;
+        'a_arg3_: let a_temp3: uint = {
+            let b_temp1: ... = consume;
+            let b_temp2: ~Foo = x;
+            'b_call: b_temp1(x)
+        };
+        'a_call: a_temp1(a_temp2, a_temp3)
+    }
+
+In this example, we will have borrowed the first argument before `x`
+is freed and then free `x` during evaluation of the second
+argument. This causes `a_temp2` to be invalidated.
+
+Of course the loans computed from the borrow expression are supposed
+to prevent this situation.  But if we just considered the loans from
+`MUTATE((*x).f, 'a_call, Total)`, the resulting loans would be:
+
+    ((*x).f, 'a_call, Total,   Mut)
+    (*x,     'a_call, Partial, Mut)
+    (x,      'a_call, Partial, Mut)
+
+Because these loans are only in scope for `'a_call`, they do nothing
+to prevent the move that occurs evaluating the second argument.
+
+The way that we solve this is to say that if you have a borrow
+expression `&'LT_P mut LV` which itself occurs in the lifetime
+`'LT_B`, then the resulting loans are:
+
+    MUTATE(LV, LT_P, Total) + ALIAS(LV, LUB(LT_P, LT_B), Total)
+
+The call to MUTATE is what we've seen so far.  The second part
+expresses the idea that the expression LV will be evaluated starting
+at LT_B until the end of LT_P.  Now, in the normal case, LT_P >= LT_B,
+and so the second set of loans that result from a ALIAS are basically
+a no-op.  However, in the case of an argument where the evaluation of
+the borrow occurs before the interval where the resulting pointer will
+be used, this ALIAS is important.
+
+In the case of our example, it would produce a set of loans like:
+
+    ((*x).f, 'a, Total, Const)
+    (*x, 'a, Total, Const)
+    (x, 'a, Total, Imm)
+
+The scope of these loans is `'a = LUB('a_arg2, 'a_call)`, and so they
+encompass all subsequent arguments.  The first set of loans are Const
+loans, which basically just prevent moves.  However, when we cross
+over the dereference of the owned pointer `x`, the rule for ALIAS
+specifies that `x` must be frozen, and hence the final loan is an Imm
+loan.  In any case the troublesome second argument would be flagged
+as an error.
+
+# Maps that are created
+
+Borrowck results in two maps.
+
+- `root_map`: identifies those expressions or patterns whose result
+  needs to be rooted. Conceptually the root_map maps from an
+  expression or pattern node to a `node_id` identifying the scope for
+  which the expression must be rooted (this `node_id` should identify
+  a block or call). The actual key to the map is not an expression id,
+  however, but a `root_map_key`, which combines an expression id with a
+  deref count and is used to cope with auto-deref.
+
+*/
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
deleted file mode 100644
index fd1f6f5c450af..0000000000000
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ /dev/null
@@ -1,641 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ----------------------------------------------------------------------
-// Gathering loans
-//
-// The borrow check proceeds in two phases. In phase one, we gather the full
-// set of loans that are required at any point.  These are sorted according to
-// their associated scopes.  In phase two, checking loans, we will then make
-// sure that all of these loans are honored.
-
-use middle::borrowck::preserve::{PreserveCondition, PcOk, PcIfPure};
-use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
-use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze,
-                       TotalTake, PartialTake, Immobile};
-use middle::borrowck::ReqMaps;
-use middle::borrowck::loan;
-use middle::mem_categorization::{cmt, mem_categorization_ctxt};
-use middle::pat_util;
-use middle::ty::{ty_region};
-use middle::ty;
-use util::common::indenter;
-use util::ppaux::{Repr, region_to_str};
-
-use core::hashmap::{HashSet, HashMap};
-use syntax::ast::{m_const, m_imm, m_mutbl};
-use syntax::ast;
-use syntax::codemap::span;
-use syntax::print::pprust;
-use syntax::visit;
-
-/// Context used while gathering loans:
-///
-/// - `bccx`: the the borrow check context
-/// - `req_maps`: the maps computed by `gather_loans()`, see def'n of the
-///   struct `ReqMaps` for more info
-/// - `item_ub`: the id of the block for the enclosing fn/method item
-/// - `root_ub`: the id of the outermost block for which we can root
-///   an `@T`.  This is the id of the innermost enclosing
-///   loop or function body.
-///
-/// The role of `root_ub` is to prevent us from having to accumulate
-/// vectors of rooted items at runtime.  Consider this case:
-///
-///     fn foo(...) -> int {
-///         let mut ptr: &int;
-///         while some_cond {
-///             let x: @int = ...;
-///             ptr = &*x;
-///         }
-///         *ptr
-///     }
-///
-/// If we are not careful here, we would infer the scope of the borrow `&*x`
-/// to be the body of the function `foo()` as a whole.  We would then
-/// have root each `@int` that is produced, which is an unbounded number.
-/// No good.  Instead what will happen is that `root_ub` will be set to the
-/// body of the while loop and we will refuse to root the pointer `&*x`
-/// because it would have to be rooted for a region greater than `root_ub`.
-struct GatherLoanCtxt {
-    bccx: @BorrowckCtxt,
-    req_maps: ReqMaps,
-    item_ub: ast::node_id,
-    root_ub: ast::node_id,
-    ignore_adjustments: HashSet<ast::node_id>
-}
-
-pub fn gather_loans(bccx: @BorrowckCtxt, crate: @ast::crate) -> ReqMaps {
-    let glcx = @mut GatherLoanCtxt {
-        bccx: bccx,
-        req_maps: ReqMaps { req_loan_map: HashMap::new(),
-                            pure_map: HashMap::new() },
-        item_ub: 0,
-        root_ub: 0,
-        ignore_adjustments: HashSet::new()
-    };
-    let v = visit::mk_vt(@visit::Visitor {visit_expr: req_loans_in_expr,
-                                          visit_fn: req_loans_in_fn,
-                                          visit_stmt: add_stmt_to_map,
-                                          .. *visit::default_visitor()});
-    visit::visit_crate(crate, glcx, v);
-    let @GatherLoanCtxt{req_maps, _} = glcx;
-    return req_maps;
-}
-
-fn req_loans_in_fn(fk: &visit::fn_kind,
-                   decl: &ast::fn_decl,
-                   body: &ast::blk,
-                   sp: span,
-                   id: ast::node_id,
-                   self: @mut GatherLoanCtxt,
-                   v: visit::vt<@mut GatherLoanCtxt>) {
-    // see explanation attached to the `root_ub` field:
-    let old_item_id = self.item_ub;
-    let old_root_ub = self.root_ub;
-    self.root_ub = body.node.id;
-
-    match *fk {
-        visit::fk_anon(*) | visit::fk_fn_block(*) => {}
-        visit::fk_item_fn(*) | visit::fk_method(*) => {
-            self.item_ub = body.node.id;
-        }
-    }
-
-    visit::visit_fn(fk, decl, body, sp, id, self, v);
-    self.root_ub = old_root_ub;
-    self.item_ub = old_item_id;
-}
-
-fn req_loans_in_expr(ex: @ast::expr,
-                     self: @mut GatherLoanCtxt,
-                     vt: visit::vt<@mut GatherLoanCtxt>) {
-    let bccx = self.bccx;
-    let tcx = bccx.tcx;
-    let old_root_ub = self.root_ub;
-
-    debug!("req_loans_in_expr(expr=%?/%s)",
-           ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
-
-    // If this expression is borrowed, have to ensure it remains valid:
-    {
-        let mut this = &mut *self;
-        if !this.ignore_adjustments.contains(&ex.id) {
-            for tcx.adjustments.find(&ex.id).each |&adjustments| {
-                this.guarantee_adjustments(ex, *adjustments);
-            }
-        }
-    }
-
-    // Special checks for various kinds of expressions:
-    match ex.node {
-      ast::expr_addr_of(mutbl, base) => {
-        let base_cmt = self.bccx.cat_expr(base);
-
-        // make sure that the thing we are pointing out stays valid
-        // for the lifetime `scope_r` of the resulting ptr:
-        let scope_r = ty_region(tcx, ex.span, tcx.ty(ex));
-        self.guarantee_valid(base_cmt, mutbl, scope_r);
-        visit::visit_expr(ex, self, vt);
-      }
-
-      ast::expr_match(ex_v, ref arms) => {
-        let cmt = self.bccx.cat_expr(ex_v);
-        for (*arms).each |arm| {
-            for arm.pats.each |pat| {
-                self.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
-            }
-        }
-        visit::visit_expr(ex, self, vt);
-      }
-
-      ast::expr_index(rcvr, _) |
-      ast::expr_binary(_, rcvr, _) |
-      ast::expr_unary(_, rcvr) |
-      ast::expr_assign_op(_, rcvr, _)
-      if self.bccx.method_map.contains_key(&ex.id) => {
-        // Receivers in method calls are always passed by ref.
-        //
-        // Here, in an overloaded operator, the call is this expression,
-        // and hence the scope of the borrow is this call.
-        //
-        // FIX? / NOT REALLY---technically we should check the other
-        // argument and consider the argument mode.  But how annoying.
-        // And this problem when goes away when argument modes are
-        // phased out.  So I elect to leave this undone.
-        let scope_r = ty::re_scope(ex.id);
-        let rcvr_cmt = self.bccx.cat_expr(rcvr);
-        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
-
-        // FIXME (#3387): Total hack: Ignore adjustments for the left-hand
-        // side. Their regions will be inferred to be too large.
-        self.ignore_adjustments.insert(rcvr.id);
-
-        visit::visit_expr(ex, self, vt);
-      }
-
-      // FIXME--#3387
-      // ast::expr_binary(_, lhs, rhs) => {
-      //     // Universal comparison operators like ==, >=, etc
-      //     // take their arguments by reference.
-      //     let lhs_ty = ty::expr_ty(self.tcx(), lhs);
-      //     if !ty::type_is_scalar(lhs_ty) {
-      //         let scope_r = ty::re_scope(ex.id);
-      //         let lhs_cmt = self.bccx.cat_expr(lhs);
-      //         self.guarantee_valid(lhs_cmt, m_imm, scope_r);
-      //         let rhs_cmt = self.bccx.cat_expr(rhs);
-      //         self.guarantee_valid(rhs_cmt, m_imm, scope_r);
-      //     }
-      //     visit::visit_expr(ex, self, vt);
-      // }
-
-      ast::expr_field(rcvr, _, _)
-      if self.bccx.method_map.contains_key(&ex.id) => {
-        // Receivers in method calls are always passed by ref.
-        //
-        // Here, the field a.b is in fact a closure.  Eventually, this
-        // should be an &fn, but for now it's an @fn.  In any case,
-        // the enclosing scope is either the call where it is a rcvr
-        // (if used like `a.b(...)`), the call where it's an argument
-        // (if used like `x(a.b)`), or the block (if used like `let x
-        // = a.b`).
-        let scope_r = self.tcx().region_maps.encl_region(ex.id);
-        let rcvr_cmt = self.bccx.cat_expr(rcvr);
-        self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
-        visit::visit_expr(ex, self, vt);
-      }
-
-      // see explanation attached to the `root_ub` field:
-      ast::expr_while(cond, ref body) => {
-        // during the condition, can only root for the condition
-        self.root_ub = cond.id;
-        (vt.visit_expr)(cond, self, vt);
-
-        // during body, can only root for the body
-        self.root_ub = body.node.id;
-        (vt.visit_block)(body, self, vt);
-      }
-
-      // see explanation attached to the `root_ub` field:
-      ast::expr_loop(ref body, _) => {
-        self.root_ub = body.node.id;
-        visit::visit_expr(ex, self, vt);
-      }
-
-      _ => {
-        visit::visit_expr(ex, self, vt);
-      }
-    }
-
-    // Check any contained expressions:
-
-    self.root_ub = old_root_ub;
-}
-
-pub impl GatherLoanCtxt {
-    fn tcx(&mut self) -> ty::ctxt { self.bccx.tcx }
-
-    fn guarantee_adjustments(&mut self,
-                             expr: @ast::expr,
-                             adjustment: &ty::AutoAdjustment) {
-        debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
-               expr.repr(self.tcx()), adjustment);
-        let _i = indenter();
-
-        match *adjustment {
-            ty::AutoAddEnv(*) => {
-                debug!("autoaddenv -- no autoref");
-                return;
-            }
-
-            ty::AutoDerefRef(
-                ty::AutoDerefRef {
-                    autoref: None, _ }) => {
-                debug!("no autoref");
-                return;
-            }
-
-            ty::AutoDerefRef(
-                ty::AutoDerefRef {
-                    autoref: Some(ref autoref),
-                    autoderefs: autoderefs}) => {
-                let mcx = &mem_categorization_ctxt {
-                    tcx: self.tcx(),
-                    method_map: self.bccx.method_map};
-                let cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
-                debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
-
-                match autoref.kind {
-                    ty::AutoPtr => {
-                        self.guarantee_valid(cmt,
-                                             autoref.mutbl,
-                                             autoref.region)
-                    }
-                    ty::AutoBorrowVec | ty::AutoBorrowVecRef => {
-                        let cmt_index = mcx.cat_index(expr, cmt);
-                        self.guarantee_valid(cmt_index,
-                                             autoref.mutbl,
-                                             autoref.region)
-                    }
-                    ty::AutoBorrowFn => {
-                        let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0);
-                        self.guarantee_valid(cmt_deref,
-                                             autoref.mutbl,
-                                             autoref.region)
-                    }
-                }
-            }
-        }
-    }
-
-    // guarantees that addr_of(cmt) will be valid for the duration of
-    // `static_scope_r`, or reports an error.  This may entail taking
-    // out loans, which will be added to the `req_loan_map`.  This can
-    // also entail "rooting" GC'd pointers, which means ensuring
-    // dynamically that they are not freed.
-    fn guarantee_valid(&mut self,
-                       cmt: cmt,
-                       req_mutbl: ast::mutability,
-                       scope_r: ty::Region)
-    {
-
-        let loan_kind = match req_mutbl {
-            m_mutbl => TotalTake,
-            m_imm => TotalFreeze,
-            m_const => Immobile
-        };
-
-        self.bccx.stats.guaranteed_paths += 1;
-
-        debug!("guarantee_valid(cmt=%s, req_mutbl=%?, \
-                loan_kind=%?, scope_r=%s)",
-               self.bccx.cmt_to_repr(cmt),
-               req_mutbl,
-               loan_kind,
-               region_to_str(self.tcx(), scope_r));
-        let _i = indenter();
-
-        match cmt.lp {
-          // If this expression is a loanable path, we MUST take out a
-          // loan.  This is somewhat non-obvious.  You might think,
-          // for example, that if we have an immutable local variable
-          // `x` whose value is being borrowed, we could rely on `x`
-          // not to change.  This is not so, however, because even
-          // immutable locals can be moved.  So we take out a loan on
-          // `x`, guaranteeing that it remains immutable for the
-          // duration of the reference: if there is an attempt to move
-          // it within that scope, the loan will be detected and an
-          // error will be reported.
-          Some(_) => {
-              match loan::loan(self.bccx, cmt, scope_r, loan_kind) {
-                  Err(ref e) => { self.bccx.report((*e)); }
-                  Ok(loans) => {
-                      self.add_loans(cmt, loan_kind, scope_r, loans);
-                  }
-              }
-          }
-
-          // The path is not loanable: in that case, we must try and
-          // preserve it dynamically (or see that it is preserved by
-          // virtue of being rooted in some immutable path).  We must
-          // also check that the mutability of the desired pointer
-          // matches with the actual mutability (but if an immutable
-          // pointer is desired, that is ok as long as we are pure)
-          None => {
-            let result: bckres<PreserveCondition> = {
-                do self.check_mutbl(loan_kind, cmt).chain |pc1| {
-                    do self.bccx.preserve(cmt, scope_r,
-                                          self.item_ub,
-                                          self.root_ub).chain |pc2| {
-                        Ok(pc1.combine(pc2))
-                    }
-                }
-            };
-
-            match result {
-                Ok(PcOk) => {
-                    debug!("result of preserve: PcOk");
-
-                    // we were able guarantee the validity of the ptr,
-                    // perhaps by rooting or because it is immutably
-                    // rooted.  good.
-                    self.bccx.stats.stable_paths += 1;
-                }
-                Ok(PcIfPure(ref e)) => {
-                    debug!("result of preserve: %?", PcIfPure((*e)));
-
-                    // we are only able to guarantee the validity if
-                    // the scope is pure
-                    match scope_r {
-                        ty::re_scope(pure_id) => {
-                            // if the scope is some block/expr in the
-                            // fn, then just require that this scope
-                            // be pure
-                            self.req_maps.pure_map.insert(pure_id, *e);
-                            self.bccx.stats.req_pure_paths += 1;
-
-                            debug!("requiring purity for scope %?",
-                                   scope_r);
-
-                            if self.tcx().sess.borrowck_note_pure() {
-                                self.bccx.span_note(
-                                    cmt.span,
-                                    fmt!("purity required"));
-                            }
-                        }
-                        _ => {
-                            // otherwise, we can't enforce purity for
-                            // that scope, so give up and report an
-                            // error
-                            self.bccx.report((*e));
-                        }
-                    }
-                }
-                Err(ref e) => {
-                    // we cannot guarantee the validity of this pointer
-                    debug!("result of preserve: error");
-                    self.bccx.report((*e));
-                }
-            }
-          }
-        }
-    }
-
-    // Check that the pat `cmt` is compatible with the required
-    // mutability, presuming that it can be preserved to stay alive
-    // long enough.
-    //
-    // For example, if you have an expression like `&x.f` where `x`
-    // has type `@mut{f:int}`, this check might fail because `&x.f`
-    // reqires an immutable pointer, but `f` lives in (aliased)
-    // mutable memory.
-    fn check_mutbl(&mut self,
-                   loan_kind: LoanKind,
-                   cmt: cmt)
-                -> bckres<PreserveCondition> {
-        debug!("check_mutbl(loan_kind=%?, cmt.mutbl=%?)",
-               loan_kind, cmt.mutbl);
-
-        match loan_kind {
-            Immobile => Ok(PcOk),
-
-            TotalTake | PartialTake => {
-                if cmt.mutbl.is_mutable() {
-                    Ok(PcOk)
-                } else {
-                    Err(bckerr { cmt: cmt, code: err_mutbl(loan_kind) })
-                }
-            }
-
-            TotalFreeze | PartialFreeze => {
-                if cmt.mutbl.is_immutable() {
-                    Ok(PcOk)
-                } else if cmt.cat.is_mutable_box() {
-                    Ok(PcOk)
-                } else {
-                    // Eventually:
-                    let e = bckerr {cmt: cmt,
-                                    code: err_mutbl(loan_kind)};
-                    Ok(PcIfPure(e))
-                }
-            }
-        }
-    }
-
-    fn add_loans(&mut self,
-                 cmt: cmt,
-                 loan_kind: LoanKind,
-                 scope_r: ty::Region,
-                 loans: ~[Loan]) {
-        if loans.len() == 0 {
-            return;
-        }
-
-        // Normally we wouldn't allow `re_free` here. However, in this case
-        // it should be sound. Below is nmatsakis' reasoning:
-        //
-        // Perhaps [this permits] a function kind of like this one here, which
-        // consumes one mut pointer and returns a narrower one:
-        //
-        //     struct Foo { f: int }
-        //     fn foo(p: &'v mut Foo) -> &'v mut int { &mut p.f }
-        //
-        // I think this should work fine but there is more subtlety to it than
-        // I at first imagined. Unfortunately it's a very important use case,
-        // I think, so it really ought to work. The changes you [pcwalton]
-        // made to permit re_free() do permit this case, I think, but I'm not
-        // sure what else they permit. I have to think that over a bit.
-        //
-        // Ordinarily, a loan with scope re_free wouldn't make sense, because
-        // you couldn't enforce it. But in this case, your function signature
-        // informs the caller that you demand exclusive access to p and its
-        // contents for the lifetime v. Since borrowed pointers are
-        // non-copyable, they must have (a) made a borrow which will enforce
-        // those conditions and then (b) given you the resulting pointer.
-        // Therefore, they should be respecting the loan. So it actually seems
-        // that it's ok in this case to have a loan with re_free, so long as
-        // the scope of the loan is no greater than the region pointer on
-        // which it is based. Neat but not something I had previously
-        // considered all the way through. (Note that we already rely on
-        // similar reasoning to permit you to return borrowed pointers into
-        // immutable structures, this is just the converse I suppose)
-
-        let scope_id = match scope_r {
-            ty::re_scope(scope_id) |
-            ty::re_free(ty::FreeRegion {scope_id, _}) => {
-                scope_id
-            }
-            _ => {
-                self.bccx.tcx.sess.span_bug(
-                    cmt.span,
-                    fmt!("loans required but scope is scope_region is %s \
-                          (%?)",
-                         region_to_str(self.tcx(), scope_r),
-                         scope_r));
-            }
-        };
-
-        self.add_loans_to_scope_id(scope_id, loans);
-
-        if loan_kind.is_freeze() && !cmt.mutbl.is_immutable() {
-            self.bccx.stats.loaned_paths_imm += 1;
-
-            if self.tcx().sess.borrowck_note_loan() {
-                self.bccx.span_note(
-                    cmt.span,
-                    fmt!("immutable loan required"));
-            }
-        } else {
-            self.bccx.stats.loaned_paths_same += 1;
-        }
-    }
-
-    fn add_loans_to_scope_id(&mut self,
-                             scope_id: ast::node_id,
-                             loans: ~[Loan]) {
-        debug!("adding %u loans to scope_id %?: %s",
-               loans.len(), scope_id,
-               str::connect(loans.map(|l| self.bccx.loan_to_repr(l)), ", "));
-        match self.req_maps.req_loan_map.find(&scope_id) {
-            Some(req_loans) => {
-                req_loans.push_all(loans);
-                return;
-            }
-            None => {}
-        }
-        self.req_maps.req_loan_map.insert(scope_id, @mut loans);
-    }
-
-    fn gather_pat(@mut self,
-                  discr_cmt: cmt,
-                  root_pat: @ast::pat,
-                  arm_id: ast::node_id,
-                  match_id: ast::node_id) {
-        do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
-            match pat.node {
-              ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
-                match bm {
-                  ast::bind_by_ref(mutbl) => {
-                    // ref x or ref x @ p --- creates a ptr which must
-                    // remain valid for the scope of the match
-
-                    // find the region of the resulting pointer (note that
-                    // the type of such a pattern will *always* be a
-                    // region pointer)
-                    let scope_r = ty_region(self.tcx(), pat.span,
-                                            self.tcx().ty(pat));
-
-                    // if the scope of the region ptr turns out to be
-                    // specific to this arm, wrap the categorization with
-                    // a cat_discr() node.  There is a detailed discussion
-                    // of the function of this node in method preserve():
-                    let arm_scope = ty::re_scope(arm_id);
-                    if self.bccx.is_subregion_of(scope_r, arm_scope) {
-                        let cmt_discr = self.bccx.cat_discr(cmt, match_id);
-                        self.guarantee_valid(cmt_discr, mutbl, scope_r);
-                    } else {
-                        self.guarantee_valid(cmt, mutbl, scope_r);
-                    }
-                  }
-                  ast::bind_by_copy | ast::bind_infer => {
-                    // Nothing to do here; neither copies nor moves induce
-                    // borrows.
-                  }
-                }
-              }
-
-              ast::pat_vec(_, Some(slice_pat), _) => {
-                  // The `slice_pat` here creates a slice into the
-                  // original vector.  This is effectively a borrow of
-                  // the elements of the vector being matched.
-
-                  let slice_ty = self.tcx().ty(slice_pat);
-                  let (slice_mutbl, slice_r) =
-                      self.vec_slice_info(slice_pat, slice_ty);
-                  let mcx = self.bccx.mc_ctxt();
-                  let cmt_index = mcx.cat_index(slice_pat, cmt);
-                  self.guarantee_valid(cmt_index, slice_mutbl, slice_r);
-              }
-
-              _ => {}
-            }
-        }
-    }
-
-    fn vec_slice_info(@mut self,
-                      pat: @ast::pat,
-                      slice_ty: ty::t) -> (ast::mutability, ty::Region) {
-        /*!
-         *
-         * In a pattern like [a, b, ..c], normally `c` has slice type,
-         * but if you have [a, b, ..ref c], then the type of `ref c`
-         * will be `&&[]`, so to extract the slice details we have
-         * to recurse through rptrs.
-         */
-
-        match ty::get(slice_ty).sty {
-            ty::ty_evec(slice_mt, ty::vstore_slice(slice_r)) => {
-                (slice_mt.mutbl, slice_r)
-            }
-
-            ty::ty_rptr(_, ref mt) => {
-                self.vec_slice_info(pat, mt.ty)
-            }
-
-            _ => {
-                self.tcx().sess.span_bug(
-                    pat.span,
-                    fmt!("Type of slice pattern is not a slice"));
-            }
-        }
-    }
-
-    fn pat_is_variant_or_struct(@mut self, pat: @ast::pat) -> bool {
-        pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
-    }
-
-    fn pat_is_binding(@mut self, pat: @ast::pat) -> bool {
-        pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
-    }
-}
-
-// Setting up info that preserve needs.
-// This is just the most convenient place to do it.
-fn add_stmt_to_map(stmt: @ast::stmt,
-                   self: @mut GatherLoanCtxt,
-                   vt: visit::vt<@mut GatherLoanCtxt>) {
-    match stmt.node {
-        ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => {
-            self.bccx.stmt_map.insert(id);
-        }
-        _ => ()
-    }
-    visit::visit_stmt(stmt, self, vt);
-}
diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
new file mode 100644
index 0000000000000..330d60a59d3ae
--- /dev/null
+++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
@@ -0,0 +1,347 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module implements the check that the lifetime of a borrow
+//! does not exceed the lifetime of the value being borrowed.
+
+use core::prelude::*;
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
+use middle::ty;
+use syntax::ast::{m_const, m_imm, m_mutbl};
+use syntax::ast;
+use syntax::codemap::span;
+use util::ppaux::{note_and_explain_region};
+
+pub fn guarantee_lifetime(bccx: @BorrowckCtxt,
+                          item_scope_id: ast::node_id,
+                          root_scope_id: ast::node_id,
+                          span: span,
+                          cmt: mc::cmt,
+                          loan_region: ty::Region,
+                          loan_mutbl: ast::mutability) {
+    debug!("guarantee_lifetime(cmt=%s, loan_region=%s)",
+           cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx));
+    let ctxt = GuaranteeLifetimeContext {bccx: bccx,
+                                         item_scope_id: item_scope_id,
+                                         span: span,
+                                         loan_region: loan_region,
+                                         loan_mutbl: loan_mutbl,
+                                         cmt_original: cmt,
+                                         root_scope_id: root_scope_id};
+    ctxt.check(cmt, None);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Private
+
+struct GuaranteeLifetimeContext {
+    bccx: @BorrowckCtxt,
+
+    // the node id of the function body for the enclosing item
+    item_scope_id: ast::node_id,
+
+    // the node id of the innermost loop / function body; this is the
+    // longest scope for which we can root managed boxes
+    root_scope_id: ast::node_id,
+
+    span: span,
+    loan_region: ty::Region,
+    loan_mutbl: ast::mutability,
+    cmt_original: mc::cmt
+}
+
+impl GuaranteeLifetimeContext {
+    fn tcx(&self) -> ty::ctxt {
+        self.bccx.tcx
+    }
+
+    fn check(&self, cmt: mc::cmt, discr_scope: Option<ast::node_id>) {
+        //! Main routine. Walks down `cmt` until we find the "guarantor".
+
+        match cmt.cat {
+            mc::cat_rvalue |
+            mc::cat_implicit_self |
+            mc::cat_copied_upvar(*) |
+            mc::cat_local(*) |
+            mc::cat_arg(*) |
+            mc::cat_self(*) |
+            mc::cat_deref(_, _, mc::region_ptr(*)) |
+            mc::cat_deref(_, _, mc::unsafe_ptr) => {
+                let scope = self.scope(cmt);
+                self.check_scope(scope)
+            }
+
+            mc::cat_stack_upvar(cmt) => {
+                self.check(cmt, discr_scope)
+            }
+
+            mc::cat_static_item => {
+            }
+
+            mc::cat_deref(base, derefs, mc::gc_ptr(ptr_mutbl)) => {
+                let base_scope = self.scope(base);
+
+                // See rule Freeze-Imm-Managed-Ptr-2 in doc.rs
+                let omit_root = (
+                    ptr_mutbl == m_imm &&
+                    self.bccx.is_subregion_of(self.loan_region, base_scope) &&
+                    base.mutbl.is_immutable() &&
+                    !self.is_moved(base)
+                );
+
+                if !omit_root {
+                    self.check_root(cmt, base, derefs, ptr_mutbl, discr_scope);
+                } else {
+                    debug!("omitting root, base=%s, base_scope=%?",
+                           base.repr(self.tcx()), base_scope);
+                }
+            }
+
+            mc::cat_deref(base, _, mc::uniq_ptr(*)) |
+            mc::cat_interior(base, _) => {
+                self.check(base, discr_scope)
+            }
+
+            mc::cat_discr(base, new_discr_scope) => {
+                // Subtle: in a match, we must ensure that each binding
+                // variable remains valid for the duration of the arm in
+                // which it appears, presuming that this arm is taken.
+                // But it is inconvenient in trans to root something just
+                // for one arm.  Therefore, we insert a cat_discr(),
+                // basically a special kind of category that says "if this
+                // value must be dynamically rooted, root it for the scope
+                // `match_id`.
+                //
+                // As an example, consider this scenario:
+                //
+                //    let mut x = @Some(3);
+                //    match *x { Some(y) {...} None {...} }
+                //
+                // Technically, the value `x` need only be rooted
+                // in the `some` arm.  However, we evaluate `x` in trans
+                // before we know what arm will be taken, so we just
+                // always root it for the duration of the match.
+                //
+                // As a second example, consider *this* scenario:
+                //
+                //    let x = @mut @Some(3);
+                //    match x { @@Some(y) {...} @@None {...} }
+                //
+                // Here again, `x` need only be rooted in the `some` arm.
+                // In this case, the value which needs to be rooted is
+                // found only when checking which pattern matches: but
+                // this check is done before entering the arm.  Therefore,
+                // even in this case we just choose to keep the value
+                // rooted for the entire match.  This means the value will be
+                // rooted even if the none arm is taken.  Oh well.
+                //
+                // At first, I tried to optimize the second case to only
+                // root in one arm, but the result was suboptimal: first,
+                // it interfered with the construction of phi nodes in the
+                // arm, as we were adding code to root values before the
+                // phi nodes were added.  This could have been addressed
+                // with a second basic block.  However, the naive approach
+                // also yielded suboptimal results for patterns like:
+                //
+                //    let x = @mut @...;
+                //    match x { @@some_variant(y) | @@some_other_variant(y) =>
+                //
+                // The reason is that we would root the value once for
+                // each pattern and not once per arm.  This is also easily
+                // fixed, but it's yet more code for what is really quite
+                // the corner case.
+                //
+                // Nonetheless, if you decide to optimize this case in the
+                // future, you need only adjust where the cat_discr()
+                // node appears to draw the line between what will be rooted
+                // in the *arm* vs the *match*.
+                self.check(base, Some(new_discr_scope))
+            }
+        }
+    }
+
+    fn check_root(&self,
+                  cmt_deref: mc::cmt,
+                  cmt_base: mc::cmt,
+                  derefs: uint,
+                  ptr_mutbl: ast::mutability,
+                  discr_scope: Option<ast::node_id>) {
+        debug!("check_root(cmt_deref=%s, cmt_base=%s, derefs=%?, ptr_mutbl=%?, \
+                discr_scope=%?)",
+               cmt_deref.repr(self.tcx()),
+               cmt_base.repr(self.tcx()),
+               derefs,
+               ptr_mutbl,
+               discr_scope);
+
+        // Make sure that the loan does not exceed the maximum time
+        // that we can root the value, dynamically.
+        let root_region = ty::re_scope(self.root_scope_id);
+        if !self.bccx.is_subregion_of(self.loan_region, root_region) {
+            self.report_error(
+                err_out_of_root_scope(root_region, self.loan_region));
+            return;
+        }
+
+        // Extract the scope id that indicates how long the rooting is required
+        let root_scope = match self.loan_region {
+            ty::re_scope(id) => id,
+            _ => {
+                // the check above should fail for anything is not re_scope
+                self.bccx.tcx.sess.span_bug(
+                    cmt_base.span,
+                    fmt!("Cannot issue root for scope region: %?",
+                         self.loan_region));
+            }
+        };
+
+        // If inside of a match arm, expand the rooting to the entire
+        // match. See the detailed discussion in `check()` above.
+        let mut root_scope = match discr_scope {
+            None => root_scope,
+            Some(id) => {
+                if self.bccx.is_subscope_of(root_scope, id) {
+                    id
+                } else {
+                    root_scope
+                }
+            }
+        };
+
+        // If we are borrowing the inside of an `@mut` box,
+        // we need to dynamically mark it to prevent incompatible
+        // borrows from happening later.
+        let opt_dyna = match ptr_mutbl {
+            m_imm | m_const => None,
+            m_mutbl => {
+                match self.loan_mutbl {
+                    m_mutbl => Some(DynaMut),
+                    m_imm | m_const => Some(DynaImm)
+                }
+            }
+        };
+
+        // FIXME(#3511) grow to the nearest cleanup scope---this can
+        // cause observable errors if freezing!
+        if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) {
+            debug!("%? is not a cleanup scope, adjusting", root_scope);
+
+            let cleanup_scope =
+                self.bccx.tcx.region_maps.cleanup_scope(root_scope);
+
+            if opt_dyna.is_some() {
+                self.tcx().sess.span_warn(
+                    self.span,
+                    fmt!("Dynamic freeze scope artifically extended \
+                          (see Issue #6248)"));
+                note_and_explain_region(
+                    self.bccx.tcx,
+                    "managed value only needs to be frozen for ",
+                    ty::re_scope(root_scope),
+                    "...");
+                note_and_explain_region(
+                    self.bccx.tcx,
+                    "...but due to Issue #6248, it will be frozen for ",
+                    ty::re_scope(cleanup_scope),
+                    "");
+            }
+
+            root_scope = cleanup_scope;
+        }
+
+        // Add a record of what is required
+        let rm_key = root_map_key {id: cmt_deref.id, derefs: derefs};
+        let root_info = RootInfo {scope: root_scope, freeze: opt_dyna};
+        self.bccx.root_map.insert(rm_key, root_info);
+
+        debug!("root_key: %? root_info: %?", rm_key, root_info);
+    }
+
+    fn check_scope(&self, max_scope: ty::Region) {
+        //! Reports an error if `loan_region` is larger than `valid_scope`
+
+        if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
+            self.report_error(err_out_of_scope(max_scope, self.loan_region));
+        }
+    }
+
+    fn is_moved(&self, cmt: mc::cmt) -> bool {
+        //! True if `cmt` is something that is potentially moved
+        //! out of the current stack frame.
+
+        match cmt.guarantor().cat {
+            mc::cat_local(id) |
+            mc::cat_self(id) |
+            mc::cat_arg(id) => {
+                self.bccx.moved_variables_set.contains(&id)
+            }
+            mc::cat_rvalue |
+            mc::cat_static_item |
+            mc::cat_implicit_self |
+            mc::cat_copied_upvar(*) |
+            mc::cat_deref(*) => {
+                false
+            }
+            r @ mc::cat_interior(*) |
+            r @ mc::cat_stack_upvar(*) |
+            r @ mc::cat_discr(*) => {
+                self.tcx().sess.span_bug(
+                    cmt.span,
+                    fmt!("illegal guarantor category: %?", r));
+            }
+        }
+    }
+
+    fn scope(&self, cmt: mc::cmt) -> ty::Region {
+        //! Returns the maximal region scope for the which the
+        //! lvalue `cmt` is guaranteed to be valid without any
+        //! rooting etc, and presuming `cmt` is not mutated.
+
+        match cmt.cat {
+            mc::cat_rvalue => {
+                ty::re_scope(self.bccx.tcx.region_maps.cleanup_scope(cmt.id))
+            }
+            mc::cat_implicit_self |
+            mc::cat_copied_upvar(_) => {
+                ty::re_scope(self.item_scope_id)
+            }
+            mc::cat_static_item => {
+                ty::re_static
+            }
+            mc::cat_local(local_id) |
+            mc::cat_arg(local_id) |
+            mc::cat_self(local_id) => {
+                self.bccx.tcx.region_maps.encl_region(local_id)
+            }
+            mc::cat_deref(_, _, mc::unsafe_ptr(*)) => {
+                ty::re_static
+            }
+            mc::cat_deref(_, _, mc::region_ptr(_, r)) => {
+                r
+            }
+            mc::cat_deref(cmt, _, mc::uniq_ptr(*)) |
+            mc::cat_deref(cmt, _, mc::gc_ptr(*)) |
+            mc::cat_interior(cmt, _) |
+            mc::cat_stack_upvar(cmt) |
+            mc::cat_discr(cmt, _) => {
+                self.scope(cmt)
+            }
+        }
+    }
+
+    fn report_error(&self, code: bckerr_code) {
+        self.bccx.report(BckError {
+            cmt: self.cmt_original,
+            span: self.span,
+            code: code
+        });
+    }
+}
diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs
new file mode 100644
index 0000000000000..5f3c5d977fef5
--- /dev/null
+++ b/src/librustc/middle/borrowck/gather_loans/mod.rs
@@ -0,0 +1,636 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ----------------------------------------------------------------------
+// Gathering loans
+//
+// The borrow check proceeds in two phases. In phase one, we gather the full
+// set of loans that are required at any point.  These are sorted according to
+// their associated scopes.  In phase two, checking loans, we will then make
+// sure that all of these loans are honored.
+
+use core::prelude::*;
+
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
+use middle::pat_util;
+use middle::ty::{ty_region};
+use middle::ty;
+use util::common::indenter;
+use util::ppaux::{Repr};
+
+use syntax::ast::{m_const, m_imm, m_mutbl};
+use syntax::ast;
+use syntax::ast_util::id_range;
+use syntax::codemap::span;
+use syntax::print::pprust;
+use syntax::visit;
+
+mod lifetime;
+mod restrictions;
+
+/// Context used while gathering loans:
+///
+/// - `bccx`: the the borrow check context
+/// - `item_ub`: the id of the block for the enclosing fn/method item
+/// - `root_ub`: the id of the outermost block for which we can root
+///   an `@T`.  This is the id of the innermost enclosing
+///   loop or function body.
+///
+/// The role of `root_ub` is to prevent us from having to accumulate
+/// vectors of rooted items at runtime.  Consider this case:
+///
+///     fn foo(...) -> int {
+///         let mut ptr: &int;
+///         while some_cond {
+///             let x: @int = ...;
+///             ptr = &*x;
+///         }
+///         *ptr
+///     }
+///
+/// If we are not careful here, we would infer the scope of the borrow `&*x`
+/// to be the body of the function `foo()` as a whole.  We would then
+/// have root each `@int` that is produced, which is an unbounded number.
+/// No good.  Instead what will happen is that `root_ub` will be set to the
+/// body of the while loop and we will refuse to root the pointer `&*x`
+/// because it would have to be rooted for a region greater than `root_ub`.
+struct GatherLoanCtxt {
+    bccx: @BorrowckCtxt,
+    id_range: id_range,
+    all_loans: @mut ~[Loan],
+    item_ub: ast::node_id,
+    repeating_ids: ~[ast::node_id]
+}
+
+pub fn gather_loans(bccx: @BorrowckCtxt,
+                    body: &ast::blk) -> (id_range, @mut ~[Loan]) {
+    let glcx = @mut GatherLoanCtxt {
+        bccx: bccx,
+        id_range: id_range::max(),
+        all_loans: @mut ~[],
+        item_ub: body.node.id,
+        repeating_ids: ~[body.node.id]
+    };
+    let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr,
+                                          visit_block: gather_loans_in_block,
+                                          visit_fn: gather_loans_in_fn,
+                                          visit_stmt: add_stmt_to_map,
+                                          visit_pat: add_pat_to_id_range,
+                                          .. *visit::default_visitor()});
+    (v.visit_block)(body, glcx, v);
+    return (glcx.id_range, glcx.all_loans);
+}
+
+fn add_pat_to_id_range(p: @ast::pat,
+                       self: @mut GatherLoanCtxt,
+                       v: visit::vt<@mut GatherLoanCtxt>) {
+    // NB: This visitor function just adds the pat ids into the id
+    // range. We gather loans that occur in patterns using the
+    // `gather_pat()` method below. Eventually these two should be
+    // brought together.
+    self.id_range.add(p.id);
+    visit::visit_pat(p, self, v);
+}
+
+fn gather_loans_in_fn(fk: &visit::fn_kind,
+                      decl: &ast::fn_decl,
+                      body: &ast::blk,
+                      sp: span,
+                      id: ast::node_id,
+                      self: @mut GatherLoanCtxt,
+                      v: visit::vt<@mut GatherLoanCtxt>) {
+    match fk {
+        // Do not visit items here, the outer loop in borrowck/mod
+        // will visit them for us in turn.
+        &visit::fk_item_fn(*) | &visit::fk_method(*) => {
+            return;
+        }
+
+        // Visit closures as part of the containing item.
+        &visit::fk_anon(*) | &visit::fk_fn_block(*) => {
+            self.push_repeating_id(body.node.id);
+            visit::visit_fn(fk, decl, body, sp, id, self, v);
+            self.pop_repeating_id(body.node.id);
+        }
+    }
+}
+
+fn gather_loans_in_block(blk: &ast::blk,
+                         self: @mut GatherLoanCtxt,
+                         vt: visit::vt<@mut GatherLoanCtxt>) {
+    self.id_range.add(blk.node.id);
+    visit::visit_block(blk, self, vt);
+}
+
+fn gather_loans_in_expr(ex: @ast::expr,
+                        self: @mut GatherLoanCtxt,
+                        vt: visit::vt<@mut GatherLoanCtxt>) {
+    let bccx = self.bccx;
+    let tcx = bccx.tcx;
+
+    debug!("gather_loans_in_expr(expr=%?/%s)",
+           ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
+
+    self.id_range.add(ex.id);
+    self.id_range.add(ex.callee_id);
+
+    // If this expression is borrowed, have to ensure it remains valid:
+    for tcx.adjustments.find(&ex.id).each |&adjustments| {
+        self.guarantee_adjustments(ex, *adjustments);
+    }
+
+    // Special checks for various kinds of expressions:
+    match ex.node {
+      ast::expr_addr_of(mutbl, base) => {
+        let base_cmt = self.bccx.cat_expr(base);
+
+        // make sure that the thing we are pointing out stays valid
+        // for the lifetime `scope_r` of the resulting ptr:
+        let scope_r = ty_region(tcx, ex.span, ty::expr_ty(tcx, ex));
+        self.guarantee_valid(ex.id, ex.span, base_cmt, mutbl, scope_r);
+        visit::visit_expr(ex, self, vt);
+      }
+
+      ast::expr_match(ex_v, ref arms) => {
+        let cmt = self.bccx.cat_expr(ex_v);
+        for arms.each |arm| {
+            for arm.pats.each |pat| {
+                self.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
+            }
+        }
+        visit::visit_expr(ex, self, vt);
+      }
+
+      ast::expr_index(_, arg) |
+      ast::expr_binary(_, _, arg)
+      if self.bccx.method_map.contains_key(&ex.id) => {
+          // Arguments in method calls are always passed by ref.
+          //
+          // Currently these do not use adjustments, so we have to
+          // hardcode this check here (note that the receiver DOES use
+          // adjustments).
+          let scope_r = ty::re_scope(ex.id);
+          let arg_cmt = self.bccx.cat_expr(arg);
+          self.guarantee_valid(arg.id, arg.span, arg_cmt, m_imm, scope_r);
+          visit::visit_expr(ex, self, vt);
+      }
+
+      // see explanation attached to the `root_ub` field:
+      ast::expr_while(cond, ref body) => {
+          // during the condition, can only root for the condition
+          self.push_repeating_id(cond.id);
+          (vt.visit_expr)(cond, self, vt);
+          self.pop_repeating_id(cond.id);
+
+          // during body, can only root for the body
+          self.push_repeating_id(body.node.id);
+          (vt.visit_block)(body, self, vt);
+          self.pop_repeating_id(body.node.id);
+      }
+
+      // see explanation attached to the `root_ub` field:
+      ast::expr_loop(ref body, _) => {
+          self.push_repeating_id(body.node.id);
+          visit::visit_expr(ex, self, vt);
+          self.pop_repeating_id(body.node.id);
+      }
+
+      _ => {
+        visit::visit_expr(ex, self, vt);
+      }
+    }
+}
+
+pub impl GatherLoanCtxt {
+    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
+
+    fn push_repeating_id(&mut self, id: ast::node_id) {
+        self.repeating_ids.push(id);
+    }
+
+    fn pop_repeating_id(&mut self, id: ast::node_id) {
+        let popped = self.repeating_ids.pop();
+        assert!(id == popped);
+    }
+
+    fn guarantee_adjustments(&mut self,
+                             expr: @ast::expr,
+                             adjustment: &ty::AutoAdjustment) {
+        debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
+               expr.repr(self.tcx()), adjustment);
+        let _i = indenter();
+
+        match *adjustment {
+            ty::AutoAddEnv(*) => {
+                debug!("autoaddenv -- no autoref");
+                return;
+            }
+
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: None, _ }) => {
+                debug!("no autoref");
+                return;
+            }
+
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: Some(ref autoref),
+                    autoderefs: autoderefs}) => {
+                let mcx = &mc::mem_categorization_ctxt {
+                    tcx: self.tcx(),
+                    method_map: self.bccx.method_map};
+                let cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
+                debug!("after autoderef, cmt=%s", cmt.repr(self.tcx()));
+
+                match *autoref {
+                    ty::AutoPtr(r, m) => {
+                        self.guarantee_valid(expr.id,
+                                             expr.span,
+                                             cmt,
+                                             m,
+                                             r)
+                    }
+                    ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
+                        let cmt_index = mcx.cat_index(expr, cmt);
+                        self.guarantee_valid(expr.id,
+                                             expr.span,
+                                             cmt_index,
+                                             m,
+                                             r)
+                    }
+                    ty::AutoBorrowFn(r) => {
+                        let cmt_deref = mcx.cat_deref_fn(expr, cmt, 0);
+                        self.guarantee_valid(expr.id,
+                                             expr.span,
+                                             cmt_deref,
+                                             m_imm,
+                                             r)
+                    }
+                    ty::AutoUnsafe(_) => {}
+                }
+            }
+        }
+    }
+
+    // Guarantees that addr_of(cmt) will be valid for the duration of
+    // `static_scope_r`, or reports an error.  This may entail taking
+    // out loans, which will be added to the `req_loan_map`.  This can
+    // also entail "rooting" GC'd pointers, which means ensuring
+    // dynamically that they are not freed.
+    fn guarantee_valid(&mut self,
+                       borrow_id: ast::node_id,
+                       borrow_span: span,
+                       cmt: mc::cmt,
+                       req_mutbl: ast::mutability,
+                       loan_region: ty::Region)
+    {
+        debug!("guarantee_valid(borrow_id=%?, cmt=%s, \
+                req_mutbl=%?, loan_region=%?)",
+               borrow_id,
+               cmt.repr(self.tcx()),
+               req_mutbl,
+               loan_region);
+
+        // a loan for the empty region can never be dereferenced, so
+        // it is always safe
+        if loan_region == ty::re_empty {
+            return;
+        }
+
+        let root_ub = { *self.repeating_ids.last() }; // FIXME(#5074)
+
+        // Check that the lifetime of the borrow does not exceed
+        // the lifetime of the data being borrowed.
+        lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub,
+                                     borrow_span, cmt, loan_region, req_mutbl);
+
+        // Check that we don't allow mutable borrows of non-mutable data.
+        check_mutability(self.bccx, borrow_span, cmt, req_mutbl);
+
+        // Compute the restrictions that are required to enforce the
+        // loan is safe.
+        let restr = restrictions::compute_restrictions(
+            self.bccx, borrow_span,
+            cmt, self.restriction_set(req_mutbl));
+
+        // Create the loan record (if needed).
+        let loan = match restr {
+            restrictions::Safe => {
+                // No restrictions---no loan record necessary
+                return;
+            }
+
+            restrictions::SafeIf(loan_path, restrictions) => {
+                let loan_scope = match loan_region {
+                    ty::re_scope(id) => id,
+                    ty::re_free(ref fr) => fr.scope_id,
+
+                    ty::re_static => {
+                        // If we get here, an error must have been
+                        // reported in
+                        // `lifetime::guarantee_lifetime()`, because
+                        // the only legal ways to have a borrow with a
+                        // static lifetime should not require
+                        // restrictions. To avoid reporting derived
+                        // errors, we just return here without adding
+                        // any loans.
+                        return;
+                    }
+
+                    ty::re_empty |
+                    ty::re_bound(*) |
+                    ty::re_infer(*) => {
+                        self.tcx().sess.span_bug(
+                            cmt.span,
+                            fmt!("Invalid borrow lifetime: %?", loan_region));
+                    }
+                };
+                debug!("loan_scope = %?", loan_scope);
+
+                let gen_scope = self.compute_gen_scope(borrow_id, loan_scope);
+                debug!("gen_scope = %?", gen_scope);
+
+                let kill_scope = self.compute_kill_scope(loan_scope, loan_path);
+                debug!("kill_scope = %?", kill_scope);
+
+                if req_mutbl == m_mutbl {
+                    self.mark_loan_path_as_mutated(loan_path);
+                }
+
+                let all_loans = &mut *self.all_loans; // FIXME(#5074)
+                Loan {
+                    index: all_loans.len(),
+                    loan_path: loan_path,
+                    cmt: cmt,
+                    mutbl: req_mutbl,
+                    gen_scope: gen_scope,
+                    kill_scope: kill_scope,
+                    span: borrow_span,
+                    restrictions: restrictions
+                }
+            }
+        };
+
+        debug!("guarantee_valid(borrow_id=%?), loan=%s",
+               borrow_id, loan.repr(self.tcx()));
+
+        // let loan_path = loan.loan_path;
+        // let loan_gen_scope = loan.gen_scope;
+        // let loan_kill_scope = loan.kill_scope;
+        self.all_loans.push(loan);
+
+        // if loan_gen_scope != borrow_id {
+            // FIXME(#6268) Nested method calls
+            //
+            // Typically, the scope of the loan includes the point at
+            // which the loan is originated. This
+            // This is a subtle case. See the test case
+            // <compile-fail/borrowck-bad-nested-calls-free.rs>
+            // to see what we are guarding against.
+
+            //let restr = restrictions::compute_restrictions(
+            //    self.bccx, borrow_span, cmt, RESTR_EMPTY);
+            //let loan = {
+            //    let all_loans = &mut *self.all_loans; // FIXME(#5074)
+            //    Loan {
+            //        index: all_loans.len(),
+            //        loan_path: loan_path,
+            //        cmt: cmt,
+            //        mutbl: m_const,
+            //        gen_scope: borrow_id,
+            //        kill_scope: kill_scope,
+            //        span: borrow_span,
+            //        restrictions: restrictions
+            //    }
+        // }
+
+        fn check_mutability(bccx: @BorrowckCtxt,
+                            borrow_span: span,
+                            cmt: mc::cmt,
+                            req_mutbl: ast::mutability) {
+            match req_mutbl {
+                m_const => {
+                    // Data of any mutability can be lent as const.
+                }
+
+                m_imm => {
+                    match cmt.mutbl {
+                        mc::McImmutable | mc::McDeclared | mc::McInherited => {
+                            // both imm and mut data can be lent as imm;
+                            // for mutable data, this is a freeze
+                        }
+                        mc::McReadOnly => {
+                            bccx.report(BckError {span: borrow_span,
+                                                  cmt: cmt,
+                                                  code: err_mutbl(req_mutbl)});
+                        }
+                    }
+                }
+
+                m_mutbl => {
+                    // Only mutable data can be lent as mutable.
+                    if !cmt.mutbl.is_mutable() {
+                        bccx.report(BckError {span: borrow_span,
+                                              cmt: cmt,
+                                              code: err_mutbl(req_mutbl)});
+                    }
+                }
+            }
+        }
+    }
+
+    fn restriction_set(&self, req_mutbl: ast::mutability) -> RestrictionSet {
+        match req_mutbl {
+            m_const => RESTR_EMPTY,
+            m_imm   => RESTR_EMPTY | RESTR_MUTATE,
+            m_mutbl => RESTR_EMPTY | RESTR_MUTATE | RESTR_FREEZE
+        }
+    }
+
+    fn mark_loan_path_as_mutated(&self, loan_path: @LoanPath) {
+        //! For mutable loans of content whose mutability derives
+        //! from a local variable, mark the mutability decl as necessary.
+
+        match *loan_path {
+            LpVar(local_id) => {
+                self.tcx().used_mut_nodes.insert(local_id);
+            }
+            LpExtend(base, mc::McInherited, _) => {
+                self.mark_loan_path_as_mutated(base);
+            }
+            LpExtend(_, mc::McDeclared, _) |
+            LpExtend(_, mc::McImmutable, _) |
+            LpExtend(_, mc::McReadOnly, _) => {
+            }
+        }
+    }
+
+    fn compute_gen_scope(&self,
+                         borrow_id: ast::node_id,
+                         loan_scope: ast::node_id) -> ast::node_id {
+        //! Determine when to introduce the loan. Typically the loan
+        //! is introduced at the point of the borrow, but in some cases,
+        //! notably method arguments, the loan may be introduced only
+        //! later, once it comes into scope.
+
+        let rm = self.bccx.tcx.region_maps;
+        if rm.is_subscope_of(borrow_id, loan_scope) {
+            borrow_id
+        } else {
+            loan_scope
+        }
+    }
+
+    fn compute_kill_scope(&self,
+                          loan_scope: ast::node_id,
+                          lp: @LoanPath) -> ast::node_id {
+        //! Determine when the loan restrictions go out of scope.
+        //! This is either when the lifetime expires or when the
+        //! local variable which roots the loan-path goes out of scope,
+        //! whichever happens faster.
+        //!
+        //! It may seem surprising that we might have a loan region
+        //! larger than the variable which roots the loan-path; this can
+        //! come about when variables of `&mut` type are re-borrowed,
+        //! as in this example:
+        //!
+        //!     fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
+        //!         &mut v.counter
+        //!     }
+        //!
+        //! In this case, the borrowed pointer (`'a`) outlives the
+        //! variable `v` that hosts it. Note that this doesn't come up
+        //! with immutable `&` pointers, because borrows of such pointers
+        //! do not require restrictions and hence do not cause a loan.
+
+        let rm = self.bccx.tcx.region_maps;
+        let lexical_scope = rm.encl_scope(lp.node_id());
+        if rm.is_subscope_of(lexical_scope, loan_scope) {
+            lexical_scope
+        } else {
+            assert!(rm.is_subscope_of(loan_scope, lexical_scope));
+            loan_scope
+        }
+    }
+
+    fn gather_pat(&mut self,
+                  discr_cmt: mc::cmt,
+                  root_pat: @ast::pat,
+                  arm_body_id: ast::node_id,
+                  match_id: ast::node_id) {
+        do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
+            match pat.node {
+              ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
+                match bm {
+                  ast::bind_by_ref(mutbl) => {
+                    // ref x or ref x @ p --- creates a ptr which must
+                    // remain valid for the scope of the match
+
+                    // find the region of the resulting pointer (note that
+                    // the type of such a pattern will *always* be a
+                    // region pointer)
+                    let scope_r =
+                        ty_region(self.tcx(), pat.span,
+                                  ty::node_id_to_type(self.tcx(), pat.id));
+
+                    // if the scope of the region ptr turns out to be
+                    // specific to this arm, wrap the categorization
+                    // with a cat_discr() node.  There is a detailed
+                    // discussion of the function of this node in
+                    // `lifetime.rs`:
+                    let arm_scope = ty::re_scope(arm_body_id);
+                    if self.bccx.is_subregion_of(scope_r, arm_scope) {
+                        let cmt_discr = self.bccx.cat_discr(cmt, match_id);
+                        self.guarantee_valid(pat.id, pat.span,
+                                             cmt_discr, mutbl, scope_r);
+                    } else {
+                        self.guarantee_valid(pat.id, pat.span,
+                                             cmt, mutbl, scope_r);
+                    }
+                  }
+                  ast::bind_by_copy | ast::bind_infer => {
+                    // Nothing to do here; neither copies nor moves induce
+                    // borrows.
+                  }
+                }
+              }
+
+              ast::pat_vec(_, Some(slice_pat), _) => {
+                  // The `slice_pat` here creates a slice into the
+                  // original vector.  This is effectively a borrow of
+                  // the elements of the vector being matched.
+
+                  let slice_ty = ty::node_id_to_type(self.tcx(),
+                                                     slice_pat.id);
+                  let (slice_mutbl, slice_r) =
+                      self.vec_slice_info(slice_pat, slice_ty);
+                  let mcx = self.bccx.mc_ctxt();
+                  let cmt_index = mcx.cat_index(slice_pat, cmt);
+                  self.guarantee_valid(pat.id, pat.span,
+                                       cmt_index, slice_mutbl, slice_r);
+              }
+
+              _ => {}
+            }
+        }
+    }
+
+    fn vec_slice_info(&self,
+                      pat: @ast::pat,
+                      slice_ty: ty::t) -> (ast::mutability, ty::Region) {
+        /*!
+         *
+         * In a pattern like [a, b, ..c], normally `c` has slice type,
+         * but if you have [a, b, ..ref c], then the type of `ref c`
+         * will be `&&[]`, so to extract the slice details we have
+         * to recurse through rptrs.
+         */
+
+        match ty::get(slice_ty).sty {
+            ty::ty_evec(slice_mt, ty::vstore_slice(slice_r)) => {
+                (slice_mt.mutbl, slice_r)
+            }
+
+            ty::ty_rptr(_, ref mt) => {
+                self.vec_slice_info(pat, mt.ty)
+            }
+
+            _ => {
+                self.tcx().sess.span_bug(
+                    pat.span,
+                    fmt!("Type of slice pattern is not a slice"));
+            }
+        }
+    }
+
+    fn pat_is_variant_or_struct(&self, pat: @ast::pat) -> bool {
+        pat_util::pat_is_variant_or_struct(self.bccx.tcx.def_map, pat)
+    }
+
+    fn pat_is_binding(&self, pat: @ast::pat) -> bool {
+        pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)
+    }
+}
+
+// Setting up info that preserve needs.
+// This is just the most convenient place to do it.
+fn add_stmt_to_map(stmt: @ast::stmt,
+                   self: @mut GatherLoanCtxt,
+                   vt: visit::vt<@mut GatherLoanCtxt>) {
+    match stmt.node {
+        ast::stmt_expr(_, id) | ast::stmt_semi(_, id) => {
+            self.bccx.stmt_map.insert(id);
+        }
+        _ => ()
+    }
+    visit::visit_stmt(stmt, self, vt);
+}
diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
new file mode 100644
index 0000000000000..0be4c67a9bc91
--- /dev/null
+++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs
@@ -0,0 +1,249 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Computes the restrictions that result from a borrow.
+
+use core::prelude::*;
+use middle::borrowck::*;
+use mc = middle::mem_categorization;
+use middle::ty;
+use syntax::ast::{m_const, m_imm, m_mutbl};
+use syntax::codemap::span;
+
+pub enum RestrictionResult {
+    Safe,
+    SafeIf(@LoanPath, ~[Restriction])
+}
+
+pub fn compute_restrictions(bccx: @BorrowckCtxt,
+                            span: span,
+                            cmt: mc::cmt,
+                            restr: RestrictionSet) -> RestrictionResult {
+    let ctxt = RestrictionsContext {
+        bccx: bccx,
+        span: span,
+        cmt_original: cmt
+    };
+
+    ctxt.compute(cmt, restr)
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Private
+
+struct RestrictionsContext {
+    bccx: @BorrowckCtxt,
+    span: span,
+    cmt_original: mc::cmt
+}
+
+impl RestrictionsContext {
+    fn tcx(&self) -> ty::ctxt {
+        self.bccx.tcx
+    }
+
+    fn compute(&self,
+               cmt: mc::cmt,
+               restrictions: RestrictionSet) -> RestrictionResult {
+
+        // Check for those cases where we cannot control the aliasing
+        // and make sure that we are not being asked to.
+        match cmt.freely_aliasable() {
+            None => {}
+            Some(cause) => {
+                self.check_aliasing_permitted(cause, restrictions);
+            }
+        }
+
+        match cmt.cat {
+            mc::cat_rvalue => {
+                // Effectively, rvalues are stored into a
+                // non-aliasable temporary on the stack. Since they
+                // are inherently non-aliasable, they can only be
+                // accessed later through the borrow itself and hence
+                // must inherently comply with its terms.
+                Safe
+            }
+
+            mc::cat_local(local_id) |
+            mc::cat_arg(local_id) |
+            mc::cat_self(local_id) => {
+                let lp = @LpVar(local_id);
+                SafeIf(lp, ~[Restriction {loan_path: lp,
+                                          set: restrictions}])
+            }
+
+            mc::cat_interior(cmt_base, i @ mc::interior_variant(_)) => {
+                // When we borrow the interior of an enum, we have to
+                // ensure the enum itself is not mutated, because that
+                // could cause the type of the memory to change.
+                let result = self.compute(cmt_base, restrictions | RESTR_MUTATE);
+                self.extend(result, cmt.mutbl, LpInterior(i), restrictions)
+            }
+
+            mc::cat_interior(cmt_base, i @ mc::interior_tuple) |
+            mc::cat_interior(cmt_base, i @ mc::interior_anon_field) |
+            mc::cat_interior(cmt_base, i @ mc::interior_field(*)) |
+            mc::cat_interior(cmt_base, i @ mc::interior_index(*)) => {
+                // For all of these cases, overwriting the base would
+                // not change the type of the memory, so no additional
+                // restrictions are needed.
+                //
+                // FIXME(#5397) --- Mut fields are not treated soundly
+                //                  (hopefully they will just get phased out)
+                let result = self.compute(cmt_base, restrictions);
+                self.extend(result, cmt.mutbl, LpInterior(i), restrictions)
+            }
+
+            mc::cat_deref(cmt_base, _, mc::uniq_ptr(*)) => {
+                // When we borrow the interior of an owned pointer, we
+                // cannot permit the base to be mutated, because that
+                // would cause the unique pointer to be freed.
+                let result = self.compute(cmt_base, restrictions | RESTR_MUTATE);
+                self.extend(result, cmt.mutbl, LpDeref, restrictions)
+            }
+
+            mc::cat_copied_upvar(*) | // FIXME(#2152) allow mutation of upvars
+            mc::cat_static_item(*) |
+            mc::cat_implicit_self(*) |
+            mc::cat_deref(_, _, mc::region_ptr(m_imm, _)) |
+            mc::cat_deref(_, _, mc::gc_ptr(m_imm)) => {
+                Safe
+            }
+
+            mc::cat_deref(_, _, mc::region_ptr(m_const, _)) |
+            mc::cat_deref(_, _, mc::gc_ptr(m_const)) => {
+                self.check_no_mutability_control(cmt, restrictions);
+                Safe
+            }
+
+            mc::cat_deref(cmt_base, _, mc::gc_ptr(m_mutbl)) => {
+                // Technically, no restrictions are *necessary* here.
+                // The validity of the borrow is guaranteed
+                // dynamically.  However, nonetheless we add a
+                // restriction to make a "best effort" to report
+                // static errors. For example, if there is code like
+                //
+                //    let v = @mut ~[1, 2, 3];
+                //    for v.each |e| {
+                //        v.push(e + 1);
+                //    }
+                //
+                // Then the code below would add restrictions on `*v`,
+                // which means that an error would be reported
+                // here. This of course is not perfect. For example,
+                // a function like the following would not report an error
+                // at compile-time but would fail dynamically:
+                //
+                //    let v = @mut ~[1, 2, 3];
+                //    let w = v;
+                //    for v.each |e| {
+                //        w.push(e + 1);
+                //    }
+                //
+                // In addition, we only add a restriction for those cases
+                // where we can construct a sensible loan path, so an
+                // example like the following will fail dynamically:
+                //
+                //    impl V {
+                //      fn get_list(&self) -> @mut ~[int];
+                //    }
+                //    ...
+                //    let v: &V = ...;
+                //    for v.get_list().each |e| {
+                //        v.get_list().push(e + 1);
+                //    }
+                match opt_loan_path(cmt_base) {
+                    None => Safe,
+                    Some(lp_base) => {
+                        let lp = @LpExtend(lp_base, cmt.mutbl, LpDeref);
+                        SafeIf(lp, ~[Restriction {loan_path: lp,
+                                                  set: restrictions}])
+                    }
+                }
+            }
+
+            mc::cat_deref(cmt_base, _, mc::region_ptr(m_mutbl, _)) => {
+                // Because an `&mut` pointer does not inherit its
+                // mutability, we can only prevent mutation or prevent
+                // freezing if it is not aliased. Therefore, in such
+                // cases we restrict aliasing on `cmt_base`.
+                if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) {
+                    let result = self.compute(cmt_base, restrictions | RESTR_ALIAS);
+                    self.extend(result, cmt.mutbl, LpDeref, restrictions)
+                } else {
+                    let result = self.compute(cmt_base, restrictions);
+                    self.extend(result, cmt.mutbl, LpDeref, restrictions)
+                }
+            }
+
+            mc::cat_deref(_, _, mc::unsafe_ptr) => {
+                // We are very trusting when working with unsafe pointers.
+                Safe
+            }
+
+            mc::cat_stack_upvar(cmt_base) |
+            mc::cat_discr(cmt_base, _) => {
+                self.compute(cmt_base, restrictions)
+            }
+        }
+    }
+
+    fn extend(&self,
+              result: RestrictionResult,
+              mc: mc::MutabilityCategory,
+              elem: LoanPathElem,
+              restrictions: RestrictionSet) -> RestrictionResult {
+        match result {
+            Safe => Safe,
+            SafeIf(base_lp, base_vec) => {
+                let lp = @LpExtend(base_lp, mc, elem);
+                SafeIf(lp, vec::append_one(base_vec,
+                                           Restriction {loan_path: lp,
+                                                        set: restrictions}))
+            }
+        }
+    }
+
+    fn check_aliasing_permitted(&self,
+                                cause: mc::AliasableReason,
+                                restrictions: RestrictionSet) {
+        //! This method is invoked when the current `cmt` is something
+        //! where aliasing cannot be controlled. It reports an error if
+        //! the restrictions required that it not be aliased; currently
+        //! this only occurs when re-borrowing an `&mut` pointer.
+        //!
+        //! NB: To be 100% consistent, we should report an error if
+        //! RESTR_FREEZE is found, because we cannot prevent freezing,
+        //! nor would we want to. However, we do not report such an
+        //! error, because this restriction only occurs when the user
+        //! is creating an `&mut` pointer to immutable or read-only
+        //! data, and there is already another piece of code that
+        //! checks for this condition.
+
+        if restrictions.intersects(RESTR_ALIAS) {
+            self.bccx.report_aliasability_violation(
+                self.span,
+                BorrowViolation,
+                cause);
+        }
+    }
+
+    fn check_no_mutability_control(&self,
+                                   cmt: mc::cmt,
+                                   restrictions: RestrictionSet) {
+        if restrictions.intersects(RESTR_MUTATE | RESTR_FREEZE) {
+            self.bccx.report(BckError {span: self.span,
+                                       cmt: cmt,
+                                       code: err_freeze_aliasable_const});
+        }
+    }
+}
+
diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs
deleted file mode 100644
index 641571373bda4..0000000000000
--- a/src/librustc/middle/borrowck/loan.rs
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-/*!
-
-The `Loan` module deals with borrows of *uniquely mutable* data.  We
-say that data is uniquely mutable if the current activation (stack
-frame) controls the only mutable reference to the data.  The most
-common way that this can occur is if the current activation owns the
-data being borrowed, but it can also occur with `&mut` pointers.  The
-primary characteristic of uniquely mutable data is that, at any given
-time, there is at most one path that can be used to mutate it, and
-that path is only accessible from the top stack frame.
-
-Given that some data found at a path P is being borrowed to a borrowed
-pointer with mutability M and lifetime L, the job of the code in this
-module is to compute the set of *loans* that are necessary to ensure
-that (1) the data found at P outlives L and that (2) if M is mutable
-then the path P will not be modified directly or indirectly except
-through that pointer.  A *loan* is the combination of a path P_L, a
-mutability M_L, and a lifetime L_L where:
-
-- The path P_L indicates what data has been lent.
-- The mutability M_L indicates the access rights on the data:
-  - const: the data cannot be moved
-  - immutable/mutable: the data cannot be moved or mutated
-- The lifetime L_L indicates the *scope* of the loan.
-
-FIXME #4730 --- much more needed, don't have time to write this all up now
-
-*/
-
-// ----------------------------------------------------------------------
-// Loan(Ex, M, S) = Ls holds if ToAddr(Ex) will remain valid for the entirety
-// of the scope S, presuming that the returned set of loans `Ls` are honored.
-
-use middle::borrowck::{Loan, bckerr, bckres, BorrowckCtxt, err_mutbl};
-use middle::borrowck::{LoanKind, TotalFreeze, PartialFreeze,
-                       TotalTake, PartialTake, Immobile};
-use middle::borrowck::{err_out_of_scope};
-use middle::mem_categorization::{cat_arg, cat_binding, cat_discr, cat_comp};
-use middle::mem_categorization::{cat_deref, cat_discr, cat_local, cat_self};
-use middle::mem_categorization::{cat_special, cat_stack_upvar, cmt};
-use middle::mem_categorization::{comp_field, comp_index, comp_variant};
-use middle::mem_categorization::{gc_ptr, region_ptr};
-use middle::ty;
-use util::common::indenter;
-
-use syntax::ast::m_imm;
-use syntax::ast;
-
-pub fn loan(bccx: @BorrowckCtxt,
-            cmt: cmt,
-            scope_region: ty::Region,
-            loan_kind: LoanKind) -> bckres<~[Loan]>
-{
-    let mut lc = LoanContext {
-        bccx: bccx,
-        scope_region: scope_region,
-        loans: ~[]
-    };
-    match lc.loan(cmt, loan_kind, true) {
-        Err(ref e) => return Err((*e)),
-        Ok(()) => {}
-    }
-    // FIXME #4945: Workaround for borrow check bug.
-    Ok(copy lc.loans)
-}
-
-struct LoanContext {
-    bccx: @BorrowckCtxt,
-
-    // the region scope for which we must preserve the memory
-    scope_region: ty::Region,
-
-    // accumulated list of loans that will be required
-    loans: ~[Loan]
-}
-
-pub impl LoanContext {
-    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
-
-    fn loan(&mut self,
-            cmt: cmt,
-            loan_kind: LoanKind,
-            owns_lent_data: bool) -> bckres<()>
-    {
-        /*!
-         *
-         * The main routine.
-         *
-         * # Parameters
-         *
-         * - `cmt`: the categorization of the data being borrowed
-         * - `req_mutbl`: the mutability of the borrowed pointer
-         *                that was created
-         * - `owns_lent_data`: indicates whether `cmt` owns the
-         *                     data that is being lent.  See
-         *                     discussion in `issue_loan()`.
-         */
-
-        debug!("loan(%s, %?)",
-               self.bccx.cmt_to_repr(cmt),
-               loan_kind);
-        let _i = indenter();
-
-        // see stable() above; should only be called when `cmt` is lendable
-        if cmt.lp.is_none() {
-            self.bccx.tcx.sess.span_bug(
-                cmt.span,
-                "loan() called with non-lendable value");
-        }
-
-        match cmt.cat {
-          cat_binding(_) | cat_rvalue | cat_special(_) => {
-            // should never be loanable
-            self.bccx.tcx.sess.span_bug(
-                cmt.span,
-                "rvalue with a non-none lp");
-          }
-          cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
-              // FIXME(#4903)
-            let local_region = self.bccx.tcx.region_maps.encl_region(local_id);
-            self.issue_loan(cmt, local_region, loan_kind,
-                            owns_lent_data)
-          }
-          cat_stack_upvar(cmt) => {
-            self.loan(cmt, loan_kind, owns_lent_data)
-          }
-          cat_discr(base, _) => {
-            self.loan(base, loan_kind, owns_lent_data)
-          }
-          cat_comp(cmt_base, comp_field(_, m)) |
-          cat_comp(cmt_base, comp_index(_, m)) => {
-            // For most components, the type of the embedded data is
-            // stable.  Therefore, the base structure need only be
-            // const---unless the component must be immutable.  In
-            // that case, it must also be embedded in an immutable
-            // location, or else the whole structure could be
-            // overwritten and the component along with it.
-            self.loan_stable_comp(cmt, cmt_base, loan_kind, m,
-                                  owns_lent_data)
-          }
-          cat_comp(cmt_base, comp_tuple) |
-          cat_comp(cmt_base, comp_anon_field) => {
-            // As above.
-            self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm,
-                                  owns_lent_data)
-          }
-          cat_comp(cmt_base, comp_variant(enum_did)) => {
-            // For enums, the memory is unstable if there are multiple
-            // variants, because if the enum value is overwritten then
-            // the memory changes type.
-            if ty::enum_is_univariant(self.bccx.tcx, enum_did) {
-                self.loan_stable_comp(cmt, cmt_base, loan_kind, m_imm,
-                                      owns_lent_data)
-            } else {
-                self.loan_unstable_deref(cmt, cmt_base, loan_kind,
-                                         owns_lent_data)
-            }
-          }
-          cat_deref(cmt_base, _, uniq_ptr) => {
-            // For unique pointers, the memory being pointed out is
-            // unstable because if the unique pointer is overwritten
-            // then the memory is freed.
-            self.loan_unstable_deref(cmt, cmt_base, loan_kind,
-                                     owns_lent_data)
-          }
-          cat_deref(cmt_base, _, region_ptr(ast::m_mutbl, region)) => {
-            // Mutable data can be loaned out as immutable or const. We must
-            // loan out the base as well as the main memory. For example,
-            // if someone borrows `*b`, we want to borrow `b` as immutable
-            // as well.
-            do self.loan(cmt_base, TotalFreeze, false).chain |_| {
-                self.issue_loan(cmt, region, loan_kind, owns_lent_data)
-            }
-          }
-          cat_deref(_, _, unsafe_ptr) |
-          cat_deref(_, _, gc_ptr(_)) |
-          cat_deref(_, _, region_ptr(_, _)) => {
-            // Aliased data is simply not lendable.
-            self.bccx.tcx.sess.span_bug(
-                cmt.span,
-                "aliased ptr with a non-none lp");
-          }
-        }
-    }
-
-    // A "stable component" is one where assigning the base of the
-    // component cannot cause the component itself to change types.
-    // Example: record fields.
-    fn loan_stable_comp(&mut self,
-                        cmt: cmt,
-                        cmt_base: cmt,
-                        loan_kind: LoanKind,
-                        comp_mutbl: ast::mutability,
-                        owns_lent_data: bool) -> bckres<()>
-    {
-        let base_kind = match (comp_mutbl, loan_kind) {
-            // Declared as "immutable" means: inherited mutability and
-            // hence mutable iff parent is mutable.  So propagate
-            // mutability on up.
-            (m_imm, TotalFreeze) | (m_imm, PartialFreeze) => PartialFreeze,
-            (m_imm, TotalTake) | (m_imm, PartialTake) => PartialTake,
-
-            // Declared as "mutable" means: always mutable no matter
-            // what the mutability of the base is.  So that means we
-            // can weaken the condition on the base to PartialFreeze.
-            // This implies that the user could freeze the base, but
-            // that is ok since the even with an &T base, the mut
-            // field will still be considered mutable.
-            (_, TotalTake) | (_, PartialTake) |
-            (_, TotalFreeze) | (_, PartialFreeze) => {
-                PartialFreeze
-            }
-
-            // If we just need to guarantee the value won't be moved,
-            // it doesn't matter what mutability the component was
-            // declared with.
-            (_, Immobile) => Immobile,
-        };
-
-        do self.loan(cmt_base, base_kind, owns_lent_data).chain |_ok| {
-            // can use static for the scope because the base
-            // determines the lifetime, ultimately
-            self.issue_loan(cmt, ty::re_static, loan_kind,
-                            owns_lent_data)
-        }
-    }
-
-    // An "unstable deref" means a deref of a ptr/comp where, if the
-    // base of the deref is assigned to, pointers into the result of the
-    // deref would be invalidated. Examples: interior of variants, uniques.
-    fn loan_unstable_deref(&mut self,
-                           cmt: cmt,
-                           cmt_base: cmt,
-                           loan_kind: LoanKind,
-                           owns_lent_data: bool) -> bckres<()> {
-        // Variant components: the base must be immutable, because
-        // if it is overwritten, the types of the embedded data
-        // could change.
-        do self.loan(cmt_base, PartialFreeze, owns_lent_data).chain |_| {
-            // can use static, as in loan_stable_comp()
-            self.issue_loan(cmt, ty::re_static, loan_kind,
-                            owns_lent_data)
-        }
-    }
-
-    fn issue_loan(&mut self,
-                  cmt: cmt,
-                  scope_ub: ty::Region,
-                  loan_kind: LoanKind,
-                  owns_lent_data: bool) -> bckres<()> {
-        // Subtle: the `scope_ub` is the maximal lifetime of `cmt`.
-        // Therefore, if `cmt` owns the data being lent, then the
-        // scope of the loan must be less than `scope_ub`, or else the
-        // data would be freed while the loan is active.
-        //
-        // However, if `cmt` does *not* own the data being lent, then
-        // it is ok if `cmt` goes out of scope during the loan.  This
-        // can occur when you have an `&mut` parameter that is being
-        // reborrowed.
-
-        if !owns_lent_data ||
-            self.bccx.is_subregion_of(self.scope_region, scope_ub)
-        {
-            if cmt.mutbl.is_mutable() {
-                // If this loan is a mutable loan, then mark the loan path (if
-                // it exists) as being used. This is similar to the check
-                // performed in check_loans.rs in check_assignment(), but this
-                // is for a different purpose of having the 'mut' qualifier.
-                for cmt.lp.each |lp| {
-                    for lp.node_id().each |&id| {
-                        self.tcx().used_mut_nodes.insert(id);
-                    }
-                }
-            } else if loan_kind.is_take() {
-                // We do not allow non-mutable data to be "taken"
-                // under any circumstances.
-                return Err(bckerr {
-                    cmt:cmt,
-                    code:err_mutbl(loan_kind)
-                });
-            }
-
-            self.loans.push(Loan {
-                // Note: cmt.lp must be Some(_) because otherwise this
-                // loan process does not apply at all.
-                lp: cmt.lp.get(),
-                cmt: cmt,
-                kind: loan_kind
-            });
-
-            return Ok(());
-        } else {
-            // The loan being requested lives longer than the data
-            // being loaned out!
-            return Err(bckerr {
-                cmt:cmt,
-                code:err_out_of_scope(scope_ub, self.scope_region)
-            });
-        }
-    }
-}
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index f1d45e6e9aaa7..68e70d245f779 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -8,254 +8,64 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-/*!
-# Borrow check
-
-This pass is in job of enforcing *memory safety* and *purity*.  As
-memory safety is by far the more complex topic, I'll focus on that in
-this description, but purity will be covered later on. In the context
-of Rust, memory safety means three basic things:
-
-- no writes to immutable memory;
-- all pointers point to non-freed memory;
-- all pointers point to memory of the same type as the pointer.
-
-The last point might seem confusing: after all, for the most part,
-this condition is guaranteed by the type check.  However, there are
-two cases where the type check effectively delegates to borrow check.
-
-The first case has to do with enums.  If there is a pointer to the
-interior of an enum, and the enum is in a mutable location (such as a
-local variable or field declared to be mutable), it is possible that
-the user will overwrite the enum with a new value of a different
-variant, and thus effectively change the type of the memory that the
-pointer is pointing at.
+/*! See doc.rs for a thorough explanation of the borrow checker */
 
-The second case has to do with mutability.  Basically, the type
-checker has only a limited understanding of mutability.  It will allow
-(for example) the user to get an immutable pointer with the address of
-a mutable local variable.  It will also allow a `@mut T` or `~mut T`
-pointer to be borrowed as a `&r.T` pointer.  These seeming oversights
-are in fact intentional; they allow the user to temporarily treat a
-mutable value as immutable.  It is up to the borrow check to guarantee
-that the value in question is not in fact mutated during the lifetime
-`r` of the reference.
+use core;
+use core::prelude::*;
 
-# Definition of unstable memory
-
-The primary danger to safety arises due to *unstable memory*.
-Unstable memory is memory whose validity or type may change as a
-result of an assignment, move, or a variable going out of scope.
-There are two cases in Rust where memory is unstable: the contents of
-unique boxes and enums.
-
-Unique boxes are unstable because when the variable containing the
-unique box is re-assigned, moves, or goes out of scope, the unique box
-is freed or---in the case of a move---potentially given to another
-task.  In either case, if there is an extant and usable pointer into
-the box, then safety guarantees would be compromised.
-
-Enum values are unstable because they are reassigned the types of
-their contents may change if they are assigned with a different
-variant than they had previously.
-
-# Safety criteria that must be enforced
-
-Whenever a piece of memory is borrowed for lifetime L, there are two
-things which the borrow checker must guarantee.  First, it must
-guarantee that the memory address will remain allocated (and owned by
-the current task) for the entirety of the lifetime L.  Second, it must
-guarantee that the type of the data will not change for the entirety
-of the lifetime L.  In exchange, the region-based type system will
-guarantee that the pointer is not used outside the lifetime L.  These
-guarantees are to some extent independent but are also inter-related.
-
-In some cases, the type of a pointer cannot be invalidated but the
-lifetime can.  For example, imagine a pointer to the interior of
-a shared box like:
-
-    let mut x = @mut {f: 5, g: 6};
-    let y = &mut x.f;
-
-Here, a pointer was created to the interior of a shared box which
-contains a record.  Even if `*x` were to be mutated like so:
-
-    *x = {f: 6, g: 7};
-
-This would cause `*y` to change from 5 to 6, but the pointer pointer
-`y` remains valid.  It still points at an integer even if that integer
-has been overwritten.
-
-However, if we were to reassign `x` itself, like so:
-
-    x = @{f: 6, g: 7};
-
-This could potentially invalidate `y`, because if `x` were the final
-reference to the shared box, then that memory would be released and
-now `y` points at freed memory.  (We will see that to prevent this
-scenario we will *root* shared boxes that reside in mutable memory
-whose contents are borrowed; rooting means that we create a temporary
-to ensure that the box is not collected).
-
-In other cases, like an enum on the stack, the memory cannot be freed
-but its type can change:
-
-    let mut x = Some(5);
-    match x {
-      Some(ref y) => { ... }
-      None => { ... }
-    }
-
-Here as before, the pointer `y` would be invalidated if we were to
-reassign `x` to `none`.  (We will see that this case is prevented
-because borrowck tracks data which resides on the stack and prevents
-variables from reassigned if there may be pointers to their interior)
-
-Finally, in some cases, both dangers can arise.  For example, something
-like the following:
-
-    let mut x = ~Some(5);
-    match x {
-      ~Some(ref y) => { ... }
-      ~None => { ... }
-    }
-
-In this case, if `x` to be reassigned or `*x` were to be mutated, then
-the pointer `y` would be invalided.  (This case is also prevented by
-borrowck tracking data which is owned by the current stack frame)
-
-# Summary of the safety check
-
-In order to enforce mutability, the borrow check has a few tricks up
-its sleeve:
-
-- When data is owned by the current stack frame, we can identify every
-  possible assignment to a local variable and simply prevent
-  potentially dangerous assignments directly.
-
-- If data is owned by a shared box, we can root the box to increase
-  its lifetime.
-
-- If data is found within a borrowed pointer, we can assume that the
-  data will remain live for the entirety of the borrowed pointer.
-
-- We can rely on the fact that pure actions (such as calling pure
-  functions) do not mutate data which is not owned by the current
-  stack frame.
-
-# Possible future directions
-
-There are numerous ways that the `borrowck` could be strengthened, but
-these are the two most likely:
-
-- flow-sensitivity: we do not currently consider flow at all but only
-  block-scoping.  This means that innocent code like the following is
-  rejected:
-
-      let mut x: int;
-      ...
-      x = 5;
-      let y: &int = &x; // immutable ptr created
-      ...
-
-  The reason is that the scope of the pointer `y` is the entire
-  enclosing block, and the assignment `x = 5` occurs within that
-  block.  The analysis is not smart enough to see that `x = 5` always
-  happens before the immutable pointer is created.  This is relatively
-  easy to fix and will surely be fixed at some point.
-
-- finer-grained purity checks: currently, our fallback for
-  guaranteeing random references into mutable, aliasable memory is to
-  require *total purity*.  This is rather strong.  We could use local
-  type-based alias analysis to distinguish writes that could not
-  possibly invalid the references which must be guaranteed.  This
-  would only work within the function boundaries; function calls would
-  still require total purity.  This seems less likely to be
-  implemented in the short term as it would make the code
-  significantly more complex; there is currently no code to analyze
-  the types and determine the possible impacts of a write.
-
-# How the code works
-
-The borrow check code is divided into several major modules, each of
-which is documented in its own file.
-
-The `gather_loans` and `check_loans` are the two major passes of the
-analysis.  The `gather_loans` pass runs over the IR once to determine
-what memory must remain valid and for how long.  Its name is a bit of
-a misnomer; it does in fact gather up the set of loans which are
-granted, but it also determines when @T pointers must be rooted and
-for which scopes purity must be required.
-
-The `check_loans` pass walks the IR and examines the loans and purity
-requirements computed in `gather_loans`.  It checks to ensure that (a)
-the conditions of all loans are honored; (b) no contradictory loans
-were granted (for example, loaning out the same memory as mutable and
-immutable simultaneously); and (c) any purity requirements are
-honored.
-
-The remaining modules are helper modules used by `gather_loans` and
-`check_loans`:
-
-- `categorization` has the job of analyzing an expression to determine
-  what kind of memory is used in evaluating it (for example, where
-  dereferences occur and what kind of pointer is dereferenced; whether
-  the memory is mutable; etc)
-- `loan` determines when data uniquely tied to the stack frame can be
-  loaned out.
-- `preserve` determines what actions (if any) must be taken to preserve
-  aliasable data.  This is the code which decides when to root
-  an @T pointer or to require purity.
-
-# Maps that are created
-
-Borrowck results in two maps.
-
-- `root_map`: identifies those expressions or patterns whose result
-  needs to be rooted.  Conceptually the root_map maps from an
-  expression or pattern node to a `node_id` identifying the scope for
-  which the expression must be rooted (this `node_id` should identify
-  a block or call).  The actual key to the map is not an expression id,
-  however, but a `root_map_key`, which combines an expression id with a
-  deref count and is used to cope with auto-deref.
-
-- `mutbl_map`: identifies those local variables which are modified or
-  moved. This is used by trans to guarantee that such variables are
-  given a memory location and not used as immediates.
- */
-
-use middle::mem_categorization::*;
+use mc = middle::mem_categorization;
 use middle::ty;
 use middle::typeck;
 use middle::moves;
+use middle::dataflow::DataFlowContext;
+use middle::dataflow::DataFlowOperator;
 use util::common::stmt_set;
-use util::ppaux::note_and_explain_region;
+use util::ppaux::{note_and_explain_region, Repr};
 
 use core::hashmap::{HashSet, HashMap};
-use core::to_bytes;
-use syntax::ast::{mutability, m_imm};
+use core::io;
+use core::result::{Result};
+use core::ops::{BitOr, BitAnd};
 use syntax::ast;
+use syntax::ast_map;
+use syntax::visit;
 use syntax::codemap::span;
 
+macro_rules! if_ok(
+    ($inp: expr) => (
+        match $inp {
+            Ok(v) => { v }
+            Err(e) => { return Err(e); }
+        }
+    )
+)
+
+pub mod doc;
+
 pub mod check_loans;
+
+#[path="gather_loans/mod.rs"]
 pub mod gather_loans;
-pub mod loan;
-pub mod preserve;
+
+pub struct LoanDataFlowOperator;
+pub type LoanDataFlow = DataFlowContext<LoanDataFlowOperator>;
 
 pub fn check_crate(
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     moves_map: moves::MovesMap,
+    moved_variables_set: moves::MovedVariablesSet,
     capture_map: moves::CaptureMap,
-    crate: @ast::crate) -> (root_map, mutbl_map, write_guard_map)
+    crate: @ast::crate) -> (root_map, write_guard_map)
 {
     let bccx = @BorrowckCtxt {
         tcx: tcx,
         method_map: method_map,
         moves_map: moves_map,
+        moved_variables_set: moved_variables_set,
         capture_map: capture_map,
         root_map: root_map(),
-        mutbl_map: @mut HashSet::new(),
+        loan_map: @mut HashMap::new(),
         write_guard_map: @mut HashSet::new(),
         stmt_map: @mut HashSet::new(),
         stats: @mut BorrowStats {
@@ -267,8 +77,9 @@ pub fn check_crate(
         }
     };
 
-    let req_maps = gather_loans::gather_loans(bccx, crate);
-    check_loans::check_loans(bccx, req_maps, crate);
+    let v = visit::mk_vt(@visit::Visitor {visit_fn: borrowck_fn,
+                                          ..*visit::default_visitor()});
+    visit::visit_crate(crate, bccx, v);
 
     if tcx.sess.borrowck_stats() {
         io::println(~"--- borrowck stats ---");
@@ -284,7 +95,7 @@ pub fn check_crate(
                          make_stat(bccx, bccx.stats.req_pure_paths)));
     }
 
-    return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
+    return (bccx.root_map, bccx.write_guard_map);
 
     fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str {
         let stat_f = stat as float;
@@ -293,6 +104,45 @@ pub fn check_crate(
     }
 }
 
+fn borrowck_fn(fk: &visit::fn_kind,
+               decl: &ast::fn_decl,
+               body: &ast::blk,
+               sp: span,
+               id: ast::node_id,
+               self: @BorrowckCtxt,
+               v: visit::vt<@BorrowckCtxt>) {
+    match fk {
+        &visit::fk_anon(*) |
+        &visit::fk_fn_block(*) => {
+            // Closures are checked as part of their containing fn item.
+        }
+
+        &visit::fk_item_fn(*) |
+        &visit::fk_method(*) => {
+            debug!("borrowck_fn(id=%?)", id);
+
+            // Check the body of fn items.
+            let (id_range, all_loans) =
+                gather_loans::gather_loans(self, body);
+            let all_loans: &~[Loan] = &*all_loans; // FIXME(#5074)
+            let mut dfcx =
+                DataFlowContext::new(self.tcx,
+                                     self.method_map,
+                                     LoanDataFlowOperator,
+                                     id_range,
+                                     all_loans.len());
+            for all_loans.eachi |loan_idx, loan| {
+                dfcx.add_gen(loan.gen_scope, loan_idx);
+                dfcx.add_kill(loan.kill_scope, loan_idx);
+            }
+            dfcx.propagate(body);
+            check_loans::check_loans(self, &dfcx, *all_loans, body);
+        }
+    }
+
+    visit::visit_fn(fk, decl, body, sp, id, self, v);
+}
+
 // ----------------------------------------------------------------------
 // Type definitions
 
@@ -300,9 +150,10 @@ pub struct BorrowckCtxt {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     moves_map: moves::MovesMap,
+    moved_variables_set: moves::MovedVariablesSet,
     capture_map: moves::CaptureMap,
     root_map: root_map,
-    mutbl_map: mutbl_map,
+    loan_map: LoanMap,
     write_guard_map: write_guard_map,
     stmt_map: stmt_set,
 
@@ -318,137 +169,235 @@ pub struct BorrowStats {
     guaranteed_paths: uint
 }
 
-pub struct RootInfo {
-    scope: ast::node_id,
-    // This will be true if we need to freeze this box at runtime. This will
-    // result in a call to `borrow_as_imm()` and `return_to_mut()`.
-    freezes: bool   // True if we need to freeze this box at runtime.
-}
-
-// a map mapping id's of expressions of gc'd type (@T, @[], etc) where
-// the box needs to be kept live to the id of the scope for which they
-// must stay live.
-pub type root_map = @mut HashMap<root_map_key, RootInfo>;
+pub type LoanMap = @mut HashMap<ast::node_id, @Loan>;
 
-// the keys to the root map combine the `id` of the expression with
-// the number of types that it is autodereferenced.  So, for example,
-// if you have an expression `x.f` and x has type ~@T, we could add an
-// entry {id:x, derefs:0} to refer to `x` itself, `{id:x, derefs:1}`
-// to refer to the deref of the unique pointer, and so on.
-#[deriving(Eq)]
+// The keys to the root map combine the `id` of the deref expression
+// with the number of types that it is *autodereferenced*. So, for
+// example, imagine I have a variable `x: @@@T` and an expression
+// `(*x).f`.  This will have 3 derefs, one explicit and then two
+// autoderefs. These are the relevant `root_map_key` values that could
+// appear:
+//
+//    {id:*x, derefs:0} --> roots `x` (type: @@@T, due to explicit deref)
+//    {id:*x, derefs:1} --> roots `*x` (type: @@T, due to autoderef #1)
+//    {id:*x, derefs:2} --> roots `**x` (type: @T, due to autoderef #2)
+//
+// Note that there is no entry with derefs:3---the type of that expression
+// is T, which is not a box.
+#[deriving(Eq, IterBytes)]
 pub struct root_map_key {
     id: ast::node_id,
     derefs: uint
 }
 
-// set of ids of local vars / formal arguments that are modified / moved.
-// this is used in trans for optimization purposes.
-pub type mutbl_map = @mut HashSet<ast::node_id>;
-
 // A set containing IDs of expressions of gc'd type that need to have a write
 // guard.
 pub type write_guard_map = @mut HashSet<root_map_key>;
 
-// Errors that can occur
-#[deriving(Eq)]
-pub enum bckerr_code {
-    err_mut_uniq,
-    err_mut_variant,
-    err_root_not_permitted,
-    err_mutbl(LoanKind),
-    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
-    err_out_of_scope(ty::Region, ty::Region) // superscope, subscope
-}
+pub type BckResult<T> = Result<T, BckError>;
 
-// Combination of an error code and the categorization of the expression
-// that caused it
 #[deriving(Eq)]
-pub struct bckerr {
-    cmt: cmt,
-    code: bckerr_code
+pub enum PartialTotal {
+    Partial,   // Loan affects some portion
+    Total      // Loan affects entire path
 }
 
-pub enum MoveError {
-    MoveOk,
-    MoveFromIllegalCmt(cmt),
-    MoveWhileBorrowed(/*move*/ cmt, /*loan*/ cmt)
+///////////////////////////////////////////////////////////////////////////
+// Loans and loan paths
+
+/// Record of a loan that was issued.
+pub struct Loan {
+    index: uint,
+    loan_path: @LoanPath,
+    cmt: mc::cmt,
+    mutbl: ast::mutability,
+    restrictions: ~[Restriction],
+    gen_scope: ast::node_id,
+    kill_scope: ast::node_id,
+    span: span,
 }
 
-// shorthand for something that fails with `bckerr` or succeeds with `T`
-pub type bckres<T> = Result<T, bckerr>;
+#[deriving(Eq)]
+pub enum LoanPath {
+    LpVar(ast::node_id),               // `x` in doc.rs
+    LpExtend(@LoanPath, mc::MutabilityCategory, LoanPathElem)
+}
 
 #[deriving(Eq)]
-pub enum LoanKind {
-    TotalFreeze,   // Entire path is frozen   (borrowed as &T)
-    PartialFreeze, // Some subpath is frozen  (borrowed as &T)
-    TotalTake,     // Entire path is "taken"  (borrowed as &mut T)
-    PartialTake,   // Some subpath is "taken" (borrowed as &mut T)
-    Immobile       // Path cannot be moved    (borrowed as &const T)
+pub enum LoanPathElem {
+    LpDeref,                      // `*LV` in doc.rs
+    LpInterior(mc::interior_kind) // `LV.f` in doc.rs
 }
 
-/// a complete record of a loan that was granted
-pub struct Loan {
-    lp: @loan_path,
-    cmt: cmt,
-    kind: LoanKind
+pub impl LoanPath {
+    fn node_id(&self) -> ast::node_id {
+        match *self {
+            LpVar(local_id) => local_id,
+            LpExtend(base, _, _) => base.node_id()
+        }
+    }
 }
 
-/// maps computed by `gather_loans` that are then used by `check_loans`
-///
-/// - `req_loan_map`: map from each block/expr to the required loans needed
-///   for the duration of that block/expr
-/// - `pure_map`: map from block/expr that must be pure to the error message
-///   that should be reported if they are not pure
-pub struct ReqMaps {
-    req_loan_map: HashMap<ast::node_id, @mut ~[Loan]>,
-    pure_map: HashMap<ast::node_id, bckerr>
+pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
+    //! Computes the `LoanPath` (if any) for a `cmt`.
+    //! Note that this logic is somewhat duplicated in
+    //! the method `compute()` found in `gather_loans::restrictions`,
+    //! which allows it to share common loan path pieces as it
+    //! traverses the CMT.
+
+    match cmt.cat {
+        mc::cat_rvalue |
+        mc::cat_static_item |
+        mc::cat_copied_upvar(_) |
+        mc::cat_implicit_self => {
+            None
+        }
+
+        mc::cat_local(id) |
+        mc::cat_arg(id) |
+        mc::cat_self(id) => {
+            Some(@LpVar(id))
+        }
+
+        mc::cat_deref(cmt_base, _, _) => {
+            opt_loan_path(cmt_base).map(
+                |&lp| @LpExtend(lp, cmt.mutbl, LpDeref))
+        }
+
+        mc::cat_interior(cmt_base, ik) => {
+            opt_loan_path(cmt_base).map(
+                |&lp| @LpExtend(lp, cmt.mutbl, LpInterior(ik)))
+        }
+
+        mc::cat_stack_upvar(cmt_base) |
+        mc::cat_discr(cmt_base, _) => {
+            opt_loan_path(cmt_base)
+        }
+    }
 }
 
-pub fn save_and_restore<T:Copy,U>(save_and_restore_t: &mut T,
-                                  f: &fn() -> U) -> U {
-    let old_save_and_restore_t = *save_and_restore_t;
-    let u = f();
-    *save_and_restore_t = old_save_and_restore_t;
-    u
+///////////////////////////////////////////////////////////////////////////
+// Restrictions
+//
+// Borrowing an lvalue often results in *restrictions* that limit what
+// can be done with this lvalue during the scope of the loan:
+//
+// - `RESTR_MUTATE`: The lvalue may not be modified and mutable pointers to
+//                   the value cannot be created.
+// - `RESTR_FREEZE`: Immutable pointers to the value cannot be created.
+// - `RESTR_ALIAS`: The lvalue may not be aliased in any way.
+//
+// In addition, no value which is restricted may be moved. Therefore,
+// restrictions are meaningful even if the RestrictionSet is empty,
+// because the restriction against moves is implied.
+
+pub struct Restriction {
+    loan_path: @LoanPath,
+    set: RestrictionSet
 }
 
-pub fn save_and_restore_managed<T:Copy,U>(save_and_restore_t: @mut T,
-                                          f: &fn() -> U) -> U {
-    let old_save_and_restore_t = *save_and_restore_t;
-    let u = f();
-    *save_and_restore_t = old_save_and_restore_t;
-    u
+pub struct RestrictionSet {
+    bits: u32
 }
 
-pub impl LoanKind {
-    fn is_freeze(&self) -> bool {
-        match *self {
-            TotalFreeze | PartialFreeze => true,
-            _ => false
-        }
+pub static RESTR_EMPTY: RestrictionSet  = RestrictionSet {bits: 0b000};
+pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b001};
+pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b010};
+pub static RESTR_ALIAS: RestrictionSet  = RestrictionSet {bits: 0b100};
+
+pub impl RestrictionSet {
+    fn intersects(&self, restr: RestrictionSet) -> bool {
+        (self.bits & restr.bits) != 0
     }
 
-    fn is_take(&self) -> bool {
-        match *self {
-            TotalTake | PartialTake => true,
-            _ => false
-        }
+    fn contains_all(&self, restr: RestrictionSet) -> bool {
+        (self.bits & restr.bits) == restr.bits
     }
 }
 
-/// Creates and returns a new root_map
+impl BitOr<RestrictionSet,RestrictionSet> for RestrictionSet {
+    fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet {
+        RestrictionSet {bits: self.bits | rhs.bits}
+    }
+}
 
-impl to_bytes::IterBytes for root_map_key {
-    fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.id, &self.derefs, lsb0, f);
+impl BitAnd<RestrictionSet,RestrictionSet> for RestrictionSet {
+    fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet {
+        RestrictionSet {bits: self.bits & rhs.bits}
     }
 }
 
+///////////////////////////////////////////////////////////////////////////
+// Rooting of managed boxes
+//
+// When we borrow the interior of a managed box, it is sometimes
+// necessary to *root* the box, meaning to stash a copy of the box
+// somewhere that the garbage collector will find it. This ensures
+// that the box is not collected for the lifetime of the borrow.
+//
+// As part of this rooting, we sometimes also freeze the box at
+// runtime, meaning that we dynamically detect when the box is
+// borrowed in incompatible ways.
+//
+// Both of these actions are driven through the `root_map`, which maps
+// from a node to the dynamic rooting action that should be taken when
+// that node executes. The node is identified through a
+// `root_map_key`, which pairs a node-id and a deref count---the
+// problem is that sometimes the box that needs to be rooted is only
+// uncovered after a certain number of auto-derefs.
+
+pub struct RootInfo {
+    scope: ast::node_id,
+    freeze: Option<DynaFreezeKind> // Some() if we should freeze box at runtime
+}
+
+pub type root_map = @mut HashMap<root_map_key, RootInfo>;
+
 pub fn root_map() -> root_map {
     return @mut HashMap::new();
 }
 
-// ___________________________________________________________________________
+pub enum DynaFreezeKind {
+    DynaImm,
+    DynaMut
+}
+
+impl ToStr for DynaFreezeKind {
+    fn to_str(&self) -> ~str {
+        match *self {
+            DynaMut => ~"mutable",
+            DynaImm => ~"immutable"
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Errors
+
+// Errors that can occur
+#[deriving(Eq)]
+pub enum bckerr_code {
+    err_mutbl(ast::mutability),
+    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
+    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
+    err_freeze_aliasable_const
+}
+
+// Combination of an error code and the categorization of the expression
+// that caused it
+#[deriving(Eq)]
+pub struct BckError {
+    span: span,
+    cmt: mc::cmt,
+    code: bckerr_code
+}
+
+pub enum AliasableViolationKind {
+    MutabilityViolation,
+    BorrowViolation
+}
+
+///////////////////////////////////////////////////////////////////////////
 // Misc
 
 pub impl BorrowckCtxt {
@@ -456,27 +405,31 @@ pub impl BorrowckCtxt {
         self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
     }
 
-    fn cat_expr(&self, expr: @ast::expr) -> cmt {
-        cat_expr(self.tcx, self.method_map, expr)
+    fn is_subscope_of(&self, r_sub: ast::node_id, r_sup: ast::node_id) -> bool {
+        self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
     }
 
-    fn cat_expr_unadjusted(&self, expr: @ast::expr) -> cmt {
-        cat_expr_unadjusted(self.tcx, self.method_map, expr)
+    fn cat_expr(&self, expr: @ast::expr) -> mc::cmt {
+        mc::cat_expr(self.tcx, self.method_map, expr)
+    }
+
+    fn cat_expr_unadjusted(&self, expr: @ast::expr) -> mc::cmt {
+        mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
     }
 
     fn cat_expr_autoderefd(&self, expr: @ast::expr,
-                           adj: @ty::AutoAdjustment) -> cmt {
+                           adj: @ty::AutoAdjustment) -> mc::cmt {
         match *adj {
             ty::AutoAddEnv(*) => {
                 // no autoderefs
-                cat_expr_unadjusted(self.tcx, self.method_map, expr)
+                mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
             }
 
             ty::AutoDerefRef(
                 ty::AutoDerefRef {
                     autoderefs: autoderefs, _}) => {
-                cat_expr_autoderefd(self.tcx, self.method_map, expr,
-                                    autoderefs)
+                mc::cat_expr_autoderefd(self.tcx, self.method_map, expr,
+                                        autoderefs)
             }
         }
     }
@@ -485,43 +438,33 @@ pub impl BorrowckCtxt {
                id: ast::node_id,
                span: span,
                ty: ty::t,
-               def: ast::def) -> cmt {
-        cat_def(self.tcx, self.method_map, id, span, ty, def)
-    }
-
-    fn cat_variant<N:ast_node>(&self,
-                                arg: N,
-                                enum_did: ast::def_id,
-                                cmt: cmt) -> cmt {
-        cat_variant(self.tcx, self.method_map, arg, enum_did, cmt)
+               def: ast::def) -> mc::cmt {
+        mc::cat_def(self.tcx, self.method_map, id, span, ty, def)
     }
 
-    fn cat_discr(&self, cmt: cmt, match_id: ast::node_id) -> cmt {
-        return @cmt_ {cat:cat_discr(cmt, match_id),.. *cmt};
+    fn cat_discr(&self, cmt: mc::cmt, match_id: ast::node_id) -> mc::cmt {
+        @mc::cmt_ {cat:mc::cat_discr(cmt, match_id),
+                   mutbl:cmt.mutbl.inherit(),
+                   ..*cmt}
     }
 
-    fn mc_ctxt(&self) -> mem_categorization_ctxt {
-        mem_categorization_ctxt {tcx: self.tcx,
+    fn mc_ctxt(&self) -> mc::mem_categorization_ctxt {
+        mc::mem_categorization_ctxt {tcx: self.tcx,
                                  method_map: self.method_map}
     }
 
-    fn cat_pattern(&self, cmt: cmt, pat: @ast::pat, op: &fn(cmt, @ast::pat)) {
+    fn cat_pattern(&self,
+                   cmt: mc::cmt,
+                   pat: @ast::pat,
+                   op: &fn(mc::cmt, @ast::pat)) {
         let mc = self.mc_ctxt();
         mc.cat_pattern(cmt, pat, op);
     }
 
-    fn report_if_err(&self, bres: bckres<()>) {
-        match bres {
-          Ok(()) => (),
-          Err(ref e) => self.report((*e))
-        }
-    }
-
-    fn report(&self, err: bckerr) {
+    fn report(&self, err: BckError) {
         self.span_err(
-            err.cmt.span,
-            fmt!("illegal borrow: %s",
-                 self.bckerr_to_str(err)));
+            err.span,
+            self.bckerr_to_str(err));
         self.note_and_explain_bckerr(err);
     }
 
@@ -533,51 +476,75 @@ pub impl BorrowckCtxt {
         self.tcx.sess.span_note(s, m);
     }
 
-    fn add_to_mutbl_map(&self, cmt: cmt) {
-        match cmt.cat {
-          cat_local(id) | cat_arg(id) => {
-            self.mutbl_map.insert(id);
-          }
-          cat_stack_upvar(cmt) => {
-            self.add_to_mutbl_map(cmt);
-          }
-          _ => ()
-        }
-    }
-
-    fn bckerr_to_str(&self, err: bckerr) -> ~str {
+    fn bckerr_to_str(&self, err: BckError) -> ~str {
         match err.code {
             err_mutbl(lk) => {
-                fmt!("creating %s alias to %s",
-                     self.loan_kind_to_str(lk),
-                     self.cmt_to_str(err.cmt))
+                fmt!("cannot borrow %s %s as %s",
+                     err.cmt.mutbl.to_user_str(),
+                     self.cmt_to_str(err.cmt),
+                     self.mut_to_str(lk))
             }
-            err_mut_uniq => {
-                ~"unique value in aliasable, mutable location"
+            err_out_of_root_scope(*) => {
+                fmt!("cannot root managed value long enough")
             }
-            err_mut_variant => {
-                ~"enum variant in aliasable, mutable location"
+            err_out_of_scope(*) => {
+                fmt!("borrowed value does not live long enough")
             }
-            err_root_not_permitted => {
-                // note: I don't expect users to ever see this error
-                // message, reasons are discussed in attempt_root() in
-                // preserve.rs.
-                ~"rooting is not permitted"
+            err_freeze_aliasable_const => {
+                // Means that the user borrowed a ~T or enum value
+                // residing in &const or @const pointer.  Terrible
+                // error message, but then &const and @const are
+                // supposed to be going away.
+                fmt!("unsafe borrow of aliasable, const value")
             }
-            err_out_of_root_scope(*) => {
-                ~"cannot root managed value long enough"
+        }
+    }
+
+    fn report_aliasability_violation(&self,
+                                     span: span,
+                                     kind: AliasableViolationKind,
+                                     cause: mc::AliasableReason) {
+        let prefix = match kind {
+            MutabilityViolation => "cannot assign to an `&mut`",
+            BorrowViolation => "cannot borrow an `&mut`"
+        };
+
+        match cause {
+            mc::AliasableOther => {
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in an aliasable location", prefix));
             }
-            err_out_of_scope(*) => {
-                ~"borrowed value does not live long enough"
+            mc::AliasableManaged(ast::m_mutbl) => {
+                // FIXME(#6269) reborrow @mut to &mut
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in a `@mut` pointer; \
+                          try borrowing as `&mut` first", prefix));
+            }
+            mc::AliasableManaged(m) => {
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in a `@%s` pointer; \
+                          try an `@mut` instead",
+                         prefix,
+                         self.mut_to_keyword(m)));
+            }
+            mc::AliasableBorrowed(m) => {
+                self.tcx.sess.span_err(
+                    span,
+                    fmt!("%s in a `&%s` pointer; \
+                          try an `&mut` instead",
+                         prefix,
+                         self.mut_to_keyword(m)));
             }
         }
     }
 
-    fn note_and_explain_bckerr(&self, err: bckerr) {
+    fn note_and_explain_bckerr(&self, err: BckError) {
         let code = err.code;
         match code {
-            err_mutbl(*) | err_mut_uniq | err_mut_variant |
-            err_root_not_permitted => {}
+            err_mutbl(*) | err_freeze_aliasable_const(*) => {}
 
             err_out_of_root_scope(super_scope, sub_scope) => {
                 note_and_explain_region(
@@ -607,46 +574,140 @@ pub impl BorrowckCtxt {
         }
     }
 
+    fn append_loan_path_to_str_from_interior(&self,
+                                             loan_path: &LoanPath,
+                                             out: &mut ~str) {
+        match *loan_path {
+            LpExtend(_, _, LpDeref) => {
+                str::push_char(out, '(');
+                self.append_loan_path_to_str(loan_path, out);
+                str::push_char(out, ')');
+            }
+            LpExtend(_, _, LpInterior(_)) |
+            LpVar(_) => {
+                self.append_loan_path_to_str(loan_path, out);
+            }
+        }
+    }
 
-    fn cmt_to_str(&self, cmt: cmt) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cmt_to_str(cmt)
+    fn append_loan_path_to_str(&self, loan_path: &LoanPath, out: &mut ~str) {
+        match *loan_path {
+            LpVar(id) => {
+                match self.tcx.items.find(&id) {
+                    Some(&ast_map::node_local(ident)) => {
+                        str::push_str(out, *self.tcx.sess.intr().get(ident));
+                    }
+                    r => {
+                        self.tcx.sess.bug(
+                            fmt!("Loan path LpVar(%?) maps to %?, not local",
+                                 id, r));
+                    }
+                }
+            }
+
+            LpExtend(lp_base, _, LpInterior(mc::interior_field(fld, _))) => {
+                self.append_loan_path_to_str_from_interior(lp_base, out);
+                str::push_char(out, '.');
+                str::push_str(out, *self.tcx.sess.intr().get(fld));
+            }
+
+            LpExtend(lp_base, _, LpInterior(mc::interior_index(*))) => {
+                self.append_loan_path_to_str_from_interior(lp_base, out);
+                str::push_str(out, "[]");
+            }
+
+            LpExtend(lp_base, _, LpInterior(mc::interior_tuple)) |
+            LpExtend(lp_base, _, LpInterior(mc::interior_anon_field)) |
+            LpExtend(lp_base, _, LpInterior(mc::interior_variant(_))) => {
+                self.append_loan_path_to_str_from_interior(lp_base, out);
+                str::push_str(out, ".(tuple)");
+            }
+
+            LpExtend(lp_base, _, LpDeref) => {
+                str::push_char(out, '*');
+                self.append_loan_path_to_str(lp_base, out);
+            }
+        }
     }
 
-    fn cmt_to_repr(&self, cmt: cmt) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
-        mc.cmt_to_repr(cmt)
+    fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str {
+        let mut result = ~"";
+        self.append_loan_path_to_str(loan_path, &mut result);
+        result
+    }
+
+    fn cmt_to_str(&self, cmt: mc::cmt) -> ~str {
+        let mc = &mc::mem_categorization_ctxt {tcx: self.tcx,
+                                               method_map: self.method_map};
+        mc.cmt_to_str(cmt)
     }
 
     fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
-        let mc = &mem_categorization_ctxt {tcx: self.tcx,
-                                           method_map: self.method_map};
+        let mc = &mc::mem_categorization_ctxt {tcx: self.tcx,
+                                               method_map: self.method_map};
         mc.mut_to_str(mutbl)
     }
 
-    fn loan_kind_to_str(&self, lk: LoanKind) -> ~str {
-        match lk {
-            TotalFreeze | PartialFreeze => ~"immutable",
-            TotalTake | PartialTake => ~"mutable",
-            Immobile => ~"read-only"
+    fn mut_to_keyword(&self, mutbl: ast::mutability) -> &'static str {
+        match mutbl {
+            ast::m_imm => "",
+            ast::m_const => "const",
+            ast::m_mutbl => "mut"
         }
     }
+}
+
+impl DataFlowOperator for LoanDataFlowOperator {
+    #[inline(always)]
+    fn initial_value(&self) -> bool {
+        false // no loans in scope by default
+    }
+
+    #[inline(always)]
+    fn join(&self, succ: uint, pred: uint) -> uint {
+        succ | pred // loans from both preds are in scope
+    }
+
+    #[inline(always)]
+    fn walk_closures(&self) -> bool {
+        true
+    }
+}
 
-    fn loan_to_repr(&self, loan: &Loan) -> ~str {
-        fmt!("Loan(lp=%?, cmt=%s, kind=%?)",
-             loan.lp, self.cmt_to_repr(loan.cmt), loan.kind)
+impl Repr for Loan {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        fmt!("Loan_%?(%s, %?, %?-%?, %s)",
+             self.index,
+             self.loan_path.repr(tcx),
+             self.mutbl,
+             self.gen_scope,
+             self.kill_scope,
+             self.restrictions.repr(tcx))
     }
 }
 
-// The inherent mutability of a component is its default mutability
-// assuming it is embedded in an immutable context.  In general, the
-// mutability can be "overridden" if the component is embedded in a
-// mutable structure.
-pub fn inherent_mutability(ck: comp_kind) -> mutability {
-    match ck {
-      comp_tuple | comp_anon_field | comp_variant(_) => m_imm,
-      comp_field(_, m) | comp_index(_, m)            => m
+impl Repr for Restriction {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        fmt!("Restriction(%s, %x)",
+             self.loan_path.repr(tcx),
+             self.set.bits as uint)
+    }
+}
+
+impl Repr for LoanPath {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        match self {
+            &LpVar(id) => {
+                fmt!("$(%?)", id)
+            }
+
+            &LpExtend(lp, _, LpDeref) => {
+                fmt!("%s.*", lp.repr(tcx))
+            }
+
+            &LpExtend(lp, _, LpInterior(ref interior)) => {
+                fmt!("%s.%s", lp.repr(tcx), interior.repr(tcx))
+            }
+        }
     }
 }
diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs
deleted file mode 100644
index 62358823f2322..0000000000000
--- a/src/librustc/middle/borrowck/preserve.rs
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ----------------------------------------------------------------------
-// Preserve(Ex, S) holds if ToAddr(Ex) will remain valid for the entirety of
-// the scope S.
-//
-
-use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, BorrowckCtxt};
-use middle::borrowck::{err_mut_uniq, err_mut_variant};
-use middle::borrowck::{err_out_of_root_scope, err_out_of_scope};
-use middle::borrowck::{err_root_not_permitted, root_map_key};
-use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
-use middle::mem_categorization::{cat_discr, cat_local, cat_self, cat_special};
-use middle::mem_categorization::{cat_stack_upvar, cmt, comp_field};
-use middle::mem_categorization::{comp_index, comp_variant, gc_ptr};
-use middle::mem_categorization::{region_ptr};
-use middle::ty;
-use util::common::indenter;
-
-use syntax::ast;
-
-pub enum PreserveCondition {
-    PcOk,
-    PcIfPure(bckerr)
-}
-
-pub impl PreserveCondition {
-    // combines two preservation conditions such that if either of
-    // them requires purity, the result requires purity
-    fn combine(&self, pc: PreserveCondition) -> PreserveCondition {
-        match *self {
-            PcOk => {pc}
-            PcIfPure(_) => {*self}
-        }
-    }
-}
-
-pub impl BorrowckCtxt {
-    fn preserve(&self,
-                cmt: cmt,
-                scope_region: ty::Region,
-                item_ub: ast::node_id,
-                root_ub: ast::node_id) -> bckres<PreserveCondition>
-    {
-        let ctxt = PreserveCtxt {
-            bccx: self,
-            scope_region: scope_region,
-            item_ub: item_ub,
-            root_ub: root_ub,
-            root_managed_data: true
-        };
-        ctxt.preserve(cmt)
-    }
-}
-
-struct PreserveCtxt<'self> {
-    bccx: &'self BorrowckCtxt,
-
-    // the region scope for which we must preserve the memory
-    scope_region: ty::Region,
-
-    // the scope for the body of the enclosing fn/method item
-    item_ub: ast::node_id,
-
-    // the upper bound on how long we can root an @T pointer
-    root_ub: ast::node_id,
-
-    // if false, do not attempt to root managed data
-    root_managed_data: bool
-}
-
-pub impl<'self> PreserveCtxt<'self> {
-    fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
-
-    fn preserve(&self, cmt: cmt) -> bckres<PreserveCondition> {
-        debug!("preserve(cmt=%s, root_ub=%?, root_managed_data=%b)",
-               self.bccx.cmt_to_repr(cmt), self.root_ub,
-               self.root_managed_data);
-        let _i = indenter();
-
-        match cmt.cat {
-          cat_special(sk_implicit_self) |
-          cat_special(sk_heap_upvar) => {
-            self.compare_scope(cmt, ty::re_scope(self.item_ub))
-          }
-          cat_special(sk_static_item) | cat_special(sk_method) => {
-            Ok(PcOk)
-          }
-          cat_rvalue => {
-            // when we borrow an rvalue, we can keep it rooted but only
-            // up to the root_ub point
-
-            // When we're in a 'const &x = ...' context, self.root_ub is
-            // zero and the rvalue is static, not bound to a scope.
-            let scope_region = if self.root_ub == 0 {
-                ty::re_static
-            } else {
-                // Maybe if we pass in the parent instead here,
-                // we can prevent the "scope not found" error
-                debug!("scope_region thing: %? ", cmt.id);
-                self.tcx().region_maps.encl_region(cmt.id)
-            };
-
-            self.compare_scope(cmt, scope_region)
-          }
-          cat_stack_upvar(cmt) => {
-            self.preserve(cmt)
-          }
-          cat_local(local_id) => {
-            // Normally, local variables are lendable, and so this
-            // case should never trigger.  However, if we are
-            // preserving an expression like a.b where the field `b`
-            // has @ type, then it will recurse to ensure that the `a`
-            // is stable to try and avoid rooting the value `a.b`.  In
-            // this case, root_managed_data will be false.
-            if self.root_managed_data {
-                self.tcx().sess.span_bug(
-                    cmt.span,
-                    "preserve() called with local and !root_managed_data");
-            }
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_binding(local_id) => {
-            // Bindings are these kind of weird implicit pointers (cc
-            // #2329).  We require (in gather_loans) that they be
-            // rooted in an immutable location.
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_arg(local_id) => {
-            // This can happen as not all args are lendable (e.g., &&
-            // modes).  In that case, the caller guarantees stability
-            // for at least the scope of the fn.  This is basically a
-            // deref of a region ptr.
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_self(local_id) => {
-            let local_region = self.tcx().region_maps.encl_region(local_id);
-            self.compare_scope(cmt, local_region)
-          }
-          cat_comp(cmt_base, comp_field(*)) |
-          cat_comp(cmt_base, comp_index(*)) |
-          cat_comp(cmt_base, comp_tuple) |
-          cat_comp(cmt_base, comp_anon_field) => {
-            // Most embedded components: if the base is stable, the
-            // type never changes.
-            self.preserve(cmt_base)
-          }
-          cat_comp(cmt_base, comp_variant(enum_did)) => {
-            if ty::enum_is_univariant(self.tcx(), enum_did) {
-                self.preserve(cmt_base)
-            } else {
-                // If there are multiple variants: overwriting the
-                // base could cause the type of this memory to change,
-                // so require imm.
-                self.require_imm(cmt, cmt_base, err_mut_variant)
-            }
-          }
-          cat_deref(cmt_base, _, uniq_ptr) => {
-            // Overwriting the base could cause this memory to be
-            // freed, so require imm.
-            self.require_imm(cmt, cmt_base, err_mut_uniq)
-          }
-          cat_deref(_, _, region_ptr(_, region)) => {
-            // References are always "stable" for lifetime `region` by
-            // induction (when the reference of type &MT was created,
-            // the memory must have been stable).
-            self.compare_scope(cmt, region)
-          }
-          cat_deref(_, _, unsafe_ptr) => {
-            // Unsafe pointers are the user's problem
-            Ok(PcOk)
-          }
-          cat_deref(base, derefs, gc_ptr(*)) => {
-            // GC'd pointers of type @MT: if this pointer lives in
-            // immutable, stable memory, then everything is fine.  But
-            // otherwise we have no guarantee the pointer will stay
-            // live, so we must root the pointer (i.e., inc the ref
-            // count) for the duration of the loan.
-            debug!("base.mutbl = %?", base.mutbl);
-            if cmt.cat.derefs_through_mutable_box() {
-                self.attempt_root(cmt, base, derefs)
-            } else if base.mutbl.is_immutable() {
-                let non_rooting_ctxt = PreserveCtxt {
-                    root_managed_data: false,
-                    ..*self
-                };
-                match non_rooting_ctxt.preserve(base) {
-                  Ok(PcOk) => {
-                    Ok(PcOk)
-                  }
-                  Ok(PcIfPure(_)) => {
-                    debug!("must root @T, otherwise purity req'd");
-                    self.attempt_root(cmt, base, derefs)
-                  }
-                  Err(ref e) => {
-                    debug!("must root @T, err: %s",
-                           self.bccx.bckerr_to_str((*e)));
-                    self.attempt_root(cmt, base, derefs)
-                  }
-                }
-            } else {
-                self.attempt_root(cmt, base, derefs)
-            }
-          }
-          cat_discr(base, match_id) => {
-            // Subtle: in a match, we must ensure that each binding
-            // variable remains valid for the duration of the arm in
-            // which it appears, presuming that this arm is taken.
-            // But it is inconvenient in trans to root something just
-            // for one arm.  Therefore, we insert a cat_discr(),
-            // basically a special kind of category that says "if this
-            // value must be dynamically rooted, root it for the scope
-            // `match_id`.
-            //
-            // As an example, consider this scenario:
-            //
-            //    let mut x = @Some(3);
-            //    match *x { Some(y) {...} None {...} }
-            //
-            // Technically, the value `x` need only be rooted
-            // in the `some` arm.  However, we evaluate `x` in trans
-            // before we know what arm will be taken, so we just
-            // always root it for the duration of the match.
-            //
-            // As a second example, consider *this* scenario:
-            //
-            //    let x = @mut @Some(3);
-            //    match x { @@Some(y) {...} @@None {...} }
-            //
-            // Here again, `x` need only be rooted in the `some` arm.
-            // In this case, the value which needs to be rooted is
-            // found only when checking which pattern matches: but
-            // this check is done before entering the arm.  Therefore,
-            // even in this case we just choose to keep the value
-            // rooted for the entire match.  This means the value will be
-            // rooted even if the none arm is taken.  Oh well.
-            //
-            // At first, I tried to optimize the second case to only
-            // root in one arm, but the result was suboptimal: first,
-            // it interfered with the construction of phi nodes in the
-            // arm, as we were adding code to root values before the
-            // phi nodes were added.  This could have been addressed
-            // with a second basic block.  However, the naive approach
-            // also yielded suboptimal results for patterns like:
-            //
-            //    let x = @mut @...;
-            //    match x { @@some_variant(y) | @@some_other_variant(y) =>
-            //
-            // The reason is that we would root the value once for
-            // each pattern and not once per arm.  This is also easily
-            // fixed, but it's yet more code for what is really quite
-            // the corner case.
-            //
-            // Nonetheless, if you decide to optimize this case in the
-            // future, you need only adjust where the cat_discr()
-            // node appears to draw the line between what will be rooted
-            // in the *arm* vs the *match*.
-
-              let match_rooting_ctxt = PreserveCtxt {
-                  scope_region: ty::re_scope(match_id),
-                  ..*self
-              };
-              match_rooting_ctxt.preserve(base)
-          }
-        }
-    }
-
-    /// Reqiures that `cmt` (which is a deref or subcomponent of
-    /// `base`) be found in an immutable location (that is, `base`
-    /// must be immutable).  Also requires that `base` itself is
-    /// preserved.
-    fn require_imm(&self,
-                   cmt: cmt,
-                   cmt_base: cmt,
-                   code: bckerr_code) -> bckres<PreserveCondition> {
-        // Variant contents and unique pointers: must be immutably
-        // rooted to a preserved address.
-        match self.preserve(cmt_base) {
-          // the base is preserved, but if we are not mutable then
-          // purity is required
-          Ok(PcOk) => {
-              if !cmt_base.mutbl.is_immutable() {
-                  Ok(PcIfPure(bckerr {cmt:cmt, code:code}))
-              } else {
-                  Ok(PcOk)
-              }
-          }
-
-          // the base requires purity too, that's fine
-          Ok(PcIfPure(ref e)) => {
-            Ok(PcIfPure((*e)))
-          }
-
-          // base is not stable, doesn't matter
-          Err(ref e) => {
-            Err((*e))
-          }
-        }
-    }
-
-    /// Checks that the scope for which the value must be preserved
-    /// is a subscope of `scope_ub`; if so, success.
-    fn compare_scope(&self,
-                     cmt: cmt,
-                     scope_ub: ty::Region) -> bckres<PreserveCondition> {
-        if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
-            Ok(PcOk)
-        } else {
-            Err(bckerr {
-                cmt:cmt,
-                code:err_out_of_scope(scope_ub, self.scope_region)
-            })
-        }
-    }
-
-    /// Here, `cmt=*base` is always a deref of managed data (if
-    /// `derefs` != 0, then an auto-deref).  This routine determines
-    /// whether it is safe to MAKE cmt stable by rooting the pointer
-    /// `base`.  We can only do the dynamic root if the desired
-    /// lifetime `self.scope_region` is a subset of `self.root_ub`
-    /// scope; otherwise, it would either require that we hold the
-    /// value live for longer than the current fn or else potentially
-    /// require that an statically unbounded number of values be
-    /// rooted (if a loop exists).
-    fn attempt_root(&self, cmt: cmt, base: cmt,
-                    derefs: uint) -> bckres<PreserveCondition> {
-        if !self.root_managed_data {
-            // normally, there is a root_ub; the only time that this
-            // is none is when a boxed value is stored in an immutable
-            // location.  In that case, we will test to see if that
-            // immutable location itself can be preserved long enough
-            // in which case no rooting is necessary.  But there it
-            // would be sort of pointless to avoid rooting the inner
-            // box by rooting an outer box, as it would just keep more
-            // memory live than necessary, so we set root_ub to none.
-            return Err(bckerr { cmt: cmt, code: err_root_not_permitted });
-        }
-
-        let root_region = ty::re_scope(self.root_ub);
-        match self.scope_region {
-          // we can only root values if the desired region is some concrete
-          // scope within the fn body
-          ty::re_scope(scope_id) => {
-            debug!("Considering root map entry for %s: \
-                    node %d:%u -> scope_id %?, root_ub %?",
-                   self.bccx.cmt_to_repr(cmt), base.id,
-                   derefs, scope_id, self.root_ub);
-            if self.bccx.is_subregion_of(self.scope_region, root_region) {
-                debug!("Elected to root");
-                let rk = root_map_key { id: base.id, derefs: derefs };
-                // This code could potentially lead cause boxes to be frozen
-                // for longer than necessarily at runtime. It prevents an
-                // ICE in trans; the fundamental problem is that it's hard
-                // to make sure trans and borrowck have the same notion of
-                // scope. The real fix is to clean up how trans handles
-                // cleanups, but that's hard. If this becomes an issue, it's
-                // an option to just change this to `let scope_to_use =
-                // scope_id;`. Though that would potentially re-introduce
-                // the ICE. See #3511 for more details.
-                let scope_to_use = if
-                    self.bccx.stmt_map.contains(&scope_id) {
-                    // Root it in its parent scope, b/c
-                    // trans won't introduce a new scope for the
-                    // stmt
-                    self.root_ub
-                }
-                else {
-                    // Use the more precise scope
-                    scope_id
-                };
-                // We freeze if and only if this is a *mutable* @ box that
-                // we're borrowing into a pointer.
-                self.bccx.root_map.insert(rk, RootInfo {
-                    scope: scope_to_use,
-                    freezes: cmt.cat.derefs_through_mutable_box()
-                });
-                return Ok(PcOk);
-            } else {
-                debug!("Unable to root");
-                return Err(bckerr {
-                    cmt: cmt,
-                    code: err_out_of_root_scope(root_region,
-                                                self.scope_region)
-                });
-            }
-          }
-
-          // we won't be able to root long enough
-          _ => {
-              return Err(bckerr {
-                cmt:cmt,
-                code:err_out_of_root_scope(root_region, self.scope_region)
-              });
-          }
-
-        }
-    }
-}
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 4e21fcb5bd97d..9e6d90532373a 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -237,7 +237,7 @@ pub fn check_item_recursion(sess: Session,
             match env.def_map.find(&e.id) {
               Some(&def_const(def_id)) => {
                 if ast_util::is_local(def_id) {
-                  match *env.ast_map.get(&def_id.node) {
+                  match env.ast_map.get_copy(&def_id.node) {
                     ast_map::node_item(it, _) => {
                       (v.visit_item)(it, env, v);
                     }
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 479271cf458dd..a50895aa013b5 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -523,7 +523,7 @@ pub fn specialize(cx: @MatchCheckCtxt,
                 }
             }
             pat_enum(_, args) => {
-                match *cx.tcx.def_map.get(&pat_id) {
+                match cx.tcx.def_map.get_copy(&pat_id) {
                     def_const(did) => {
                         let const_expr =
                             lookup_const_by_id(cx.tcx, did).get();
@@ -567,7 +567,7 @@ pub fn specialize(cx: @MatchCheckCtxt,
             }
             pat_struct(_, ref flds, _) => {
                 // Is this a struct or an enum variant?
-                match *cx.tcx.def_map.get(&pat_id) {
+                match cx.tcx.def_map.get_copy(&pat_id) {
                     def_variant(_, variant_id) => {
                         if variant(variant_id) == *ctor_id {
                             // FIXME #4731: Is this right? --pcw
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index 63b9fcf7096d0..7c1933d67853a 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -185,9 +185,7 @@ pub fn lookup_const_by_id(tcx: ty::ctxt,
         }
     } else {
         let maps = astencode::Maps {
-            mutbl_map: @mut HashSet::new(),
             root_map: @mut HashMap::new(),
-            last_use_map: @mut HashMap::new(),
             method_map: @mut HashMap::new(),
             vtable_map: @mut HashMap::new(),
             write_guard_map: @mut HashSet::new(),
diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs
new file mode 100644
index 0000000000000..ccb34851046bd
--- /dev/null
+++ b/src/librustc/middle/dataflow.rs
@@ -0,0 +1,1008 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+
+/*!
+ * A module for propagating forward dataflow information. The analysis
+ * assumes that the items to be propagated can be represented as bits
+ * and thus uses bitvectors. Your job is simply to specify the so-called
+ * GEN and KILL bits for each expression.
+ */
+
+use core::prelude::*;
+use core::cast;
+use core::uint;
+use syntax::ast;
+use syntax::ast_util;
+use syntax::ast_util::id_range;
+use syntax::print::{pp, pprust};
+use middle::ty;
+use middle::typeck;
+use util::ppaux::Repr;
+
+pub struct DataFlowContext<O> {
+    priv tcx: ty::ctxt,
+    priv method_map: typeck::method_map,
+
+    /// the data flow operator
+    priv oper: O,
+
+    /// range of ids that appear within the item in question
+    priv id_range: id_range,
+
+    /// number of bits to propagate per id
+    priv bits_per_id: uint,
+
+    /// number of words we will use to store bits_per_id.
+    /// equal to bits_per_id/uint::bits rounded up.
+    priv words_per_id: uint,
+
+    // Bit sets per id.  The following three fields (`gens`, `kills`,
+    // and `on_entry`) all have the same structure. For each id in
+    // `id_range`, there is a range of words equal to `words_per_id`.
+    // So, to access the bits for any given id, you take a slice of
+    // the full vector (see the method `compute_id_range()`).
+
+    /// bits generated as we exit the scope `id`. Updated by `add_gen()`.
+    priv gens: ~[uint],
+
+    /// bits killed as we exit the scope `id`. Updated by `add_kill()`.
+    priv kills: ~[uint],
+
+    /// bits that are valid on entry to the scope `id`. Updated by
+    /// `propagate()`.
+    priv on_entry: ~[uint]
+}
+
+/// Parameterization for the precise form of data flow that is used.
+pub trait DataFlowOperator {
+    /// Specifies the initial value for each bit in the `on_entry` set
+    fn initial_value(&self) -> bool;
+
+    /// Joins two predecessor bits together, typically either `|` or `&`
+    fn join(&self, succ: uint, pred: uint) -> uint;
+
+    /// True if we should propagate through closures
+    fn walk_closures(&self) -> bool;
+}
+
+struct PropagationContext<'self, O> {
+    dfcx: &'self mut DataFlowContext<O>,
+    changed: bool
+}
+
+#[deriving(Eq)]
+enum LoopKind {
+    /// A `while` or `loop` loop
+    TrueLoop,
+
+    /// A `for` "loop" (i.e., really a func call where `break`, `return`,
+    /// and `loop` all essentially perform an early return from the closure)
+    ForLoop
+}
+
+struct LoopScope<'self> {
+    loop_id: ast::node_id,
+    loop_kind: LoopKind,
+    break_bits: ~[uint]
+}
+
+impl<O:DataFlowOperator> DataFlowContext<O> {
+    pub fn new(tcx: ty::ctxt,
+               method_map: typeck::method_map,
+               oper: O,
+               id_range: id_range,
+               bits_per_id: uint) -> DataFlowContext<O> {
+        let words_per_id = (bits_per_id + uint::bits - 1) / uint::bits;
+
+        debug!("DataFlowContext::new(id_range=%?, bits_per_id=%?, words_per_id=%?)",
+               id_range, bits_per_id, words_per_id);
+
+        let len = (id_range.max - id_range.min) as uint * words_per_id;
+        let gens = vec::from_elem(len, 0);
+        let kills = vec::from_elem(len, 0);
+        let elem = if oper.initial_value() {uint::max_value} else {0};
+        let on_entry = vec::from_elem(len, elem);
+
+        DataFlowContext {
+            tcx: tcx,
+            method_map: method_map,
+            words_per_id: words_per_id,
+            bits_per_id: bits_per_id,
+            oper: oper,
+            id_range: id_range,
+            gens: gens,
+            kills: kills,
+            on_entry: on_entry
+        }
+    }
+
+    pub fn add_gen(&mut self, id: ast::node_id, bit: uint) {
+        //! Indicates that `id` generates `bit`
+
+        debug!("add_gen(id=%?, bit=%?)", id, bit);
+        let (start, end) = self.compute_id_range(id);
+        {
+            let gens = vec::mut_slice(self.gens, start, end);
+            set_bit(gens, bit);
+        }
+    }
+
+    pub fn add_kill(&mut self, id: ast::node_id, bit: uint) {
+        //! Indicates that `id` kills `bit`
+
+        debug!("add_kill(id=%?, bit=%?)", id, bit);
+        let (start, end) = self.compute_id_range(id);
+        {
+            let kills = vec::mut_slice(self.kills, start, end);
+            set_bit(kills, bit);
+        }
+    }
+
+    fn apply_gen_kill(&self, id: ast::node_id, bits: &mut [uint]) {
+        //! Applies the gen and kill sets for `id` to `bits`
+
+        debug!("apply_gen_kill(id=%?, bits=%s) [before]",
+               id, mut_bits_to_str(bits));
+        let (start, end) = self.compute_id_range(id);
+        let gens = self.gens.slice(start, end);
+        bitwise(bits, gens, |a, b| a | b);
+        let kills = self.kills.slice(start, end);
+        bitwise(bits, kills, |a, b| a & !b);
+
+        debug!("apply_gen_kill(id=%?, bits=%s) [after]",
+               id, mut_bits_to_str(bits));
+    }
+
+    fn apply_kill(&self, id: ast::node_id, bits: &mut [uint]) {
+        debug!("apply_kill(id=%?, bits=%s) [before]",
+               id, mut_bits_to_str(bits));
+        let (start, end) = self.compute_id_range(id);
+        let kills = self.kills.slice(start, end);
+        bitwise(bits, kills, |a, b| a & !b);
+        debug!("apply_kill(id=%?, bits=%s) [after]",
+               id, mut_bits_to_str(bits));
+    }
+
+    fn compute_id_range(&self, absolute_id: ast::node_id) -> (uint, uint) {
+        assert!(absolute_id >= self.id_range.min);
+        assert!(absolute_id < self.id_range.max);
+
+        let relative_id = absolute_id - self.id_range.min;
+        let start = (relative_id as uint) * self.words_per_id;
+        let end = start + self.words_per_id;
+        (start, end)
+    }
+
+
+    pub fn each_bit_on_entry(&self,
+                             id: ast::node_id,
+                             f: &fn(uint) -> bool) {
+        //! Iterates through each bit that is set on entry to `id`.
+        //! Only useful after `propagate()` has been called.
+
+        let (start, end) = self.compute_id_range(id);
+        let on_entry = vec::slice(self.on_entry, start, end);
+        debug!("each_bit_on_entry(id=%?, on_entry=%s)",
+               id, bits_to_str(on_entry));
+        self.each_bit(on_entry, f);
+    }
+
+    pub fn each_gen_bit(&self,
+                        id: ast::node_id,
+                        f: &fn(uint) -> bool) {
+        //! Iterates through each bit in the gen set for `id`.
+
+        let (start, end) = self.compute_id_range(id);
+        let gens = vec::slice(self.gens, start, end);
+        debug!("each_gen_bit(id=%?, gens=%s)",
+               id, bits_to_str(gens));
+        self.each_bit(gens, f)
+    }
+
+    fn each_bit(&self,
+                words: &[uint],
+                f: &fn(uint) -> bool) {
+        //! Helper for iterating over the bits in a bit set.
+
+        for words.eachi |word_index, &word| {
+            if word != 0 {
+                let base_index = word_index * uint::bits;
+                for uint::range(0, uint::bits) |offset| {
+                    let bit = 1 << offset;
+                    if (word & bit) != 0 {
+                        // NB: we round up the total number of bits
+                        // that we store in any given bit set so that
+                        // it is an even multiple of uint::bits.  This
+                        // means that there may be some stray bits at
+                        // the end that do not correspond to any
+                        // actual value.  So before we callback, check
+                        // whether the bit_index is greater than the
+                        // actual value the user specified and stop
+                        // iterating if so.
+                        let bit_index = base_index + offset;
+                        if bit_index >= self.bits_per_id || !f(bit_index) {
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+impl<O:DataFlowOperator+Copy+'static> DataFlowContext<O> {
+//                      ^^^^^^^^^^^^ only needed for pretty printing
+    pub fn propagate(&mut self, blk: &ast::blk) {
+        //! Performs the data flow analysis.
+
+        if self.bits_per_id == 0 {
+            // Optimize the surprisingly common degenerate case.
+            return;
+        }
+
+        let mut propcx = PropagationContext {
+            dfcx: self,
+            changed: true
+        };
+
+        let mut temp = vec::from_elem(self.words_per_id, 0);
+        let mut loop_scopes = ~[];
+
+        while propcx.changed {
+            propcx.changed = false;
+            propcx.reset(temp);
+            propcx.walk_block(blk, temp, &mut loop_scopes);
+        }
+
+        debug!("Dataflow result:");
+        debug!("%s", {
+            let this = @copy *self;
+            this.pretty_print_to(io::stderr(), blk);
+            ""
+        });
+    }
+
+    fn pretty_print_to(@self, wr: @io::Writer, blk: &ast::blk) {
+        let pre: @fn(pprust::ann_node) = |node| {
+            let (ps, id) = match node {
+                pprust::node_expr(ps, expr) => (ps, expr.id),
+                pprust::node_block(ps, blk) => (ps, blk.node.id),
+                pprust::node_item(ps, _) => (ps, 0),
+                pprust::node_pat(ps, pat) => (ps, pat.id)
+            };
+
+            if id >= self.id_range.min || id < self.id_range.max {
+                let (start, end) = self.compute_id_range(id);
+                let on_entry = vec::slice(self.on_entry, start, end);
+                let entry_str = bits_to_str(on_entry);
+
+                let gens = vec::slice(self.gens, start, end);
+                let gens_str = if gens.any(|&u| u != 0) {
+                    fmt!(" gen: %s", bits_to_str(gens))
+                } else {
+                    ~""
+                };
+
+                let kills = vec::slice(self.kills, start, end);
+                let kills_str = if kills.any(|&u| u != 0) {
+                    fmt!(" kill: %s", bits_to_str(kills))
+                } else {
+                    ~""
+                };
+
+                let comment_str = fmt!("id %d: %s%s%s",
+                                       id, entry_str, gens_str, kills_str);
+                pprust::synth_comment(ps, comment_str);
+                pp::space(ps.s);
+            }
+        };
+
+        let post: @fn(pprust::ann_node) = |_| {
+        };
+
+        let ps = pprust::rust_printer_annotated(
+            wr, self.tcx.sess.intr(),
+            pprust::pp_ann {pre:pre, post:post});
+        pprust::cbox(ps, pprust::indent_unit);
+        pprust::ibox(ps, 0u);
+        pprust::print_block(ps, blk);
+        pp::eof(ps.s);
+    }
+}
+
+impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
+    fn tcx(&self) -> ty::ctxt {
+        self.dfcx.tcx
+    }
+
+    fn walk_block(&mut self,
+                  blk: &ast::blk,
+                  in_out: &mut [uint],
+                  loop_scopes: &mut ~[LoopScope]) {
+        debug!("DataFlowContext::walk_block(blk.node.id=%?, in_out=%s)",
+               blk.node.id, bits_to_str(reslice(in_out)));
+
+        self.merge_with_entry_set(blk.node.id, in_out);
+
+        for blk.node.stmts.each |&stmt| {
+            self.walk_stmt(stmt, in_out, loop_scopes);
+        }
+
+        self.walk_opt_expr(blk.node.expr, in_out, loop_scopes);
+
+        self.dfcx.apply_gen_kill(blk.node.id, in_out);
+    }
+
+    fn walk_stmt(&mut self,
+                 stmt: @ast::stmt,
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        match stmt.node {
+            ast::stmt_decl(decl, _) => {
+                self.walk_decl(decl, in_out, loop_scopes);
+            }
+
+            ast::stmt_expr(expr, _) | ast::stmt_semi(expr, _) => {
+                self.walk_expr(expr, in_out, loop_scopes);
+            }
+
+            ast::stmt_mac(*) => {
+                self.tcx().sess.span_bug(stmt.span, ~"unexpanded macro");
+            }
+        }
+    }
+
+    fn walk_decl(&mut self,
+                 decl: @ast::decl,
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        match decl.node {
+            ast::decl_local(ref locals) => {
+                for locals.each |local| {
+                    self.walk_pat(local.node.pat, in_out, loop_scopes);
+                    self.walk_opt_expr(local.node.init, in_out, loop_scopes);
+                }
+            }
+
+            ast::decl_item(_) => {}
+        }
+    }
+
+    fn walk_expr(&mut self,
+                 expr: @ast::expr,
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        debug!("DataFlowContext::walk_expr(expr=%s, in_out=%s)",
+               expr.repr(self.dfcx.tcx), bits_to_str(reslice(in_out)));
+
+        self.merge_with_entry_set(expr.id, in_out);
+
+        match expr.node {
+            ast::expr_fn_block(ref decl, ref body) => {
+                if self.dfcx.oper.walk_closures() {
+                    // In the absence of once fns, we must assume that
+                    // every function body will execute more than
+                    // once. Thus we treat every function body like a
+                    // loop.
+                    //
+                    // What is subtle and a bit tricky, also, is how
+                    // to deal with the "output" bits---that is, what
+                    // do we consider to be the successor of a
+                    // function body, given that it could be called
+                    // from any point within its lifetime? What we do
+                    // is to add their effects immediately as of the
+                    // point of creation. Of course we have to ensure
+                    // that this is sound for the analyses which make
+                    // use of dataflow.
+                    //
+                    // In the case of the initedness checker (which
+                    // does not currently use dataflow, but I hope to
+                    // convert at some point), we will simply not walk
+                    // closures at all, so it's a moot point.
+                    //
+                    // In the case of the borrow checker, this means
+                    // the loans which would be created by calling a
+                    // function come into effect immediately when the
+                    // function is created. This is guaranteed to be
+                    // earlier than the point at which the loan
+                    // actually comes into scope (which is the point
+                    // at which the closure is *called*). Because
+                    // loans persist until the scope of the loans is
+                    // exited, it is always a safe approximation to
+                    // have a loan begin earlier than it actually will
+                    // at runtime, so this should be sound.
+                    //
+                    // We stil have to be careful in the region
+                    // checker and borrow checker to treat function
+                    // bodies like loops, which implies some
+                    // limitations. For example, a closure cannot root
+                    // a managed box for longer than its body.
+                    //
+                    // General control flow looks like this:
+                    //
+                    //  +- (expr) <----------+
+                    //  |    |               |
+                    //  |    v               |
+                    //  |  (body) -----------+--> (exit)
+                    //  |    |               |
+                    //  |    + (break/loop) -+
+                    //  |                    |
+                    //  +--------------------+
+                    //
+                    // This is a bit more conservative than a loop.
+                    // Note that we must assume that even after a
+                    // `break` occurs (e.g., in a `for` loop) that the
+                    // closure may be reinvoked.
+                    //
+                    // One difference from other loops is that `loop`
+                    // and `break` statements which target a closure
+                    // both simply add to the `break_bits`.
+
+                    // func_bits represents the state when the function
+                    // returns
+                    let mut func_bits = reslice(in_out).to_vec();
+
+                    loop_scopes.push(LoopScope {
+                        loop_id: expr.id,
+                        loop_kind: ForLoop,
+                        break_bits: reslice(in_out).to_vec()
+                    });
+                    for decl.inputs.each |input| {
+                        self.walk_pat(input.pat, func_bits, loop_scopes);
+                    }
+                    self.walk_block(body, func_bits, loop_scopes);
+
+                    // add the bits from any early return via `break`,
+                    // `continue`, or `return` into `func_bits`
+                    let loop_scope = loop_scopes.pop();
+                    join_bits(&self.dfcx.oper, loop_scope.break_bits, func_bits);
+
+                    // add `func_bits` to the entry bits for `expr`,
+                    // since we must assume the function may be called
+                    // more than once
+                    self.add_to_entry_set(expr.id, reslice(func_bits));
+
+                    // the final exit bits include whatever was present
+                    // in the original, joined with the bits from the function
+                    join_bits(&self.dfcx.oper, func_bits, in_out);
+                }
+            }
+
+            ast::expr_if(cond, ref then, els) => {
+                //
+                //     (cond)
+                //       |
+                //       v
+                //      ( )
+                //     /   \
+                //    |     |
+                //    v     v
+                //  (then)(els)
+                //    |     |
+                //    v     v
+                //   (  succ  )
+                //
+                self.walk_expr(cond, in_out, loop_scopes);
+
+                let mut then_bits = reslice(in_out).to_vec();
+                self.walk_block(then, then_bits, loop_scopes);
+
+                self.walk_opt_expr(els, in_out, loop_scopes);
+                join_bits(&self.dfcx.oper, then_bits, in_out);
+            }
+
+            ast::expr_while(cond, ref blk) => {
+                //
+                //     (expr) <--+
+                //       |       |
+                //       v       |
+                //  +--(cond)    |
+                //  |    |       |
+                //  |    v       |
+                //  v  (blk) ----+
+                //       |
+                //    <--+ (break)
+                //
+
+                self.walk_expr(cond, in_out, loop_scopes);
+
+                let mut body_bits = reslice(in_out).to_vec();
+                loop_scopes.push(LoopScope {
+                    loop_id: expr.id,
+                    loop_kind: TrueLoop,
+                    break_bits: reslice(in_out).to_vec()
+                });
+                self.walk_block(blk, body_bits, loop_scopes);
+                self.add_to_entry_set(expr.id, body_bits);
+                let new_loop_scope = loop_scopes.pop();
+                copy_bits(new_loop_scope.break_bits, in_out);
+            }
+
+            ast::expr_loop(ref blk, _) => {
+                //
+                //     (expr) <--+
+                //       |       |
+                //       v       |
+                //     (blk) ----+
+                //       |
+                //    <--+ (break)
+                //
+
+                let mut body_bits = reslice(in_out).to_vec();
+                self.reset(in_out);
+                loop_scopes.push(LoopScope {
+                    loop_id: expr.id,
+                    loop_kind: TrueLoop,
+                    break_bits: reslice(in_out).to_vec()
+                });
+                self.walk_block(blk, body_bits, loop_scopes);
+                self.add_to_entry_set(expr.id, body_bits);
+
+                let new_loop_scope = loop_scopes.pop();
+                assert_eq!(new_loop_scope.loop_id, expr.id);
+                copy_bits(new_loop_scope.break_bits, in_out);
+            }
+
+            ast::expr_match(discr, ref arms) => {
+                //
+                //    (discr)
+                //     / | \
+                //    |  |  |
+                //    v  v  v
+                //   (..arms..)
+                //    |  |  |
+                //    v  v  v
+                //   (  succ  )
+                //
+                //
+                self.walk_expr(discr, in_out, loop_scopes);
+
+                let mut guards = reslice(in_out).to_vec();
+
+                // We know that exactly one arm will be taken, so we
+                // can start out with a blank slate and just union
+                // together the bits from each arm:
+                self.reset(in_out);
+
+                for arms.each |arm| {
+                    // in_out reflects the discr and all guards to date
+                    self.walk_opt_expr(arm.guard, guards, loop_scopes);
+
+                    // determine the bits for the body and then union
+                    // them into `in_out`, which reflects all bodies to date
+                    let mut body = reslice(guards).to_vec();
+                    self.walk_pat_alternatives(arm.pats, body, loop_scopes);
+                    self.walk_block(&arm.body, body, loop_scopes);
+                    join_bits(&self.dfcx.oper, body, in_out);
+                }
+            }
+
+            ast::expr_ret(o_e) => {
+                self.walk_opt_expr(o_e, in_out, loop_scopes);
+
+                // is this a return from a `for`-loop closure?
+                match loop_scopes.position(|s| s.loop_kind == ForLoop) {
+                    Some(i) => {
+                        // if so, add the in_out bits to the state
+                        // upon exit. Remember that we cannot count
+                        // upon the `for` loop function not to invoke
+                        // the closure again etc.
+                        self.break_from_to(expr, &mut loop_scopes[i], in_out);
+                    }
+
+                    None => {}
+                }
+
+                self.reset(in_out);
+            }
+
+            ast::expr_break(label) => {
+                let scope = self.find_scope(expr, label, loop_scopes);
+                self.break_from_to(expr, scope, in_out);
+                self.reset(in_out);
+            }
+
+            ast::expr_again(label) => {
+                let scope = self.find_scope(expr, label, loop_scopes);
+
+                match scope.loop_kind {
+                    TrueLoop => {
+                        self.pop_scopes(expr, scope, in_out);
+                        self.add_to_entry_set(scope.loop_id, reslice(in_out));
+                    }
+
+                    ForLoop => {
+                        // If this `loop` construct is looping back to a `for`
+                        // loop, then `loop` is really just a return from the
+                        // closure. Therefore, we treat it the same as `break`.
+                        // See case for `expr_fn_block` for more details.
+                        self.break_from_to(expr, scope, in_out);
+                    }
+                }
+
+                self.reset(in_out);
+            }
+
+            ast::expr_assign(l, r) |
+            ast::expr_assign_op(_, l, r) => {
+                self.walk_expr(r, in_out, loop_scopes);
+                self.walk_expr(l, in_out, loop_scopes);
+            }
+
+            ast::expr_swap(l, r) => {
+                self.walk_expr(l, in_out, loop_scopes);
+                self.walk_expr(r, in_out, loop_scopes);
+            }
+
+            ast::expr_vec(ref exprs, _) => {
+                self.walk_exprs(*exprs, in_out, loop_scopes)
+            }
+
+            ast::expr_repeat(l, r, _) => {
+                self.walk_expr(l, in_out, loop_scopes);
+                self.walk_expr(r, in_out, loop_scopes);
+            }
+
+            ast::expr_struct(_, ref fields, with_expr) => {
+                self.walk_opt_expr(with_expr, in_out, loop_scopes);
+                for fields.each |field| {
+                    self.walk_expr(field.node.expr, in_out, loop_scopes);
+                }
+            }
+
+            ast::expr_call(f, ref args, _) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               f, *args, in_out, loop_scopes);
+            }
+
+            ast::expr_method_call(rcvr, _, _, ref args, _) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               rcvr, *args, in_out, loop_scopes);
+            }
+
+            ast::expr_index(l, r) |
+            ast::expr_binary(_, l, r) if self.is_method_call(expr) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               l, [r], in_out, loop_scopes);
+            }
+
+            ast::expr_unary(_, e) if self.is_method_call(expr) => {
+                self.walk_call(expr.callee_id, expr.id,
+                               e, [], in_out, loop_scopes);
+            }
+
+            ast::expr_tup(ref exprs) => {
+                self.walk_exprs(*exprs, in_out, loop_scopes);
+            }
+
+            ast::expr_binary(op, l, r) if ast_util::lazy_binop(op) => {
+                self.walk_expr(l, in_out, loop_scopes);
+                let temp = reslice(in_out).to_vec();
+                self.walk_expr(r, in_out, loop_scopes);
+                join_bits(&self.dfcx.oper, temp, in_out);
+            }
+
+            ast::expr_log(l, r) |
+            ast::expr_index(l, r) |
+            ast::expr_binary(_, l, r) => {
+                self.walk_exprs([l, r], in_out, loop_scopes);
+            }
+
+            ast::expr_lit(*) |
+            ast::expr_path(*) => {
+            }
+
+            ast::expr_addr_of(_, e) |
+            ast::expr_copy(e) |
+            ast::expr_loop_body(e) |
+            ast::expr_do_body(e) |
+            ast::expr_cast(e, _) |
+            ast::expr_unary(_, e) |
+            ast::expr_paren(e) |
+            ast::expr_vstore(e, _) |
+            ast::expr_field(e, _, _) => {
+                self.walk_expr(e, in_out, loop_scopes);
+            }
+
+            ast::expr_inline_asm(ref inline_asm) => {
+                for inline_asm.inputs.each |&(_, expr)| {
+                    self.walk_expr(expr, in_out, loop_scopes);
+                }
+                for inline_asm.outputs.each |&(_, expr)| {
+                    self.walk_expr(expr, in_out, loop_scopes);
+                }
+            }
+
+            ast::expr_block(ref blk) => {
+                self.walk_block(blk, in_out, loop_scopes);
+            }
+
+            ast::expr_mac(*) => {
+                self.tcx().sess.span_bug(expr.span, ~"unexpanded macro");
+            }
+        }
+
+        self.dfcx.apply_gen_kill(expr.id, in_out);
+    }
+
+    fn pop_scopes(&mut self,
+                  from_expr: @ast::expr,
+                  to_scope: &mut LoopScope,
+                  in_out: &mut [uint]) {
+        //! Whenever you have a `break` or a `loop` statement, flow
+        //! exits through any number of enclosing scopes on its
+        //! way to the new destination. This function applies the kill
+        //! sets of those enclosing scopes to `in_out` (those kill sets
+        //! concern items that are going out of scope).
+
+        let tcx = self.tcx();
+        let region_maps = tcx.region_maps;
+
+        debug!("pop_scopes(from_expr=%s, to_scope=%?, in_out=%s)",
+               from_expr.repr(tcx), to_scope.loop_id,
+               bits_to_str(reslice(in_out)));
+
+        let mut id = from_expr.id;
+        while id != to_scope.loop_id {
+            self.dfcx.apply_kill(id, in_out);
+
+            match region_maps.opt_encl_scope(id) {
+                Some(i) => { id = i; }
+                None => {
+                    tcx.sess.span_bug(
+                        from_expr.span,
+                        fmt!("pop_scopes(from_expr=%s, to_scope=%?) \
+                              to_scope does not enclose from_expr",
+                             from_expr.repr(tcx), to_scope.loop_id));
+                }
+            }
+        }
+    }
+
+    fn break_from_to(&mut self,
+                     from_expr: @ast::expr,
+                     to_scope: &mut LoopScope,
+                     in_out: &mut [uint]) {
+        self.pop_scopes(from_expr, to_scope, in_out);
+        self.dfcx.apply_kill(from_expr.id, in_out);
+        join_bits(&self.dfcx.oper, reslice(in_out), to_scope.break_bits);
+        debug!("break_from_to(from_expr=%s, to_scope=%?) final break_bits=%s",
+               from_expr.repr(self.tcx()),
+               to_scope.loop_id,
+               bits_to_str(reslice(in_out)));
+    }
+
+    fn walk_exprs(&mut self,
+                  exprs: &[@ast::expr],
+                  in_out: &mut [uint],
+                  loop_scopes: &mut ~[LoopScope]) {
+        for exprs.each |&expr| {
+            self.walk_expr(expr, in_out, loop_scopes);
+        }
+    }
+
+    fn walk_opt_expr(&mut self,
+                     opt_expr: Option<@ast::expr>,
+                     in_out: &mut [uint],
+                     loop_scopes: &mut ~[LoopScope]) {
+        for opt_expr.each |&expr| {
+            self.walk_expr(expr, in_out, loop_scopes);
+        }
+    }
+
+    fn walk_call(&mut self,
+                 _callee_id: ast::node_id,
+                 call_id: ast::node_id,
+                 arg0: @ast::expr,
+                 args: &[@ast::expr],
+                 in_out: &mut [uint],
+                 loop_scopes: &mut ~[LoopScope]) {
+        self.walk_expr(arg0, in_out, loop_scopes);
+        self.walk_exprs(args, in_out, loop_scopes);
+
+        // FIXME(#6268) nested method calls
+        // self.merge_with_entry_set(callee_id, in_out);
+        // self.dfcx.apply_gen_kill(callee_id, in_out);
+
+        let return_ty = ty::node_id_to_type(self.tcx(), call_id);
+        let fails = ty::type_is_bot(return_ty);
+        if fails {
+            self.reset(in_out);
+        }
+    }
+
+    fn walk_pat(&mut self,
+                pat: @ast::pat,
+                in_out: &mut [uint],
+                _loop_scopes: &mut ~[LoopScope]) {
+        debug!("DataFlowContext::walk_pat(pat=%s, in_out=%s)",
+               pat.repr(self.dfcx.tcx), bits_to_str(reslice(in_out)));
+
+        do ast_util::walk_pat(pat) |p| {
+            debug!("  p.id=%? in_out=%s", p.id, bits_to_str(reslice(in_out)));
+            self.merge_with_entry_set(p.id, in_out);
+            self.dfcx.apply_gen_kill(p.id, in_out);
+        }
+    }
+
+    fn walk_pat_alternatives(&mut self,
+                             pats: &[@ast::pat],
+                             in_out: &mut [uint],
+                             loop_scopes: &mut ~[LoopScope]) {
+        if pats.len() == 1 {
+            // Common special case:
+            return self.walk_pat(pats[0], in_out, loop_scopes);
+        }
+
+        // In the general case, the patterns in `pats` are
+        // alternatives, so we must treat this like an N-way select
+        // statement.
+        let initial_state = reslice(in_out).to_vec();
+        for pats.each |&pat| {
+            let mut temp = copy initial_state;
+            self.walk_pat(pat, temp, loop_scopes);
+            join_bits(&self.dfcx.oper, temp, in_out);
+        }
+    }
+
+    fn find_scope<'a>(&self,
+                      expr: @ast::expr,
+                      label: Option<ast::ident>,
+                      loop_scopes: &'a mut ~[LoopScope]) -> &'a mut LoopScope {
+        let index = match label {
+            None => {
+                let len = loop_scopes.len();
+                len - 1
+            }
+
+            Some(_) => {
+                match self.tcx().def_map.find(&expr.id) {
+                    Some(&ast::def_label(loop_id)) => {
+                        match loop_scopes.position(|l| l.loop_id == loop_id) {
+                            Some(i) => i,
+                            None => {
+                                self.tcx().sess.span_bug(
+                                    expr.span,
+                                    fmt!("No loop scope for id %?", loop_id));
+                            }
+                        }
+                    }
+
+                    r => {
+                        self.tcx().sess.span_bug(
+                            expr.span,
+                            fmt!("Bad entry `%?` in def_map for label", r));
+                    }
+                }
+            }
+        };
+
+        &mut loop_scopes[index]
+    }
+
+    fn is_method_call(&self, expr: @ast::expr) -> bool {
+        self.dfcx.method_map.contains_key(&expr.id)
+    }
+
+    fn reset(&mut self, bits: &mut [uint]) {
+        let e = if self.dfcx.oper.initial_value() {uint::max_value} else {0};
+        for vec::each_mut(bits) |b| { *b = e; }
+    }
+
+    fn add_to_entry_set(&mut self, id: ast::node_id, pred_bits: &[uint]) {
+        debug!("add_to_entry_set(id=%?, pred_bits=%s)",
+               id, bits_to_str(pred_bits));
+        let (start, end) = self.dfcx.compute_id_range(id);
+        let changed = { // FIXME(#5074) awkward construction
+            let on_entry = vec::mut_slice(self.dfcx.on_entry, start, end);
+            join_bits(&self.dfcx.oper, pred_bits, on_entry)
+        };
+        if changed {
+            debug!("changed entry set for %? to %s",
+                   id, bits_to_str(self.dfcx.on_entry.slice(start, end)));
+            self.changed = true;
+        }
+    }
+
+    fn merge_with_entry_set(&mut self,
+                            id: ast::node_id,
+                            pred_bits: &mut [uint]) {
+        debug!("merge_with_entry_set(id=%?, pred_bits=%s)",
+               id, mut_bits_to_str(pred_bits));
+        let (start, end) = self.dfcx.compute_id_range(id);
+        let changed = { // FIXME(#5074) awkward construction
+            let on_entry = vec::mut_slice(self.dfcx.on_entry, start, end);
+            let changed = join_bits(&self.dfcx.oper, reslice(pred_bits), on_entry);
+            copy_bits(reslice(on_entry), pred_bits);
+            changed
+        };
+        if changed {
+            debug!("changed entry set for %? to %s",
+                   id, bits_to_str(self.dfcx.on_entry.slice(start, end)));
+            self.changed = true;
+        }
+    }
+}
+
+fn mut_bits_to_str(words: &mut [uint]) -> ~str {
+    bits_to_str(reslice(words))
+}
+
+fn bits_to_str(words: &[uint]) -> ~str {
+    let mut result = ~"";
+    let mut sep = '[';
+
+    // Note: this is a little endian printout of bytes.
+
+    for words.each |&word| {
+        let mut v = word;
+        for uint::range(0, uint::bytes) |_| {
+            str::push_char(&mut result, sep);
+            str::push_str(&mut result, fmt!("%02x", v & 0xFF));
+            v >>= 8;
+            sep = '-';
+        }
+    }
+    str::push_char(&mut result, ']');
+    return result;
+}
+
+fn copy_bits(in_vec: &[uint], out_vec: &mut [uint]) -> bool {
+    bitwise(out_vec, in_vec, |_, b| b)
+}
+
+fn join_bits<O:DataFlowOperator>(oper: &O,
+                                 in_vec: &[uint],
+                                 out_vec: &mut [uint]) -> bool {
+    bitwise(out_vec, in_vec, |a, b| oper.join(a, b))
+}
+
+#[inline(always)]
+fn bitwise(out_vec: &mut [uint],
+           in_vec: &[uint],
+           op: &fn(uint, uint) -> uint) -> bool {
+    assert_eq!(out_vec.len(), in_vec.len());
+    let mut changed = false;
+    for uint::range(0, out_vec.len()) |i| {
+        let old_val = out_vec[i];
+        let new_val = op(old_val, in_vec[i]);
+        out_vec[i] = new_val;
+        changed |= (old_val != new_val);
+    }
+    return changed;
+}
+
+fn set_bit(words: &mut [uint], bit: uint) -> bool {
+    debug!("set_bit: words=%s bit=%s",
+           mut_bits_to_str(words), bit_str(bit));
+    let word = bit / uint::bits;
+    let bit_in_word = bit % uint::bits;
+    let bit_mask = 1 << bit_in_word;
+    debug!("word=%u bit_in_word=%u bit_mask=%u", word, bit_in_word, word);
+    let oldv = words[word];
+    let newv = oldv | bit_mask;
+    words[word] = newv;
+    oldv != newv
+}
+
+fn bit_str(bit: uint) -> ~str {
+    let byte = bit >> 8;
+    let lobits = 1 << (bit & 0xFF);
+    fmt!("[%u:%u-%02x]", bit, byte, lobits)
+}
+
+fn reslice<'a>(v: &'a mut [uint]) -> &'a [uint] {
+    // bFIXME(#5074) this function should not be necessary at all
+    unsafe {
+        cast::transmute(v)
+    }
+}
+
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index 6cdc96f7809a9..57c3e7c3b9a05 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -10,7 +10,6 @@
 
 use middle::freevars::freevar_entry;
 use middle::freevars;
-use middle::liveness;
 use middle::pat_util;
 use middle::ty;
 use middle::typeck;
@@ -56,19 +55,16 @@ pub static try_adding: &'static str = "Try adding a move";
 pub struct Context {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
-    last_use_map: liveness::last_use_map,
-    current_item: node_id,
+    current_item: node_id
 }
 
 pub fn check_crate(tcx: ty::ctxt,
                    method_map: typeck::method_map,
-                   last_use_map: liveness::last_use_map,
                    crate: @crate) {
     let ctx = Context {
         tcx: tcx,
         method_map: method_map,
-        last_use_map: last_use_map,
-        current_item: -1,
+        current_item: -1
     };
     let visit = visit::mk_vt(@visit::Visitor {
         visit_arm: check_arm,
@@ -132,7 +128,7 @@ fn check_item(item: @item, cx: Context, visitor: visit::vt<Context>) {
                             // Yes, it's a destructor.
                             match self_type.node {
                                 ty_path(_, path_node_id) => {
-                                    let struct_def = *cx.tcx.def_map.get(
+                                    let struct_def = cx.tcx.def_map.get_copy(
                                         &path_node_id);
                                     let struct_did =
                                         ast_util::def_id_of_def(struct_def);
@@ -261,11 +257,9 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt<Context>) {
         _ => e.id
     };
     for cx.tcx.node_type_substs.find(&type_parameter_id).each |ts| {
-        // FIXME(#5562): removing this copy causes a segfault before stage2
-        let ts = /*bad*/ copy **ts;
         let type_param_defs = match e.node {
           expr_path(_) => {
-            let did = ast_util::def_id_of_def(*cx.tcx.def_map.get(&e.id));
+            let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&e.id));
             ty::lookup_item_type(cx.tcx, did).generics.type_param_defs
           }
           _ => {
@@ -286,7 +280,7 @@ pub fn check_expr(e: @expr, cx: Context, v: visit::vt<Context>) {
                        ts.repr(cx.tcx),
                        type_param_defs.repr(cx.tcx)));
         }
-        for vec::each2(ts, *type_param_defs) |&ty, type_param_def| {
+        for vec::each2(**ts, *type_param_defs) |&ty, type_param_def| {
             check_bounds(cx, type_parameter_id, e.span, ty, type_param_def)
         }
     }
@@ -324,12 +318,10 @@ fn check_ty(aty: @Ty, cx: Context, v: visit::vt<Context>) {
     match aty.node {
       ty_path(_, id) => {
         for cx.tcx.node_type_substs.find(&id).each |ts| {
-            // FIXME(#5562): removing this copy causes a segfault before stage2
-            let ts = /*bad*/ copy **ts;
-            let did = ast_util::def_id_of_def(*cx.tcx.def_map.get(&id));
+            let did = ast_util::def_id_of_def(cx.tcx.def_map.get_copy(&id));
             let type_param_defs =
                 ty::lookup_item_type(cx.tcx, did).generics.type_param_defs;
-            for vec::each2(ts, *type_param_defs) |&ty, type_param_def| {
+            for vec::each2(**ts, *type_param_defs) |&ty, type_param_def| {
                 check_bounds(cx, aty.id, aty.span, ty, type_param_def)
             }
         }
@@ -392,7 +384,7 @@ pub fn check_bounds(cx: Context,
 fn is_nullary_variant(cx: Context, ex: @expr) -> bool {
     match ex.node {
       expr_path(_) => {
-        match *cx.tcx.def_map.get(&ex.id) {
+        match cx.tcx.def_map.get_copy(&ex.id) {
           def_variant(edid, vdid) => {
             vec::len(ty::enum_variant_with_id(cx.tcx, edid, vdid).args) == 0u
           }
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index f6d138a4a699e..001218ea0cf62 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -28,7 +28,6 @@ use syntax::ast_util::local_def;
 use syntax::visit::{default_simple_visitor, mk_simple_visitor, SimpleVisitor};
 use syntax::visit::visit_crate;
 
-use core::cast::transmute;
 use core::hashmap::HashMap;
 
 pub enum LangItem {
@@ -67,21 +66,24 @@ pub enum LangItem {
     MallocFnLangItem,           // 28
     FreeFnLangItem,             // 29
     BorrowAsImmFnLangItem,      // 30
-    ReturnToMutFnLangItem,      // 31
-    CheckNotBorrowedFnLangItem, // 32
-    StrDupUniqFnLangItem,       // 33
-
-    StartFnLangItem,            // 34
+    BorrowAsMutFnLangItem,      // 31
+    ReturnToMutFnLangItem,      // 32
+    CheckNotBorrowedFnLangItem, // 33
+    StrDupUniqFnLangItem,       // 34
+    RecordBorrowFnLangItem,     // 35
+    UnrecordBorrowFnLangItem,   // 36
+
+    StartFnLangItem,            // 37
 }
 
 pub struct LanguageItems {
-    items: [Option<def_id>, ..35]
+    items: [Option<def_id>, ..38]
 }
 
 pub impl LanguageItems {
     pub fn new() -> LanguageItems {
         LanguageItems {
-            items: [ None, ..35 ]
+            items: [ None, ..38 ]
         }
     }
 
@@ -129,11 +131,14 @@ pub impl LanguageItems {
             28 => "malloc",
             29 => "free",
             30 => "borrow_as_imm",
-            31 => "return_to_mut",
-            32 => "check_not_borrowed",
-            33 => "strdup_uniq",
+            31 => "borrow_as_mut",
+            32 => "return_to_mut",
+            33 => "check_not_borrowed",
+            34 => "strdup_uniq",
+            35 => "record_borrow",
+            36 => "unrecord_borrow",
 
-            34 => "start",
+            37 => "start",
 
             _ => "???"
         }
@@ -238,6 +243,9 @@ pub impl LanguageItems {
     pub fn borrow_as_imm_fn(&const self) -> def_id {
         self.items[BorrowAsImmFnLangItem as uint].get()
     }
+    pub fn borrow_as_mut_fn(&const self) -> def_id {
+        self.items[BorrowAsMutFnLangItem as uint].get()
+    }
     pub fn return_to_mut_fn(&const self) -> def_id {
         self.items[ReturnToMutFnLangItem as uint].get()
     }
@@ -247,15 +255,20 @@ pub impl LanguageItems {
     pub fn strdup_uniq_fn(&const self) -> def_id {
         self.items[StrDupUniqFnLangItem as uint].get()
     }
+    pub fn record_borrow_fn(&const self) -> def_id {
+        self.items[RecordBorrowFnLangItem as uint].get()
+    }
+    pub fn unrecord_borrow_fn(&const self) -> def_id {
+        self.items[UnrecordBorrowFnLangItem as uint].get()
+    }
     pub fn start_fn(&const self) -> def_id {
         self.items[StartFnLangItem as uint].get()
     }
 }
 
-fn LanguageItemCollector<'r>(crate: @crate,
-                             session: Session,
-                             items: &'r mut LanguageItems)
-                          -> LanguageItemCollector<'r> {
+fn LanguageItemCollector(crate: @crate,
+                         session: Session)
+                      -> LanguageItemCollector {
     let mut item_refs = HashMap::new();
 
     item_refs.insert(@~"const", ConstTraitLangItem as uint);
@@ -294,22 +307,25 @@ fn LanguageItemCollector<'r>(crate: @crate,
     item_refs.insert(@~"malloc", MallocFnLangItem as uint);
     item_refs.insert(@~"free", FreeFnLangItem as uint);
     item_refs.insert(@~"borrow_as_imm", BorrowAsImmFnLangItem as uint);
+    item_refs.insert(@~"borrow_as_mut", BorrowAsMutFnLangItem as uint);
     item_refs.insert(@~"return_to_mut", ReturnToMutFnLangItem as uint);
     item_refs.insert(@~"check_not_borrowed",
                      CheckNotBorrowedFnLangItem as uint);
     item_refs.insert(@~"strdup_uniq", StrDupUniqFnLangItem as uint);
+    item_refs.insert(@~"record_borrow", RecordBorrowFnLangItem as uint);
+    item_refs.insert(@~"unrecord_borrow", UnrecordBorrowFnLangItem as uint);
     item_refs.insert(@~"start", StartFnLangItem as uint);
 
     LanguageItemCollector {
         crate: crate,
         session: session,
-        items: items,
+        items: LanguageItems::new(),
         item_refs: item_refs
     }
 }
 
-struct LanguageItemCollector<'self> {
-    items: &'self mut LanguageItems,
+struct LanguageItemCollector {
+    items: LanguageItems,
 
     crate: @crate,
     session: Session,
@@ -317,8 +333,8 @@ struct LanguageItemCollector<'self> {
     item_refs: HashMap<@~str, uint>,
 }
 
-pub impl<'self> LanguageItemCollector<'self> {
-    fn match_and_collect_meta_item(&self, item_def_id: def_id,
+pub impl LanguageItemCollector {
+    fn match_and_collect_meta_item(&mut self, item_def_id: def_id,
                                    meta_item: @meta_item) {
         match meta_item.node {
             meta_name_value(key, literal) => {
@@ -333,7 +349,7 @@ pub impl<'self> LanguageItemCollector<'self> {
         }
     }
 
-    fn collect_item(&self, item_index: uint, item_def_id: def_id) {
+    fn collect_item(&mut self, item_index: uint, item_def_id: def_id) {
         // Check for duplicates.
         match self.items.items[item_index] {
             Some(original_def_id) if original_def_id != item_def_id => {
@@ -349,42 +365,45 @@ pub impl<'self> LanguageItemCollector<'self> {
         self.items.items[item_index] = Some(item_def_id);
     }
 
-    fn match_and_collect_item(&self,
+    fn match_and_collect_item(&mut self,
                               item_def_id: def_id, key: @~str, value: @~str) {
         if *key != ~"lang" {
             return;    // Didn't match.
         }
 
-        match self.item_refs.find(&value) {
+        let item_index = self.item_refs.find(&value).map(|x| **x);
+        // prevent borrow checker from considering   ^~~~~~~~~~~
+        // self to be borrowed (annoying)
+
+        match item_index {
+            Some(item_index) => {
+                self.collect_item(item_index, item_def_id);
+            }
             None => {
                 // Didn't match.
-            }
-            Some(&item_index) => {
-                self.collect_item(item_index, item_def_id)
+                return;
             }
         }
     }
 
-    fn collect_local_language_items(&self) {
-        unsafe {
-            let this: *LanguageItemCollector<'self> = transmute(self);
-            visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor {
-                visit_item: |item| {
-                    for item.attrs.each |attribute| {
-                        unsafe {
-                            (*this).match_and_collect_meta_item(
-                                local_def(item.id),
-                                attribute.node.value
-                            );
-                        }
+    fn collect_local_language_items(&mut self) {
+        let this: *mut LanguageItemCollector = &mut *self;
+        visit_crate(self.crate, (), mk_simple_visitor(@SimpleVisitor {
+            visit_item: |item| {
+                for item.attrs.each |attribute| {
+                    unsafe {
+                        (*this).match_and_collect_meta_item(
+                            local_def(item.id),
+                            attribute.node.value
+                        );
                     }
-                },
-                .. *default_simple_visitor()
-            }));
-        }
+                }
+            },
+            .. *default_simple_visitor()
+        }));
     }
 
-    fn collect_external_language_items(&self) {
+    fn collect_external_language_items(&mut self) {
         let crate_store = self.session.cstore;
         do iter_crate_data(crate_store) |crate_number, _crate_metadata| {
             for each_lang_item(crate_store, crate_number)
@@ -408,7 +427,7 @@ pub impl<'self> LanguageItemCollector<'self> {
         }
     }
 
-    fn collect(&self) {
+    fn collect(&mut self) {
         self.collect_local_language_items();
         self.collect_external_language_items();
         self.check_completeness();
@@ -418,8 +437,8 @@ pub impl<'self> LanguageItemCollector<'self> {
 pub fn collect_language_items(crate: @crate,
                               session: Session)
                            -> LanguageItems {
-    let mut items = LanguageItems::new();
-    let collector = LanguageItemCollector(crate, session, &mut items);
+    let mut collector = LanguageItemCollector(crate, session);
     collector.collect();
-    copy items
+    let LanguageItemCollector { items, _ } = collector;
+    items
 }
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 265d6b0c7677c..b0d6d477c0c87 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -667,7 +667,7 @@ fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
         for vec::each(vec::append_one(tys, decl.output)) |ty| {
             match ty.node {
               ast::ty_path(_, id) => {
-                match *cx.def_map.get(&id) {
+                match cx.def_map.get_copy(&id) {
                   ast::def_prim_ty(ast::ty_int(ast::ty_i)) => {
                     cx.sess.span_lint(
                         ctypes, id, fn_id,
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 40d62cac3572d..0bd73a15d507c 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -112,7 +112,6 @@ use util::ppaux::ty_to_str;
 
 use core::cast::transmute;
 use core::hashmap::HashMap;
-use core::util::with;
 use syntax::ast::*;
 use syntax::codemap::span;
 use syntax::parse::token::special_idents;
@@ -121,16 +120,6 @@ use syntax::visit::{fk_anon, fk_fn_block, fk_item_fn, fk_method};
 use syntax::visit::{vt};
 use syntax::{visit, ast_util};
 
-// Maps from an expr id to a list of variable ids for which this expr
-// is the last use.  Typically, the expr is a path and the node id is
-// the local/argument/etc that the path refers to.  However, it also
-// possible for the expr to be a closure, in which case the list is a
-// list of closed over variables that can be moved into the closure.
-//
-// Very subtle (#2633): borrowck will remove entries from this table
-// if it detects an outstanding loan (that is, the addr is taken).
-pub type last_use_map = @mut HashMap<node_id, @mut ~[node_id]>;
-
 #[deriving(Eq)]
 struct Variable(uint);
 #[deriving(Eq)]
@@ -158,7 +147,7 @@ pub fn check_crate(tcx: ty::ctxt,
                    method_map: typeck::method_map,
                    variable_moves_map: moves::VariableMovesMap,
                    capture_map: moves::CaptureMap,
-                   crate: @crate) -> last_use_map {
+                   crate: @crate) {
     let visitor = visit::mk_vt(@visit::Visitor {
         visit_fn: visit_fn,
         visit_local: visit_local,
@@ -168,16 +157,13 @@ pub fn check_crate(tcx: ty::ctxt,
         .. *visit::default_visitor()
     });
 
-    let last_use_map = @mut HashMap::new();
     let initial_maps = @mut IrMaps(tcx,
                                    method_map,
                                    variable_moves_map,
                                    capture_map,
-                                   last_use_map,
                                    0);
     visit::visit_crate(crate, initial_maps, visitor);
     tcx.sess.abort_if_errors();
-    return last_use_map;
 }
 
 impl to_str::ToStr for LiveNode {
@@ -241,23 +227,11 @@ enum VarKind {
     ImplicitRet
 }
 
-fn relevant_def(def: def) -> Option<node_id> {
-    match def {
-        def_binding(nid, _) |
-        def_arg(nid, _) |
-        def_local(nid, _) |
-        def_self(nid, _) => Some(nid),
-
-        _ => None
-    }
-}
-
 struct IrMaps {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     variable_moves_map: moves::VariableMovesMap,
     capture_map: moves::CaptureMap,
-    last_use_map: last_use_map,
 
     num_live_nodes: uint,
     num_vars: uint,
@@ -274,7 +248,6 @@ fn IrMaps(tcx: ty::ctxt,
           method_map: typeck::method_map,
           variable_moves_map: moves::VariableMovesMap,
           capture_map: moves::CaptureMap,
-          last_use_map: last_use_map,
           cur_item: node_id)
        -> IrMaps {
     IrMaps {
@@ -282,7 +255,6 @@ fn IrMaps(tcx: ty::ctxt,
         method_map: method_map,
         variable_moves_map: variable_moves_map,
         capture_map: capture_map,
-        last_use_map: last_use_map,
         num_live_nodes: 0,
         num_vars: 0,
         live_node_map: HashMap::new(),
@@ -367,35 +339,13 @@ pub impl IrMaps {
     fn lnk(&mut self, ln: LiveNode) -> LiveNodeKind {
         self.lnks[*ln]
     }
-
-    fn add_last_use(&mut self, expr_id: node_id, var: Variable) {
-        let vk = self.var_kinds[*var];
-        debug!("Node %d is a last use of variable %?", expr_id, vk);
-        match vk {
-            Arg(id, _) |
-            Local(LocalInfo { id: id, kind: FromLetNoInitializer, _ }) |
-            Local(LocalInfo { id: id, kind: FromLetWithInitializer, _ }) |
-            Local(LocalInfo { id: id, kind: FromMatch(_), _ }) => {
-                let v = match self.last_use_map.find(&expr_id) {
-                    Some(&v) => v,
-                    None => {
-                        let v = @mut ~[];
-                        self.last_use_map.insert(expr_id, v);
-                        v
-                    }
-                };
-
-                v.push(id);
-            }
-            ImplicitRet => debug!("--but it is not owned"),
-        }
-    }
 }
 
 fn visit_item(item: @item, self: @mut IrMaps, v: vt<@mut IrMaps>) {
-    do with(&mut self.cur_item, item.id) {
-        visit::visit_item(item, self, v)
-    }
+    let old_cur_item = self.cur_item;
+    self.cur_item = item.id;
+    visit::visit_item(item, self, v);
+    self.cur_item = old_cur_item;
 }
 
 fn visit_fn(fk: &visit::fn_kind,
@@ -413,7 +363,6 @@ fn visit_fn(fk: &visit::fn_kind,
                               self.method_map,
                               self.variable_moves_map,
                               self.capture_map,
-                              self.last_use_map,
                               self.cur_item);
 
     unsafe {
@@ -517,9 +466,9 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) {
     match expr.node {
       // live nodes required for uses or definitions of variables:
       expr_path(_) => {
-        let def = *self.tcx.def_map.get(&expr.id);
+        let def = self.tcx.def_map.get_copy(&expr.id);
         debug!("expr %d: path that leads to %?", expr.id, def);
-        if relevant_def(def).is_some() {
+        if moves::moved_variable_node_id_from_def(def).is_some() {
             self.add_live_node_for_node(expr.id, ExprNode(expr.span));
         }
         visit::visit_expr(expr, self, vt);
@@ -536,7 +485,7 @@ fn visit_expr(expr: @expr, self: @mut IrMaps, vt: vt<@mut IrMaps>) {
         let cvs = self.capture_map.get(&expr.id);
         let mut call_caps = ~[];
         for cvs.each |cv| {
-            match relevant_def(cv.def) {
+            match moves::moved_variable_node_id_from_def(cv.def) {
               Some(rv) => {
                 let cv_ln = self.add_live_node(FreeVarNode(cv.span));
                 let is_move = match cv.mode {
@@ -664,8 +613,8 @@ pub impl Liveness {
     fn variable_from_path(&self, expr: @expr) -> Option<Variable> {
         match expr.node {
           expr_path(_) => {
-            let def = *self.tcx.def_map.get(&expr.id);
-            relevant_def(def).map(
+            let def = self.tcx.def_map.get_copy(&expr.id);
+            moves::moved_variable_node_id_from_def(def).map(
                 |rdef| self.variable(*rdef, expr.span)
             )
           }
@@ -681,7 +630,7 @@ pub impl Liveness {
                              span: span) -> Option<Variable> {
         match self.tcx.def_map.find(&node_id) {
           Some(&def) => {
-            relevant_def(def).map(
+            moves::moved_variable_node_id_from_def(def).map(
                 |rdef| self.variable(*rdef, span)
             )
           }
@@ -810,11 +759,13 @@ pub impl Liveness {
             None => {
                 // Vanilla 'break' or 'loop', so use the enclosing
                 // loop scope
-                let loop_scope = &mut *self.loop_scope;
-                if loop_scope.len() == 0 {
-                    self.tcx.sess.span_bug(sp, "break outside loop");
-                }
-                else {
+                let len = { // FIXME(#5074) stage0
+                    let loop_scope = &mut *self.loop_scope;
+                    loop_scope.len()
+                };
+                if len == 0 {
+                    self.tcx.sess.span_bug(sp, ~"break outside loop");
+                } else {
                     // FIXME(#5275): this shouldn't have to be a method...
                     self.last_loop_scope()
                 }
@@ -1384,8 +1335,8 @@ pub impl Liveness {
 
     fn access_path(&self, expr: @expr, succ: LiveNode, acc: uint)
                   -> LiveNode {
-        let def = *self.tcx.def_map.get(&expr.id);
-        match relevant_def(def) {
+        let def = self.tcx.def_map.get_copy(&expr.id);
+        match moves::moved_variable_node_id_from_def(def) {
           Some(nid) => {
             let ln = self.live_node(expr.id, expr.span);
             if acc != 0u {
@@ -1518,7 +1469,6 @@ fn check_expr(expr: @expr, self: @Liveness, vt: vt<@Liveness>) {
       expr_path(_) => {
         for self.variable_from_def_map(expr.id, expr.span).each |var| {
             let ln = self.live_node(expr.id, expr.span);
-            self.consider_last_use(expr, ln, *var);
 
             match self.ir.variable_moves_map.find(&expr.id) {
                 None => {}
@@ -1537,7 +1487,6 @@ fn check_expr(expr: @expr, self: @Liveness, vt: vt<@Liveness>) {
         let caps = self.ir.captures(expr);
         for caps.each |cap| {
             let var = self.variable(cap.var_nid, expr.span);
-            self.consider_last_use(expr, cap.ln, var);
             if cap.is_move {
                 self.check_move_from_var(cap.ln, var, expr);
             }
@@ -1606,7 +1555,7 @@ enum ReadKind {
 }
 
 pub impl Liveness {
-    fn check_ret(@self, id: node_id, sp: span, _fk: &visit::fn_kind,
+    fn check_ret(&self, id: node_id, sp: span, _fk: &visit::fn_kind,
                  entry_ln: LiveNode) {
         if self.live_on_entry(entry_ln, self.s.no_ret_var).is_some() {
             // if no_ret_var is live, then we fall off the end of the
@@ -1626,11 +1575,11 @@ pub impl Liveness {
         }
     }
 
-    fn check_move_from_var(@self, ln: LiveNode,
+    fn check_move_from_var(&self,
+                           ln: LiveNode,
                            var: Variable,
                            move_expr: @expr) {
         /*!
-         *
          * Checks whether `var` is live on entry to any of the
          * successors of `ln`.  If it is, report an error.
          * `move_expr` is the expression which caused the variable
@@ -1650,20 +1599,10 @@ pub impl Liveness {
         }
     }
 
-    fn consider_last_use(@self, expr: @expr, ln: LiveNode, var: Variable) {
-        debug!("consider_last_use(expr.id=%?, ln=%s, var=%s)",
-               expr.id, ln.to_str(), var.to_str());
-
-        match self.live_on_exit(ln, var) {
-          Some(_) => {}
-          None => self.ir.add_last_use(expr.id, var)
-       }
-    }
-
     fn check_lvalue(@self, expr: @expr, vt: vt<@Liveness>) {
         match expr.node {
           expr_path(_) => {
-            match *self.tcx.def_map.get(&expr.id) {
+            match self.tcx.def_map.get_copy(&expr.id) {
               def_local(nid, mutbl) => {
                 // Assignment to an immutable variable or argument: only legal
                 // if there is no later assignment. If this local is actually
@@ -1676,7 +1615,7 @@ pub impl Liveness {
                 self.warn_about_dead_assign(expr.span, expr.id, ln, var);
               }
               def => {
-                match relevant_def(def) {
+                match moves::moved_variable_node_id_from_def(def) {
                   Some(nid) => {
                     let ln = self.live_node(expr.id, expr.span);
                     let var = self.variable(nid, expr.span);
@@ -1696,14 +1635,14 @@ pub impl Liveness {
        }
     }
 
-    fn check_for_reassignments_in_pat(@self, pat: @pat, mutbl: bool) {
+    fn check_for_reassignments_in_pat(&self, pat: @pat, mutbl: bool) {
         do self.pat_bindings(pat) |ln, var, sp, id| {
             self.check_for_reassignment(ln, var, sp,
                                         if mutbl {Some(id)} else {None});
         }
     }
 
-    fn check_for_reassignment(@self, ln: LiveNode, var: Variable,
+    fn check_for_reassignment(&self, ln: LiveNode, var: Variable,
                               orig_span: span, mutbl: Option<node_id>) {
         match self.assigned_on_exit(ln, var) {
           Some(ExprNode(span)) => {
@@ -1728,7 +1667,7 @@ pub impl Liveness {
         }
     }
 
-    fn report_illegal_move(@self, lnk: LiveNodeKind,
+    fn report_illegal_move(&self, lnk: LiveNodeKind,
                            var: Variable,
                            move_expr: @expr) {
         // the only time that it is possible to have a moved variable
@@ -1793,7 +1732,8 @@ pub impl Liveness {
         };
     }
 
-    fn report_move_location(@self, move_expr: @expr,
+    fn report_move_location(&self,
+                            move_expr: @expr,
                             var: Variable,
                             expr_descr: &str,
                             pronoun: &str) {
@@ -1807,7 +1747,8 @@ pub impl Liveness {
                  ty_to_str(self.tcx, move_expr_ty)));
     }
 
-    fn report_illegal_read(@self, chk_span: span,
+    fn report_illegal_read(&self,
+                           chk_span: span,
                            lnk: LiveNodeKind,
                            var: Variable,
                            rk: ReadKind) {
@@ -1838,12 +1779,12 @@ pub impl Liveness {
         }
     }
 
-    fn should_warn(@self, var: Variable) -> Option<@~str> {
+    fn should_warn(&self, var: Variable) -> Option<@~str> {
         let name = self.ir.variable_name(var);
         if name[0] == ('_' as u8) { None } else { Some(name) }
     }
 
-    fn warn_about_unused_args(@self, decl: &fn_decl, entry_ln: LiveNode) {
+    fn warn_about_unused_args(&self, decl: &fn_decl, entry_ln: LiveNode) {
         for decl.inputs.each |arg| {
             do pat_util::pat_bindings(self.tcx.def_map, arg.pat)
                     |_bm, p_id, sp, _n| {
@@ -1853,7 +1794,7 @@ pub impl Liveness {
         }
     }
 
-    fn warn_about_unused_or_dead_vars_in_pat(@self, pat: @pat) {
+    fn warn_about_unused_or_dead_vars_in_pat(&self, pat: @pat) {
         do self.pat_bindings(pat) |ln, var, sp, id| {
             if !self.warn_about_unused(sp, id, ln, var) {
                 self.warn_about_dead_assign(sp, id, ln, var);
@@ -1861,7 +1802,7 @@ pub impl Liveness {
         }
     }
 
-    fn warn_about_unused(@self, sp: span, id: node_id,
+    fn warn_about_unused(&self, sp: span, id: node_id,
                          ln: LiveNode, var: Variable) -> bool {
         if !self.used_on_entry(ln, var) {
             for self.should_warn(var).each |name| {
@@ -1891,7 +1832,7 @@ pub impl Liveness {
         return false;
     }
 
-    fn warn_about_dead_assign(@self, sp: span, id: node_id,
+    fn warn_about_dead_assign(&self, sp: span, id: node_id,
                               ln: LiveNode, var: Variable) {
         if self.live_on_exit(ln, var).is_none() {
             for self.should_warn(var).each |name| {
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 29ca875806f93..dde4c04479288 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -48,7 +48,7 @@
 
 use middle::ty;
 use middle::typeck;
-use util::ppaux::{ty_to_str, region_to_str};
+use util::ppaux::{ty_to_str, region_to_str, Repr};
 use util::common::indenter;
 
 use syntax::ast::{m_imm, m_const, m_mutbl};
@@ -58,48 +58,46 @@ use syntax::print::pprust;
 
 #[deriving(Eq)]
 pub enum categorization {
-    cat_rvalue,                     // result of eval'ing some misc expr
-    cat_special(special_kind),      //
-    cat_local(ast::node_id),        // local variable
-    cat_binding(ast::node_id),      // pattern binding
-    cat_arg(ast::node_id),          // formal argument
-    cat_stack_upvar(cmt),           // upvar in stack closure
-    cat_deref(cmt, uint, ptr_kind), // deref of a ptr
-    cat_comp(cmt, comp_kind),       // adjust to locate an internal component
-    cat_discr(cmt, ast::node_id),   // match discriminant (see preserve())
-    cat_self(ast::node_id),         // explicit `self`
+    cat_rvalue,                        // result of eval'ing some misc expr
+    cat_static_item,
+    cat_implicit_self,
+    cat_copied_upvar(CopiedUpvar),     // upvar copied into @fn or ~fn env
+    cat_stack_upvar(cmt),              // by ref upvar from &fn
+    cat_local(ast::node_id),           // local variable
+    cat_arg(ast::node_id),             // formal argument
+    cat_deref(cmt, uint, ptr_kind),    // deref of a ptr
+    cat_interior(cmt, interior_kind),          // something interior
+    cat_discr(cmt, ast::node_id),      // match discriminant (see preserve())
+    cat_self(ast::node_id),            // explicit `self`
+}
+
+#[deriving(Eq)]
+struct CopiedUpvar {
+    upvar_id: ast::node_id,
+    onceness: ast::Onceness,
 }
 
 // different kinds of pointers:
 #[deriving(Eq)]
 pub enum ptr_kind {
-    uniq_ptr,
+    uniq_ptr(ast::mutability),
     gc_ptr(ast::mutability),
     region_ptr(ast::mutability, ty::Region),
     unsafe_ptr
 }
 
-// I am coining the term "components" to mean "pieces of a data
-// structure accessible without a dereference":
+// We use the term "interior" to mean "something reachable from the
+// base without a pointer dereference", e.g. a field
 #[deriving(Eq)]
-pub enum comp_kind {
-    comp_tuple,                  // elt in a tuple
-    comp_anon_field,             // anonymous field (in e.g.
-                                 // struct Foo(int, int);
-    comp_variant(ast::def_id),   // internals to a variant of given enum
-    comp_field(ast::ident,       // name of field
-               ast::mutability), // declared mutability of field
-    comp_index(ty::t,            // type of vec/str/etc being deref'd
-               ast::mutability)  // mutability of vec content
-}
-
-// different kinds of expressions we might evaluate
-#[deriving(Eq)]
-pub enum special_kind {
-    sk_method,
-    sk_static_item,
-    sk_implicit_self,   // old by-reference `self`
-    sk_heap_upvar
+pub enum interior_kind {
+    interior_tuple,                  // elt in a tuple
+    interior_anon_field,             // anonymous field (in e.g.
+                                     // struct Foo(int, int);
+    interior_variant(ast::def_id),   // internals to a variant of given enum
+    interior_field(ast::ident,       // name of field
+                   ast::mutability), // declared mutability of field
+    interior_index(ty::t,            // type of vec/str/etc being deref'd
+                   ast::mutability)  // mutability of vec content
 }
 
 #[deriving(Eq)]
@@ -110,49 +108,48 @@ pub enum MutabilityCategory {
     McInherited  // Inherited from the fact that owner is mutable.
 }
 
+// `cmt`: "Category, Mutability, and Type".
+//
 // a complete categorization of a value indicating where it originated
 // and how it is located, as well as the mutability of the memory in
 // which the value is stored.
 //
-// note: cmt stands for "categorized mutable type".
+// *WARNING* The field `cmt.type` is NOT necessarily the same as the
+// result of `node_id_to_type(cmt.id)`. This is because the `id` is
+// always the `id` of the node producing the type; in an expression
+// like `*x`, the type of this deref node is the deref'd type (`T`),
+// but in a pattern like `@x`, the `@x` pattern is again a
+// dereference, but its type is the type *before* the dereference
+// (`@T`). So use `cmt.type` to find the type of the value in a consistent
+// fashion. For more details, see the method `cat_pattern`
 #[deriving(Eq)]
 pub struct cmt_ {
     id: ast::node_id,          // id of expr/pat producing this value
     span: span,                // span of same expr/pat
     cat: categorization,       // categorization of expr
-    lp: Option<@loan_path>,    // loan path for expr, if any
     mutbl: MutabilityCategory, // mutability of expr as lvalue
-    ty: ty::t                  // type of the expr
+    ty: ty::t                  // type of the expr (*see WARNING above*)
 }
 
 pub type cmt = @cmt_;
 
-// a loan path is like a category, but it exists only when the data is
-// interior to the stack frame.  loan paths are used as the key to a
-// map indicating what is borrowed at any point in time.
-#[deriving(Eq)]
-pub enum loan_path {
-    lp_local(ast::node_id),
-    lp_arg(ast::node_id),
-    lp_self,
-    lp_deref(@loan_path, ptr_kind),
-    lp_comp(@loan_path, comp_kind)
-}
-
 // We pun on *T to mean both actual deref of a ptr as well
 // as accessing of components:
-pub enum deref_kind {deref_ptr(ptr_kind), deref_comp(comp_kind)}
+pub enum deref_kind {deref_ptr(ptr_kind), deref_interior(interior_kind)}
 
 // Categorizes a derefable type.  Note that we include vectors and strings as
 // derefable (we model an index as the combination of a deref and then a
 // pointer adjustment).
 pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
     match ty::get(t).sty {
-        ty::ty_uniq(*) |
+        ty::ty_uniq(mt) => {
+            Some(deref_ptr(uniq_ptr(mt.mutbl)))
+        }
+
         ty::ty_evec(_, ty::vstore_uniq) |
         ty::ty_estr(ty::vstore_uniq) |
         ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, _}) => {
-            Some(deref_ptr(uniq_ptr))
+            Some(deref_ptr(uniq_ptr(m_imm)))
         }
 
         ty::ty_rptr(r, mt) |
@@ -181,19 +178,19 @@ pub fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
         }
 
         ty::ty_enum(did, _) => {
-            Some(deref_comp(comp_variant(did)))
+            Some(deref_interior(interior_variant(did)))
         }
 
         ty::ty_struct(_, _) => {
-            Some(deref_comp(comp_anon_field))
+            Some(deref_interior(interior_anon_field))
         }
 
         ty::ty_evec(mt, ty::vstore_fixed(_)) => {
-            Some(deref_comp(comp_index(t, mt.mutbl)))
+            Some(deref_interior(interior_index(t, mt.mutbl)))
         }
 
         ty::ty_estr(ty::vstore_fixed(_)) => {
-            Some(deref_comp(comp_index(t, m_imm)))
+            Some(deref_interior(interior_index(t, m_imm)))
         }
 
         _ => None
@@ -257,19 +254,6 @@ pub fn cat_def(
     return mcx.cat_def(expr_id, expr_span, expr_ty, def);
 }
 
-pub fn cat_variant<N:ast_node>(
-    tcx: ty::ctxt,
-    method_map: typeck::method_map,
-    arg: N,
-    enum_did: ast::def_id,
-    cmt: cmt) -> cmt {
-
-    let mcx = &mem_categorization_ctxt {
-        tcx: tcx, method_map: method_map
-    };
-    return mcx.cat_variant(arg, enum_did, cmt);
-}
-
 pub trait ast_node {
     fn id(&self) -> ast::node_id;
     fn span(&self) -> span;
@@ -285,16 +269,6 @@ impl ast_node for @ast::pat {
     fn span(&self) -> span { self.span }
 }
 
-pub trait get_type_for_node {
-    fn ty<N:ast_node>(&self, node: N) -> ty::t;
-}
-
-impl get_type_for_node for ty::ctxt {
-    fn ty<N:ast_node>(&self, node: N) -> ty::t {
-        ty::node_id_to_type(*self, node.id())
-    }
-}
-
 pub struct mem_categorization_ctxt {
     tcx: ty::ctxt,
     method_map: typeck::method_map,
@@ -338,26 +312,24 @@ pub impl MutabilityCategory {
         }
     }
 
-    fn to_user_str(&self) -> ~str {
+    fn to_user_str(&self) -> &'static str {
         match *self {
-            McDeclared | McInherited => ~"mutable",
-            McImmutable => ~"immutable",
-            McReadOnly => ~"const"
+            McDeclared | McInherited => "mutable",
+            McImmutable => "immutable",
+            McReadOnly => "const"
         }
     }
 }
 
-pub impl loan_path {
-    fn node_id(&self) -> Option<ast::node_id> {
-        match *self {
-            lp_local(id) | lp_arg(id) => Some(id),
-            lp_deref(lp, _) | lp_comp(lp, _) => lp.node_id(),
-            lp_self => None
-        }
+pub impl mem_categorization_ctxt {
+    fn expr_ty(&self, expr: @ast::expr) -> ty::t {
+        ty::expr_ty(self.tcx, expr)
+    }
+
+    fn pat_ty(&self, pat: @ast::pat) -> ty::t {
+        ty::node_id_to_type(self.tcx, pat.id)
     }
-}
 
-pub impl mem_categorization_ctxt {
     fn cat_expr(&self, expr: @ast::expr) -> cmt {
         match self.tcx.adjustments.find(&expr.id) {
             None => {
@@ -406,8 +378,7 @@ pub impl mem_categorization_ctxt {
         debug!("cat_expr: id=%d expr=%s",
                expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()));
 
-        let tcx = self.tcx;
-        let expr_ty = tcx.ty(expr);
+        let expr_ty = self.expr_ty(expr);
         match expr.node {
           ast::expr_unary(ast::deref, e_base) => {
             if self.method_map.contains_key(&expr.id) {
@@ -419,12 +390,13 @@ pub impl mem_categorization_ctxt {
           }
 
           ast::expr_field(base, f_name, _) => {
-            if self.method_map.contains_key(&expr.id) {
-                return self.cat_method_ref(expr, expr_ty);
-            }
+            // Method calls are now a special syntactic form,
+            // so `a.b` should always be a field.
+            assert!(!self.method_map.contains_key(&expr.id));
 
             let base_cmt = self.cat_expr(base);
-            self.cat_field(expr, base_cmt, f_name, expr.id)
+            self.cat_field(expr, base_cmt, f_name,
+                           self.expr_ty(expr), expr.id)
           }
 
           ast::expr_index(base, _) => {
@@ -437,7 +409,7 @@ pub impl mem_categorization_ctxt {
           }
 
           ast::expr_path(_) => {
-            let def = *self.tcx.def_map.get(&expr.id);
+            let def = self.tcx.def_map.get_copy(&expr.id);
             self.cat_def(expr.id, expr.span, expr_ty, def)
           }
 
@@ -475,8 +447,7 @@ pub impl mem_categorization_ctxt {
             @cmt_ {
                 id:id,
                 span:span,
-                cat:cat_special(sk_static_item),
-                lp:None,
+                cat:cat_static_item,
                 mutbl: McImmutable,
                 ty:expr_ty
             }
@@ -487,66 +458,70 @@ pub impl mem_categorization_ctxt {
             // stuff as `&const` and `&mut`?
 
             // m: mutability of the argument
-            // lp: loan path, must be none for aliasable things
             let m = if mutbl {McDeclared} else {McImmutable};
-            let lp = Some(@lp_arg(vid));
             @cmt_ {
-                id:id,
-                span:span,
-                cat:cat_arg(vid),
-                lp:lp,
+                id: id,
+                span: span,
+                cat: cat_arg(vid),
                 mutbl: m,
                 ty:expr_ty
             }
           }
 
           ast::def_self(self_id, is_implicit) => {
-            let cat, loan_path;
-            if is_implicit {
-                cat = cat_special(sk_implicit_self);
-                loan_path = None;
+            let cat = if is_implicit {
+                cat_implicit_self
             } else {
-                cat = cat_self(self_id);
-                loan_path = Some(@lp_self);
+                cat_self(self_id)
             };
 
             @cmt_ {
                 id:id,
                 span:span,
                 cat:cat,
-                lp:loan_path,
                 mutbl: McImmutable,
                 ty:expr_ty
             }
           }
 
-          ast::def_upvar(_, inner, fn_node_id, _) => {
-            let ty = ty::node_id_to_type(self.tcx, fn_node_id);
-            let sigil = ty::ty_closure_sigil(ty);
-            match sigil {
-                ast::BorrowedSigil => {
-                    let upcmt = self.cat_def(id, span, expr_ty, *inner);
-                    @cmt_ {
-                        id:id,
-                        span:span,
-                        cat:cat_stack_upvar(upcmt),
-                        lp:upcmt.lp,
-                        mutbl:upcmt.mutbl,
-                        ty:upcmt.ty
-                    }
-                }
-                ast::OwnedSigil | ast::ManagedSigil => {
-                    // FIXME #2152 allow mutation of moved upvars
-                    @cmt_ {
-                        id:id,
-                        span:span,
-                        cat:cat_special(sk_heap_upvar),
-                        lp:None,
-                        mutbl:McImmutable,
-                        ty:expr_ty
-                    }
-                }
-            }
+          ast::def_upvar(upvar_id, inner, fn_node_id, _) => {
+              let ty = ty::node_id_to_type(self.tcx, fn_node_id);
+              match ty::get(ty).sty {
+                  ty::ty_closure(ref closure_ty) => {
+                      let sigil = closure_ty.sigil;
+                      match sigil {
+                          ast::BorrowedSigil => {
+                              let upvar_cmt =
+                                  self.cat_def(id, span, expr_ty, *inner);
+                              @cmt_ {
+                                  id:id,
+                                  span:span,
+                                  cat:cat_stack_upvar(upvar_cmt),
+                                  mutbl:upvar_cmt.mutbl.inherit(),
+                                  ty:upvar_cmt.ty
+                              }
+                          }
+                          ast::OwnedSigil | ast::ManagedSigil => {
+                              // FIXME #2152 allow mutation of moved upvars
+                              @cmt_ {
+                                  id:id,
+                                  span:span,
+                                  cat:cat_copied_upvar(CopiedUpvar {
+                                      upvar_id: upvar_id,
+                                      onceness: closure_ty.onceness}),
+                                  mutbl:McImmutable,
+                                  ty:expr_ty
+                              }
+                          }
+                      }
+                  }
+                  _ => {
+                      self.tcx.sess.span_bug(
+                          span,
+                          fmt!("Upvar of non-closure %? - %s",
+                               fn_node_id, ty.repr(self.tcx)));
+                  }
+              }
           }
 
           ast::def_local(vid, mutbl) => {
@@ -555,7 +530,6 @@ pub impl mem_categorization_ctxt {
                 id:id,
                 span:span,
                 cat:cat_local(vid),
-                lp:Some(@lp_local(vid)),
                 mutbl:m,
                 ty:expr_ty
             }
@@ -567,7 +541,6 @@ pub impl mem_categorization_ctxt {
                 id:id,
                 span:span,
                 cat:cat_local(vid),
-                lp:Some(@lp_local(vid)),
                 mutbl:McImmutable,
                 ty:expr_ty
             }
@@ -575,26 +548,11 @@ pub impl mem_categorization_ctxt {
         }
     }
 
-    fn cat_variant<N:ast_node>(&self,
-                                arg: N,
-                                enum_did: ast::def_id,
-                                cmt: cmt) -> cmt {
-        @cmt_ {
-            id: arg.id(),
-            span: arg.span(),
-            cat: cat_comp(cmt, comp_variant(enum_did)),
-            lp: cmt.lp.map(|l| @lp_comp(*l, comp_variant(enum_did)) ),
-            mutbl: cmt.mutbl.inherit(),
-            ty: self.tcx.ty(arg)
-        }
-    }
-
     fn cat_rvalue<N:ast_node>(&self, elt: N, expr_ty: ty::t) -> cmt {
         @cmt_ {
             id:elt.id(),
             span:elt.span(),
             cat:cat_rvalue,
-            lp:None,
             mutbl:McImmutable,
             ty:expr_ty
         }
@@ -606,9 +564,9 @@ pub impl mem_categorization_ctxt {
     /// or if the container is mutable.
     fn inherited_mutability(&self,
                             base_m: MutabilityCategory,
-                            comp_m: ast::mutability) -> MutabilityCategory
+                            interior_m: ast::mutability) -> MutabilityCategory
     {
-        match comp_m {
+        match interior_m {
             m_imm => base_m.inherit(),
             m_const => McReadOnly,
             m_mutbl => McDeclared
@@ -621,6 +579,7 @@ pub impl mem_categorization_ctxt {
                              node: N,
                              base_cmt: cmt,
                              f_name: ast::ident,
+                             f_ty: ty::t,
                              field_id: ast::node_id) -> cmt {
         let f_mutbl = match field_mutbl(self.tcx, base_cmt.ty,
                                         f_name, field_id) {
@@ -634,15 +593,13 @@ pub impl mem_categorization_ctxt {
             }
         };
         let m = self.inherited_mutability(base_cmt.mutbl, f_mutbl);
-        let f_comp = comp_field(f_name, f_mutbl);
-        let lp = base_cmt.lp.map(|lp| @lp_comp(*lp, f_comp) );
+        let f_interior = interior_field(f_name, f_mutbl);
         @cmt_ {
             id: node.id(),
             span: node.span(),
-            cat: cat_comp(base_cmt, f_comp),
-            lp:lp,
+            cat: cat_interior(base_cmt, f_interior),
             mutbl: m,
-            ty: self.tcx.ty(node)
+            ty: f_ty
         }
     }
 
@@ -688,25 +645,10 @@ pub impl mem_categorization_ctxt {
     {
         match deref_kind(self.tcx, base_cmt.ty) {
             deref_ptr(ptr) => {
-                let lp = do base_cmt.lp.chain_ref |l| {
-                    // Given that the ptr itself is loanable, we can
-                    // loan out deref'd uniq ptrs or mut ptrs as the data
-                    // they are the only way to mutably reach the data they
-                    // point at. Other ptr types admit mutable aliases and
-                    // are therefore not loanable.
-                    match ptr {
-                        uniq_ptr => Some(@lp_deref(*l, ptr)),
-                        region_ptr(ast::m_mutbl, _) => {
-                            Some(@lp_deref(*l, ptr))
-                        }
-                        gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => None
-                    }
-                };
-
                 // for unique ptrs, we inherit mutability from the
                 // owning reference.
                 let m = match ptr {
-                    uniq_ptr => {
+                    uniq_ptr(*) => {
                         self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
                     }
                     gc_ptr(*) | region_ptr(_, _) | unsafe_ptr => {
@@ -718,20 +660,17 @@ pub impl mem_categorization_ctxt {
                     id:node.id(),
                     span:node.span(),
                     cat:cat_deref(base_cmt, deref_cnt, ptr),
-                    lp:lp,
                     mutbl:m,
                     ty:mt.ty
                 }
             }
 
-            deref_comp(comp) => {
-                let lp = base_cmt.lp.map(|l| @lp_comp(*l, comp) );
+            deref_interior(interior) => {
                 let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
                 @cmt_ {
                     id:node.id(),
                     span:node.span(),
-                    cat:cat_comp(base_cmt, comp),
-                    lp:lp,
+                    cat:cat_interior(base_cmt, interior),
                     mutbl:m,
                     ty:mt.ty
                 }
@@ -740,8 +679,8 @@ pub impl mem_categorization_ctxt {
     }
 
     fn cat_index<N:ast_node>(&self,
-                              elt: N,
-                              base_cmt: cmt) -> cmt {
+                             elt: N,
+                             base_cmt: cmt) -> cmt {
         let mt = match ty::index(base_cmt.ty) {
           Some(mt) => mt,
           None => {
@@ -754,17 +693,10 @@ pub impl mem_categorization_ctxt {
 
         return match deref_kind(self.tcx, base_cmt.ty) {
           deref_ptr(ptr) => {
-            // (a) the contents are loanable if the base is loanable
-            // and this is a *unique* vector
-            let deref_lp = match ptr {
-              uniq_ptr => {base_cmt.lp.map(|lp| @lp_deref(*lp, uniq_ptr))}
-              _ => {None}
-            };
-
-            // (b) for unique ptrs, we inherit mutability from the
+            // for unique ptrs, we inherit mutability from the
             // owning reference.
             let m = match ptr {
-              uniq_ptr => {
+              uniq_ptr(*) => {
                 self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
               }
               gc_ptr(_) | region_ptr(_, _) | unsafe_ptr => {
@@ -772,79 +704,51 @@ pub impl mem_categorization_ctxt {
               }
             };
 
-            // (c) the deref is explicit in the resulting cmt
+            // the deref is explicit in the resulting cmt
             let deref_cmt = @cmt_ {
                 id:elt.id(),
                 span:elt.span(),
                 cat:cat_deref(base_cmt, 0u, ptr),
-                lp:deref_lp,
                 mutbl:m,
                 ty:mt.ty
             };
 
-            comp(elt, deref_cmt, base_cmt.ty, m, mt)
+            interior(elt, deref_cmt, base_cmt.ty, m, mt)
           }
 
-          deref_comp(_) => {
+          deref_interior(_) => {
             // fixed-length vectors have no deref
             let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
-            comp(elt, base_cmt, base_cmt.ty, m, mt)
+            interior(elt, base_cmt, base_cmt.ty, m, mt)
           }
         };
 
-        fn comp<N:ast_node>(elt: N, of_cmt: cmt,
-                             vect: ty::t, mutbl: MutabilityCategory,
-                             mt: ty::mt) -> cmt
+        fn interior<N: ast_node>(elt: N, of_cmt: cmt,
+                                 vect: ty::t, mutbl: MutabilityCategory,
+                                 mt: ty::mt) -> cmt
         {
-            let comp = comp_index(vect, mt.mutbl);
-            let index_lp = of_cmt.lp.map(|lp| @lp_comp(*lp, comp) );
+            let interior = interior_index(vect, mt.mutbl);
             @cmt_ {
                 id:elt.id(),
                 span:elt.span(),
-                cat:cat_comp(of_cmt, comp),
-                lp:index_lp,
+                cat:cat_interior(of_cmt, interior),
                 mutbl:mutbl,
                 ty:mt.ty
             }
         }
     }
 
-    fn cat_tuple_elt<N:ast_node>(&self,
-                                  elt: N,
-                                  cmt: cmt) -> cmt {
-        @cmt_ {
-            id: elt.id(),
-            span: elt.span(),
-            cat: cat_comp(cmt, comp_tuple),
-            lp: cmt.lp.map(|l| @lp_comp(*l, comp_tuple) ),
-            mutbl: cmt.mutbl.inherit(),
-            ty: self.tcx.ty(elt)
-        }
-    }
-
-    fn cat_anon_struct_field<N:ast_node>(&self,
-                                          elt: N,
-                                          cmt: cmt) -> cmt {
-        @cmt_ {
-            id: elt.id(),
-            span: elt.span(),
-            cat: cat_comp(cmt, comp_anon_field),
-            lp: cmt.lp.map(|l| @lp_comp(*l, comp_anon_field)),
-            mutbl: cmt.mutbl.inherit(),
-            ty: self.tcx.ty(elt)
-        }
-    }
-
-    fn cat_method_ref(&self,
-                      expr: @ast::expr,
-                      expr_ty: ty::t) -> cmt {
+    fn cat_imm_interior<N:ast_node>(&self,
+                                    node: N,
+                                    base_cmt: cmt,
+                                    interior_ty: ty::t,
+                                    interior: interior_kind) -> cmt {
         @cmt_ {
-            id:expr.id,
-            span:expr.span,
-            cat:cat_special(sk_method),
-            lp:None,
-            mutbl:McImmutable,
-            ty:expr_ty
+            id: node.id(),
+            span: node.span(),
+            cat: cat_interior(base_cmt, interior),
+            mutbl: base_cmt.mutbl.inherit(),
+            ty: interior_ty
         }
     }
 
@@ -865,32 +769,42 @@ pub impl mem_categorization_ctxt {
         // we can be sure that the binding will remain valid for the
         // duration of the arm.
         //
-        // The correspondence between the id in the cmt and which
-        // pattern is being referred to is somewhat...subtle.  In
-        // general, the id of the cmt is the id of the node that
-        // produces the value.  For patterns, that's actually the
-        // *subpattern*, generally speaking.
+        // (*) There is subtlety concerning the correspondence between
+        // pattern ids and types as compared to *expression* ids and
+        // types. This is explained briefly. on the definition of the
+        // type `cmt`, so go off and read what it says there, then
+        // come back and I'll dive into a bit more detail here. :) OK,
+        // back?
         //
-        // To see what I mean about ids etc, consider:
+        // In general, the id of the cmt should be the node that
+        // "produces" the value---patterns aren't executable code
+        // exactly, but I consider them to "execute" when they match a
+        // value. So if you have something like:
         //
         //     let x = @@3;
         //     match x {
         //       @@y { ... }
         //     }
         //
-        // Here the cmt for `y` would be something like
+        // In this case, the cmt and the relevant ids would be:
+        //
+        //     CMT             Id                  Type of Id Type of cmt
         //
         //     local(x)->@->@
+        //     ^~~~~~~^        `x` from discr      @@int      @@int
+        //     ^~~~~~~~~~^     `@@y` pattern node  @@int      @int
+        //     ^~~~~~~~~~~~~^  `@y` pattern node   @int       int
         //
-        // where the id of `local(x)` is the id of the `x` that appears
-        // in the match, the id of `local(x)->@` is the `@y` pattern,
-        // and the id of `local(x)->@->@` is the id of the `y` pattern.
-
+        // You can see that the types of the id and the cmt are in
+        // sync in the first line, because that id is actually the id
+        // of an expression. But once we get to pattern ids, the types
+        // step out of sync again. So you'll see below that we always
+        // get the type of the *subpattern* and use that.
 
         let tcx = self.tcx;
         debug!("cat_pattern: id=%d pat=%s cmt=%s",
                pat.id, pprust::pat_to_str(pat, tcx.sess.intr()),
-               self.cmt_to_repr(cmt));
+               cmt.repr(tcx));
         let _i = indenter();
 
         op(cmt, pat);
@@ -907,22 +821,27 @@ pub impl mem_categorization_ctxt {
             match self.tcx.def_map.find(&pat.id) {
                 Some(&ast::def_variant(enum_did, _)) => {
                     // variant(x, y, z)
-                    for subpats.each |subpat| {
-                        let subcmt = self.cat_variant(*subpat, enum_did, cmt);
-                        self.cat_pattern(subcmt, *subpat, op);
+                    for subpats.each |&subpat| {
+                        let subpat_ty = self.pat_ty(subpat); // see (*)
+                        let subcmt =
+                            self.cat_imm_interior(pat, cmt, subpat_ty,
+                                                  interior_variant(enum_did));
+                        self.cat_pattern(subcmt, subpat, op);
                     }
                 }
                 Some(&ast::def_fn(*)) |
                 Some(&ast::def_struct(*)) => {
-                    for subpats.each |subpat| {
-                        let cmt_field = self.cat_anon_struct_field(*subpat,
-                                                                   cmt);
-                        self.cat_pattern(cmt_field, *subpat, op);
+                    for subpats.each |&subpat| {
+                        let subpat_ty = self.pat_ty(subpat); // see (*)
+                        let cmt_field =
+                            self.cat_imm_interior(pat, cmt, subpat_ty,
+                                                  interior_anon_field);
+                        self.cat_pattern(cmt_field, subpat, op);
                     }
                 }
                 Some(&ast::def_const(*)) => {
-                    for subpats.each |subpat| {
-                        self.cat_pattern(cmt, *subpat, op);
+                    for subpats.each |&subpat| {
+                        self.cat_pattern(cmt, subpat, op);
                     }
                 }
                 _ => {
@@ -944,39 +863,43 @@ pub impl mem_categorization_ctxt {
           ast::pat_struct(_, ref field_pats, _) => {
             // {f1: p1, ..., fN: pN}
             for field_pats.each |fp| {
-                let cmt_field = self.cat_field(fp.pat, cmt, fp.ident, pat.id);
+                let field_ty = self.pat_ty(fp.pat); // see (*)
+                let cmt_field = self.cat_field(pat, cmt, fp.ident,
+                                               field_ty, pat.id);
                 self.cat_pattern(cmt_field, fp.pat, op);
             }
           }
 
           ast::pat_tup(ref subpats) => {
             // (p1, ..., pN)
-            for subpats.each |subpat| {
-                let subcmt = self.cat_tuple_elt(*subpat, cmt);
-                self.cat_pattern(subcmt, *subpat, op);
+            for subpats.each |&subpat| {
+                let subpat_ty = self.pat_ty(subpat); // see (*)
+                let subcmt = self.cat_imm_interior(pat, cmt, subpat_ty,
+                                                   interior_tuple);
+                self.cat_pattern(subcmt, subpat, op);
             }
           }
 
           ast::pat_box(subpat) | ast::pat_uniq(subpat) |
           ast::pat_region(subpat) => {
             // @p1, ~p1
-            let subcmt = self.cat_deref(subpat, cmt, 0);
+            let subcmt = self.cat_deref(pat, cmt, 0);
             self.cat_pattern(subcmt, subpat, op);
           }
 
           ast::pat_vec(ref before, slice, ref after) => {
-              for before.each |pat| {
-                  let elt_cmt = self.cat_index(*pat, cmt);
-                  self.cat_pattern(elt_cmt, *pat, op);
+              for before.each |&before_pat| {
+                  let elt_cmt = self.cat_index(pat, cmt);
+                  self.cat_pattern(elt_cmt, before_pat, op);
               }
-              for slice.each |slice_pat| {
-                  let slice_ty = self.tcx.ty(*slice_pat);
-                  let slice_cmt = self.cat_rvalue(*slice_pat, slice_ty);
-                  self.cat_pattern(slice_cmt, *slice_pat, op);
+              for slice.each |&slice_pat| {
+                  let slice_ty = self.pat_ty(slice_pat);
+                  let slice_cmt = self.cat_rvalue(pat, slice_ty);
+                  self.cat_pattern(slice_cmt, slice_pat, op);
               }
-              for after.each |pat| {
-                  let elt_cmt = self.cat_index(*pat, cmt);
-                  self.cat_pattern(elt_cmt, *pat, op);
+              for after.each |&after_pat| {
+                  let elt_cmt = self.cat_index(pat, cmt);
+                  self.cat_pattern(elt_cmt, after_pat, op);
               }
           }
 
@@ -986,29 +909,6 @@ pub impl mem_categorization_ctxt {
         }
     }
 
-    fn cat_to_repr(&self, cat: categorization) -> ~str {
-        match cat {
-          cat_special(sk_method) => ~"method",
-          cat_special(sk_static_item) => ~"static_item",
-          cat_special(sk_implicit_self) => ~"implicit-self",
-          cat_special(sk_heap_upvar) => ~"heap-upvar",
-          cat_stack_upvar(_) => ~"stack-upvar",
-          cat_rvalue => ~"rvalue",
-          cat_local(node_id) => fmt!("local(%d)", node_id),
-          cat_binding(node_id) => fmt!("binding(%d)", node_id),
-          cat_arg(node_id) => fmt!("arg(%d)", node_id),
-          cat_self(node_id) => fmt!("self(%d)", node_id),
-          cat_deref(cmt, derefs, ptr) => {
-            fmt!("%s->(%s, %u)", self.cat_to_repr(cmt.cat),
-                 self.ptr_sigil(ptr), derefs)
-          }
-          cat_comp(cmt, comp) => {
-            fmt!("%s.%s", self.cat_to_repr(cmt.cat), *self.comp_to_repr(comp))
-          }
-          cat_discr(cmt, _) => self.cat_to_repr(cmt.cat)
-        }
-    }
-
     fn mut_to_str(&self, mutbl: ast::mutability) -> ~str {
         match mutbl {
           m_mutbl => ~"mutable",
@@ -1017,84 +917,33 @@ pub impl mem_categorization_ctxt {
         }
     }
 
-    fn ptr_sigil(&self, ptr: ptr_kind) -> ~str {
-        match ptr {
-          uniq_ptr => ~"~",
-          gc_ptr(_) => ~"@",
-          region_ptr(_, _) => ~"&",
-          unsafe_ptr => ~"*"
-        }
-    }
-
-    fn comp_to_repr(&self, comp: comp_kind) -> @~str {
-        match comp {
-          comp_field(fld, _) => self.tcx.sess.str_of(fld),
-          comp_index(*) => @~"[]",
-          comp_tuple => @~"()",
-          comp_anon_field => @~"<anonymous field>",
-          comp_variant(_) => @~"<enum>"
-        }
-    }
-
-    fn lp_to_str(&self, lp: @loan_path) -> ~str {
-        match *lp {
-          lp_local(node_id) => {
-            fmt!("local(%d)", node_id)
-          }
-          lp_arg(node_id) => {
-            fmt!("arg(%d)", node_id)
-          }
-          lp_self => ~"self",
-          lp_deref(lp, ptr) => {
-            fmt!("%s->(%s)", self.lp_to_str(lp),
-                 self.ptr_sigil(ptr))
-          }
-          lp_comp(lp, comp) => {
-            fmt!("%s.%s", self.lp_to_str(lp),
-                 *self.comp_to_repr(comp))
-          }
-        }
-    }
-
-    fn cmt_to_repr(&self, cmt: cmt) -> ~str {
-        fmt!("{%s id:%d m:%? lp:%s ty:%s}",
-             self.cat_to_repr(cmt.cat),
-             cmt.id,
-             cmt.mutbl,
-             cmt.lp.map_default(~"none", |p| self.lp_to_str(*p) ),
-             ty_to_str(self.tcx, cmt.ty))
-    }
-
     fn cmt_to_str(&self, cmt: cmt) -> ~str {
-        let mut_str = cmt.mutbl.to_user_str();
         match cmt.cat {
-          cat_special(sk_method) => ~"method",
-          cat_special(sk_static_item) => ~"static item",
-          cat_special(sk_implicit_self) => ~"self reference",
-          cat_special(sk_heap_upvar) => {
+          cat_static_item => ~"static item",
+          cat_implicit_self => ~"self reference",
+          cat_copied_upvar(_) => {
               ~"captured outer variable in a heap closure"
           }
           cat_rvalue => ~"non-lvalue",
-          cat_local(_) => mut_str + ~" local variable",
-          cat_binding(_) => ~"pattern binding",
+          cat_local(_) => ~"local variable",
           cat_self(_) => ~"self value",
-          cat_arg(_) => ~"argument",
-          cat_deref(_, _, pk) => fmt!("dereference of %s %s pointer",
-                                      mut_str, self.ptr_sigil(pk)),
-          cat_stack_upvar(_) => {
-            ~"captured outer " + mut_str + ~" variable in a stack closure"
-          }
-          cat_comp(_, comp_field(*)) => mut_str + ~" field",
-          cat_comp(_, comp_tuple) => ~"tuple content",
-          cat_comp(_, comp_anon_field) => ~"anonymous field",
-          cat_comp(_, comp_variant(_)) => ~"enum content",
-          cat_comp(_, comp_index(t, _)) => {
+          cat_arg(*) => ~"argument",
+          cat_deref(_, _, pk) => fmt!("dereference of %s pointer",
+                                      ptr_sigil(pk)),
+          cat_interior(_, interior_field(*)) => ~"field",
+          cat_interior(_, interior_tuple) => ~"tuple content",
+          cat_interior(_, interior_anon_field) => ~"anonymous field",
+          cat_interior(_, interior_variant(_)) => ~"enum content",
+          cat_interior(_, interior_index(t, _)) => {
             match ty::get(t).sty {
-              ty::ty_evec(*) => mut_str + ~" vec content",
-              ty::ty_estr(*) => mut_str + ~" str content",
-              _ => mut_str + ~" indexed content"
+              ty::ty_evec(*) => ~"vec content",
+              ty::ty_estr(*) => ~"str content",
+              _ => ~"indexed content"
             }
           }
+          cat_stack_upvar(_) => {
+              ~"captured outer variable"
+          }
           cat_discr(cmt, _) => {
             self.cmt_to_str(cmt)
           }
@@ -1128,7 +977,7 @@ pub fn field_mutbl(tcx: ty::ctxt,
         }
       }
       ty::ty_enum(*) => {
-        match *tcx.def_map.get(&node_id) {
+        match tcx.def_map.get_copy(&node_id) {
           ast::def_variant(_, variant_id) => {
             for ty::lookup_struct_fields(tcx, variant_id).each |fld| {
                 if fld.ident == f_name {
@@ -1149,33 +998,141 @@ pub fn field_mutbl(tcx: ty::ctxt,
     return None;
 }
 
-pub impl categorization {
-    fn derefs_through_mutable_box(&const self) -> bool {
-        match *self {
-            cat_deref(_, _, gc_ptr(ast::m_mutbl)) => {
-                true
+pub enum AliasableReason {
+    AliasableManaged(ast::mutability),
+    AliasableBorrowed(ast::mutability),
+    AliasableOther
+}
+
+pub impl cmt_ {
+    fn guarantor(@self) -> cmt {
+        //! Returns `self` after stripping away any owned pointer derefs or
+        //! interior content. The return value is basically the `cmt` which
+        //! determines how long the value in `self` remains live.
+
+        match self.cat {
+            cat_rvalue |
+            cat_static_item |
+            cat_implicit_self |
+            cat_copied_upvar(*) |
+            cat_local(*) |
+            cat_self(*) |
+            cat_arg(*) |
+            cat_deref(_, _, unsafe_ptr(*)) |
+            cat_deref(_, _, gc_ptr(*)) |
+            cat_deref(_, _, region_ptr(*)) => {
+                self
+            }
+            cat_stack_upvar(b) |
+            cat_discr(b, _) |
+            cat_interior(b, _) |
+            cat_deref(b, _, uniq_ptr(*)) => {
+                b.guarantor()
+            }
+        }
+    }
+
+    fn is_freely_aliasable(&self) -> bool {
+        self.freely_aliasable().is_some()
+    }
+
+    fn freely_aliasable(&self) -> Option<AliasableReason> {
+        //! True if this lvalue resides in an area that is
+        //! freely aliasable, meaning that rustc cannot track
+        //! the alias//es with precision.
+
+        // Maybe non-obvious: copied upvars can only be considered
+        // non-aliasable in once closures, since any other kind can be
+        // aliased and eventually recused.
+
+        match self.cat {
+            cat_copied_upvar(CopiedUpvar {onceness: ast::Once, _}) |
+            cat_rvalue(*) |
+            cat_local(*) |
+            cat_arg(_) |
+            cat_self(*) |
+            cat_deref(_, _, unsafe_ptr(*)) | // of course it is aliasable, but...
+            cat_deref(_, _, region_ptr(m_mutbl, _)) => {
+                None
+            }
+
+            cat_copied_upvar(CopiedUpvar {onceness: ast::Many, _}) |
+            cat_static_item(*) |
+            cat_implicit_self(*) => {
+                Some(AliasableOther)
+            }
+
+            cat_deref(_, _, gc_ptr(m)) => {
+                Some(AliasableManaged(m))
+            }
+
+            cat_deref(_, _, region_ptr(m @ m_const, _)) |
+            cat_deref(_, _, region_ptr(m @ m_imm, _)) => {
+                Some(AliasableBorrowed(m))
             }
-            cat_deref(subcmt, _, _) |
-            cat_comp(subcmt, _) |
-            cat_discr(subcmt, _) |
-            cat_stack_upvar(subcmt) => {
-                subcmt.cat.derefs_through_mutable_box()
+
+            cat_stack_upvar(b) |
+            cat_deref(b, _, uniq_ptr(*)) |
+            cat_interior(b, _) |
+            cat_discr(b, _) => {
+                b.freely_aliasable()
             }
+        }
+    }
+}
+
+impl Repr for cmt {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        fmt!("{%s id:%d m:%? ty:%s}",
+             self.cat.repr(tcx),
+             self.id,
+             self.mutbl,
+             self.ty.repr(tcx))
+    }
+}
+
+impl Repr for categorization {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
+        match *self {
+            cat_static_item |
+            cat_implicit_self |
             cat_rvalue |
-            cat_special(*) |
+            cat_copied_upvar(*) |
             cat_local(*) |
-            cat_binding(*) |
-            cat_arg(*) |
-            cat_self(*) => {
-                false
+            cat_self(*) |
+            cat_arg(*) => fmt!("%?", *self),
+            cat_deref(cmt, derefs, ptr) => {
+                fmt!("%s->(%s, %u)", cmt.cat.repr(tcx),
+                     ptr_sigil(ptr), derefs)
+            }
+            cat_interior(cmt, interior) => {
+                fmt!("%s.%s",
+                     cmt.cat.repr(tcx),
+                     interior.repr(tcx))
             }
+            cat_stack_upvar(cmt) |
+            cat_discr(cmt, _) => cmt.cat.repr(tcx)
         }
     }
+}
+
+pub fn ptr_sigil(ptr: ptr_kind) -> ~str {
+    match ptr {
+        uniq_ptr(_) => ~"~",
+        gc_ptr(_) => ~"@",
+        region_ptr(_, _) => ~"&",
+        unsafe_ptr => ~"*"
+    }
+}
 
-    fn is_mutable_box(&const self) -> bool {
+impl Repr for interior_kind {
+    fn repr(&self, tcx: ty::ctxt) -> ~str {
         match *self {
-            cat_deref(_, _, gc_ptr(ast::m_mutbl)) => true,
-            _ => false
+            interior_field(fld, _) => copy *tcx.sess.str_of(fld),
+            interior_index(*) => ~"[]",
+            interior_tuple => ~"()",
+            interior_anon_field => ~"<anonymous field>",
+            interior_variant(_) => ~"<enum>"
         }
     }
 }
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
index ecee2ea1a2fb2..040ff30f9e63f 100644
--- a/src/librustc/middle/moves.rs
+++ b/src/librustc/middle/moves.rs
@@ -246,10 +246,19 @@ pub type MovesMap = @mut HashSet<node_id>;
  * expression */
 pub type VariableMovesMap = @mut HashMap<node_id, @expr>;
 
+/**
+ * Set of variable node-ids that are moved.
+ *
+ * Note: The `VariableMovesMap` stores expression ids that
+ * are moves, whereas this set stores the ids of the variables
+ * that are moved at some point */
+pub type MovedVariablesSet = @mut HashSet<node_id>;
+
 /** See the section Output on the module comment for explanation. */
 pub struct MoveMaps {
     moves_map: MovesMap,
     variable_moves_map: VariableMovesMap,
+    moved_variables_set: MovedVariablesSet,
     capture_map: CaptureMap
 }
 
@@ -279,13 +288,25 @@ pub fn compute_moves(tcx: ty::ctxt,
         move_maps: MoveMaps {
             moves_map: @mut HashSet::new(),
             variable_moves_map: @mut HashMap::new(),
-            capture_map: @mut HashMap::new()
+            capture_map: @mut HashMap::new(),
+            moved_variables_set: @mut HashSet::new()
         }
     };
     visit::visit_crate(crate, visit_cx, visitor);
     return visit_cx.move_maps;
 }
 
+pub fn moved_variable_node_id_from_def(def: def) -> Option<node_id> {
+    match def {
+      def_binding(nid, _) |
+      def_arg(nid, _) |
+      def_local(nid, _) |
+      def_self(nid, _) => Some(nid),
+
+      _ => None
+    }
+}
+
 // ______________________________________________________________________
 // Expressions
 
@@ -419,6 +440,11 @@ pub impl VisitContext {
                     MoveInPart(entire_expr) => {
                         self.move_maps.variable_moves_map.insert(
                             expr.id, entire_expr);
+
+                        let def = self.tcx.def_map.get_copy(&expr.id);
+                        for moved_variable_node_id_from_def(def).each |&id| {
+                            self.move_maps.moved_variables_set.insert(id);
+                        }
                     }
                     Read => {}
                     MoveInWhole => {
diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs
index 083c436c83e84..ce0f124da74c9 100644
--- a/src/librustc/middle/privacy.rs
+++ b/src/librustc/middle/privacy.rs
@@ -481,7 +481,7 @@ pub fn check_crate(tcx: ty::ctxt,
                     }
                 }
                 expr_path(path) => {
-                    check_path(expr.span, *tcx.def_map.get(&expr.id), path);
+                    check_path(expr.span, tcx.def_map.get_copy(&expr.id), path);
                 }
                 expr_struct(_, ref fields, _) => {
                     match ty::get(ty::expr_ty(tcx, expr)).sty {
@@ -499,7 +499,7 @@ pub fn check_crate(tcx: ty::ctxt,
                         ty_enum(id, _) => {
                             if id.crate != local_crate ||
                                     !privileged_items.contains(&(id.node)) {
-                                match *tcx.def_map.get(&expr.id) {
+                                match tcx.def_map.get_copy(&expr.id) {
                                     def_variant(_, variant_id) => {
                                         for (*fields).each |field| {
                                                 debug!("(privacy checking) \
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 753a6d25cbb3d..cdc3aa9fedb7e 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -47,59 +47,27 @@ The region maps encode information about region relationships.
   - the free region map is populated during type check as we check
     each function. See the function `relate_free_regions` for
     more information.
+- `cleanup_scopes` includes scopes where trans cleanups occur
+  - this is intended to reflect the current state of trans, not
+    necessarily how I think things ought to work
 */
 pub struct RegionMaps {
     priv scope_map: HashMap<ast::node_id, ast::node_id>,
     priv free_region_map: HashMap<FreeRegion, ~[FreeRegion]>,
+    priv cleanup_scopes: HashSet<ast::node_id>
 }
 
-pub struct ctxt {
+pub struct Context {
     sess: Session,
     def_map: resolve::DefMap,
 
     // Generated maps:
     region_maps: @mut RegionMaps,
 
-    // Generally speaking, expressions are parented to their innermost
-    // enclosing block. But some kinds of expressions serve as
-    // parents: calls, methods, etc.  In addition, some expressions
-    // serve as parents by virtue of where they appear.  For example,
-    // the condition in a while loop is always a parent.  In those
-    // cases, we add the node id of such an expression to this set so
-    // that when we visit it we can view it as a parent.
-    root_exprs: @mut HashSet<ast::node_id>,
-
-    // The parent scope is the innermost block, statement, call, or match
-    // expression during the execution of which the current expression
-    // will be evaluated.  Generally speaking, the innermost parent
-    // scope is also the closest suitable ancestor in the AST tree.
-    //
-    // There is a subtle point concerning call arguments.  Imagine
-    // you have a call:
-    //
-    // { // block a
-    //     foo( // call b
-    //        x,
-    //        y);
-    // }
-    //
-    // In what lifetime are the expressions `x` and `y` evaluated?  At
-    // first, I imagine the answer was the block `a`, as the arguments
-    // are evaluated before the call takes place.  But this turns out
-    // to be wrong.  The lifetime of the call must encompass the
-    // argument evaluation as well.
-    //
-    // The reason is that evaluation of an earlier argument could
-    // create a borrow which exists during the evaluation of later
-    // arguments.  Consider this torture test, for example,
-    //
-    // fn test1(x: @mut ~int) {
-    //     foo(&**x, *x = ~5);
-    // }
-    //
-    // Here, the first argument `&**x` will be a borrow of the `~int`,
-    // but the second argument overwrites that very value! Bad.
-    // (This test is borrowck-pure-scope-in-call.rs, btw)
+    // Scope where variables should be parented to
+    var_parent: parent,
+
+    // Innermost enclosing expression
     parent: parent,
 }
 
@@ -128,10 +96,22 @@ pub impl RegionMaps {
                      sup: ast::node_id)
     {
         debug!("record_parent(sub=%?, sup=%?)", sub, sup);
+        assert!(sub != sup);
 
         self.scope_map.insert(sub, sup);
     }
 
+    pub fn record_cleanup_scope(&mut self,
+                                scope_id: ast::node_id)
+    {
+        //! Records that a scope is a CLEANUP SCOPE.  This is invoked
+        //! from within regionck.  We wait until regionck because we do
+        //! not know which operators are overloaded until that point,
+        //! and only overloaded operators result in cleanup scopes.
+
+        self.cleanup_scopes.insert(scope_id);
+    }
+
     fn opt_encl_scope(&self,
                       id: ast::node_id) -> Option<ast::node_id>
     {
@@ -151,6 +131,22 @@ pub impl RegionMaps {
         }
     }
 
+    fn is_cleanup_scope(&self, scope_id: ast::node_id) -> bool {
+        self.cleanup_scopes.contains(&scope_id)
+    }
+
+    fn cleanup_scope(&self,
+                     expr_id: ast::node_id) -> ast::node_id
+    {
+        //! Returns the scope when temps in expr will be cleaned up
+
+        let mut id = self.encl_scope(expr_id);
+        while !self.cleanup_scopes.contains(&id) {
+            id = self.encl_scope(id);
+        }
+        return id;
+    }
+
     fn encl_region(&self,
                    id: ast::node_id) -> ty::Region
     {
@@ -159,22 +155,38 @@ pub impl RegionMaps {
         ty::re_scope(self.encl_scope(id))
     }
 
-    fn is_sub_scope(&self,
-                    sub_scope: ast::node_id,
-                    superscope: ast::node_id) -> bool
+    pub fn scopes_intersect(&self,
+                            scope1: ast::node_id,
+                            scope2: ast::node_id) -> bool
+    {
+        self.is_subscope_of(scope1, scope2) || self.is_subscope_of(scope2, scope1)
+    }
+
+    fn is_subscope_of(&self,
+                      subscope: ast::node_id,
+                      superscope: ast::node_id) -> bool
     {
         /*!
-         * Returns true if `sub_scope` is equal to or is lexically
+         * Returns true if `subscope` is equal to or is lexically
          * nested inside `superscope` and false otherwise.
          */
 
-        let mut sub_scope = sub_scope;
-        while superscope != sub_scope {
-            match self.scope_map.find(&sub_scope) {
-                None => return false,
-                Some(&scope) => sub_scope = scope
+        let mut s = subscope;
+        while superscope != s {
+            match self.scope_map.find(&s) {
+                None => {
+                    debug!("is_subscope_of(%?, %?, s=%?)=false",
+                           subscope, superscope, s);
+
+                    return false;
+                }
+                Some(&scope) => s = scope
             }
         }
+
+        debug!("is_subscope_of(%?, %?)=true",
+               subscope, superscope);
+
         return true;
     }
 
@@ -239,11 +251,11 @@ pub impl RegionMaps {
                 }
 
                 (ty::re_scope(sub_scope), ty::re_scope(super_scope)) => {
-                    self.is_sub_scope(sub_scope, super_scope)
+                    self.is_subscope_of(sub_scope, super_scope)
                 }
 
                 (ty::re_scope(sub_scope), ty::re_free(ref fr)) => {
-                    self.is_sub_scope(sub_scope, fr.scope_id)
+                    self.is_subscope_of(sub_scope, fr.scope_id)
                 }
 
                 (ty::re_free(sub_fr), ty::re_free(super_fr)) => {
@@ -301,6 +313,7 @@ pub impl RegionMaps {
         fn ancestors_of(self: &RegionMaps, scope: ast::node_id)
             -> ~[ast::node_id]
         {
+            // debug!("ancestors_of(scope=%d)", scope);
             let mut result = ~[scope];
             let mut scope = scope;
             loop {
@@ -311,13 +324,14 @@ pub impl RegionMaps {
                         scope = superscope;
                     }
                 }
+                // debug!("ancestors_of_loop(scope=%d)", scope);
             }
         }
     }
 }
 
 /// Extracts that current parent from cx, failing if there is none.
-pub fn parent_id(cx: ctxt, span: span) -> ast::node_id {
+pub fn parent_id(cx: Context, span: span) -> ast::node_id {
     match cx.parent {
       None => {
         cx.sess.span_bug(span, "crate should not be parent here");
@@ -329,143 +343,136 @@ pub fn parent_id(cx: ctxt, span: span) -> ast::node_id {
 }
 
 /// Records the current parent (if any) as the parent of `child_id`.
-pub fn record_parent(cx: ctxt, child_id: ast::node_id) {
+pub fn parent_to_expr(cx: Context, child_id: ast::node_id) {
     for cx.parent.each |parent_id| {
         cx.region_maps.record_parent(child_id, *parent_id);
     }
 }
 
-pub fn resolve_block(blk: &ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_block(blk: &ast::blk, cx: Context, visitor: visit::vt<Context>) {
     // Record the parent of this block.
-    record_parent(cx, blk.node.id);
+    parent_to_expr(cx, blk.node.id);
 
     // Descend.
-    let new_cx: ctxt = ctxt {parent: Some(blk.node.id),.. cx};
+    let new_cx = Context {var_parent: Some(blk.node.id),
+                          parent: Some(blk.node.id),
+                          ..cx};
     visit::visit_block(blk, new_cx, visitor);
 }
 
-pub fn resolve_arm(arm: &ast::arm, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_arm(arm: &ast::arm, cx: Context, visitor: visit::vt<Context>) {
     visit::visit_arm(arm, cx, visitor);
 }
 
-pub fn resolve_pat(pat: @ast::pat, cx: ctxt, visitor: visit::vt<ctxt>) {
-    match pat.node {
-      ast::pat_ident(*) => {
-        let defn_opt = cx.def_map.find(&pat.id);
-        match defn_opt {
-          Some(&ast::def_variant(_,_)) => {
-            /* Nothing to do; this names a variant. */
-          }
-          _ => {
-            /* This names a local. Bind it to the containing scope. */
-            record_parent(cx, pat.id);
-          }
-        }
-      }
-      _ => { /* no-op */ }
-    }
-
+pub fn resolve_pat(pat: @ast::pat, cx: Context, visitor: visit::vt<Context>) {
+    assert!(cx.var_parent == cx.parent);
+    parent_to_expr(cx, pat.id);
     visit::visit_pat(pat, cx, visitor);
 }
 
-pub fn resolve_stmt(stmt: @ast::stmt, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_stmt(stmt: @ast::stmt, cx: Context, visitor: visit::vt<Context>) {
     match stmt.node {
-      ast::stmt_decl(*) => {
-        visit::visit_stmt(stmt, cx, visitor);
-      }
-      // This code has to be kept consistent with trans::base::trans_stmt
-      ast::stmt_expr(_, stmt_id) |
-      ast::stmt_semi(_, stmt_id) => {
-        record_parent(cx, stmt_id);
-        let mut expr_cx = cx;
-        expr_cx.parent = Some(stmt_id);
-        visit::visit_stmt(stmt, expr_cx, visitor);
-      }
-      ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro")
+        ast::stmt_decl(*) => {
+            visit::visit_stmt(stmt, cx, visitor);
+        }
+        ast::stmt_expr(_, stmt_id) |
+        ast::stmt_semi(_, stmt_id) => {
+            parent_to_expr(cx, stmt_id);
+            let expr_cx = Context {parent: Some(stmt_id), ..cx};
+            visit::visit_stmt(stmt, expr_cx, visitor);
+        }
+        ast::stmt_mac(*) => cx.sess.bug(~"unexpanded macro")
     }
 }
 
-pub fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
-    record_parent(cx, expr.id);
+pub fn resolve_expr(expr: @ast::expr, cx: Context, visitor: visit::vt<Context>) {
+    parent_to_expr(cx, expr.id);
 
     let mut new_cx = cx;
+    new_cx.parent = Some(expr.id);
     match expr.node {
-      // Calls or overloadable operators
-      // FIXME #3387
-      // ast::expr_index(*) | ast::expr_binary(*) |
-      // ast::expr_unary(*) |
-      ast::expr_call(*) | ast::expr_method_call(*) => {
-        debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
-                                                           cx.sess.intr()));
-        new_cx.parent = Some(expr.id);
-      }
-      ast::expr_match(*) => {
-        debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
-                                                           cx.sess.intr()));
-        new_cx.parent = Some(expr.id);
-      }
-      ast::expr_while(cond, _) => {
-        new_cx.root_exprs.insert(cond.id);
-      }
-      _ => {}
+        ast::expr_assign_op(*) | ast::expr_index(*) | ast::expr_binary(*) |
+        ast::expr_unary(*) | ast::expr_call(*) | ast::expr_method_call(*) => {
+            // FIXME(#6268) Nested method calls
+            //
+            // The lifetimes for a call or method call look as follows:
+            //
+            // call.id
+            // - arg0.id
+            // - ...
+            // - argN.id
+            // - call.callee_id
+            //
+            // The idea is that call.callee_id represents *the time when
+            // the invoked function is actually running* and call.id
+            // represents *the time to prepare the arguments and make the
+            // call*.  See the section "Borrows in Calls" borrowck/doc.rs
+            // for an extended explanantion of why this distinction is
+            // important.
+            //
+            // parent_to_expr(new_cx, expr.callee_id);
+        }
+
+        ast::expr_match(*) => {
+            new_cx.var_parent = Some(expr.id);
+        }
+
+        _ => {}
     };
 
-    if new_cx.root_exprs.contains(&expr.id) {
-        new_cx.parent = Some(expr.id);
-    }
 
     visit::visit_expr(expr, new_cx, visitor);
 }
 
 pub fn resolve_local(local: @ast::local,
-                     cx: ctxt,
-                     visitor: visit::vt<ctxt>) {
-    record_parent(cx, local.node.id);
+                     cx: Context,
+                     visitor: visit::vt<Context>) {
+    assert!(cx.var_parent == cx.parent);
+    parent_to_expr(cx, local.node.id);
     visit::visit_local(local, cx, visitor);
 }
 
-pub fn resolve_item(item: @ast::item, cx: ctxt, visitor: visit::vt<ctxt>) {
+pub fn resolve_item(item: @ast::item, cx: Context, visitor: visit::vt<Context>) {
     // Items create a new outer block scope as far as we're concerned.
-    let new_cx: ctxt = ctxt {parent: None,.. cx};
+    let new_cx = Context {var_parent: None, parent: None, ..cx};
     visit::visit_item(item, new_cx, visitor);
 }
 
 pub fn resolve_fn(fk: &visit::fn_kind,
                   decl: &ast::fn_decl,
                   body: &ast::blk,
-                  sp: span,
+                  _sp: span,
                   id: ast::node_id,
-                  cx: ctxt,
-                  visitor: visit::vt<ctxt>) {
-    let fn_cx = match *fk {
-        visit::fk_item_fn(*) | visit::fk_method(*) => {
-            // Top-level functions are a root scope.
-            ctxt {parent: Some(id),.. cx}
-        }
-
-        visit::fk_anon(*) | visit::fk_fn_block(*) => {
-            // Closures continue with the inherited scope.
-            cx
-        }
-    };
-
-    // Record the ID of `self`.
+                  cx: Context,
+                  visitor: visit::vt<Context>) {
+    debug!("region::resolve_fn(id=%?, body.node.id=%?, cx.parent=%?)",
+           id, body.node.id, cx.parent);
+
+    // The arguments and `self` are parented to the body of the fn.
+    let decl_cx = Context {parent: Some(body.node.id),
+                           var_parent: Some(body.node.id),
+                           ..cx};
     match *fk {
         visit::fk_method(_, _, method) => {
             cx.region_maps.record_parent(method.self_id, body.node.id);
         }
         _ => {}
     }
+    visit::visit_fn_decl(decl, decl_cx, visitor);
 
-    debug!("visiting fn with body %d. cx.parent: %? \
-            fn_cx.parent: %?",
-           body.node.id, cx.parent, fn_cx.parent);
-
-    for decl.inputs.each |input| {
-        cx.region_maps.record_parent(input.id, body.node.id);
-    }
-
-    visit::visit_fn(fk, decl, body, sp, id, fn_cx, visitor);
+    // The body of the fn itself is either a root scope (top-level fn)
+    // or it continues with the inherited scope (closures).
+    let body_cx = match *fk {
+        visit::fk_item_fn(*) |
+        visit::fk_method(*) => {
+            Context {parent: None, var_parent: None, ..cx}
+        }
+        visit::fk_anon(*) |
+        visit::fk_fn_block(*) => {
+            cx
+        }
+    };
+    (visitor.visit_block)(body, body_cx, visitor);
 }
 
 pub fn resolve_crate(sess: Session,
@@ -474,13 +481,14 @@ pub fn resolve_crate(sess: Session,
 {
     let region_maps = @mut RegionMaps {
         scope_map: HashMap::new(),
-        free_region_map: HashMap::new()
+        free_region_map: HashMap::new(),
+        cleanup_scopes: HashSet::new(),
     };
-    let cx: ctxt = ctxt {sess: sess,
-                         def_map: def_map,
-                         region_maps: region_maps,
-                         root_exprs: @mut HashSet::new(),
-                         parent: None};
+    let cx = Context {sess: sess,
+                      def_map: def_map,
+                      region_maps: region_maps,
+                      parent: None,
+                      var_parent: None};
     let visitor = visit::mk_vt(@visit::Visitor {
         visit_block: resolve_block,
         visit_item: resolve_item,
@@ -771,7 +779,8 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
                    pprust::ty_to_str(ty, sess.intr()));
 
             if cx.region_is_relevant(r) {
-                cx.add_rp(cx.item_id, cx.add_variance(rv_contravariant))
+                let rv = cx.add_variance(rv_contravariant);
+                cx.add_rp(cx.item_id, rv)
             }
         }
 
@@ -781,14 +790,14 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
             match f.region {
                 Some(_) => {
                     if cx.region_is_relevant(f.region) {
-                        cx.add_rp(cx.item_id,
-                                  cx.add_variance(rv_contravariant))
+                        let rv = cx.add_variance(rv_contravariant);
+                        cx.add_rp(cx.item_id, rv)
                     }
                 }
                 None => {
                     if f.sigil == ast::BorrowedSigil && cx.anon_implies_rp {
-                        cx.add_rp(cx.item_id,
-                                  cx.add_variance(rv_contravariant));
+                        let rv = cx.add_variance(rv_contravariant);
+                        cx.add_rp(cx.item_id, rv)
                     }
                 }
             }
@@ -819,7 +828,8 @@ pub fn determine_rp_in_ty(ty: @ast::Ty,
                     debug!("reference to external, rp'd type %s",
                            pprust::ty_to_str(ty, sess.intr()));
                     if cx.region_is_relevant(path.rp) {
-                        cx.add_rp(cx.item_id, cx.add_variance(variance))
+                        let rv = cx.add_variance(variance);
+                        cx.add_rp(cx.item_id, rv)
                     }
                   }
                 }
@@ -938,7 +948,7 @@ pub fn determine_rp_in_crate(sess: Session,
         let cx = &mut *cx;
         while cx.worklist.len() != 0 {
             let c_id = cx.worklist.pop();
-            let c_variance = *cx.region_paramd_items.get(&c_id);
+            let c_variance = cx.region_paramd_items.get_copy(&c_id);
             debug!("popped %d from worklist", c_id);
             match cx.dep_map.find(&c_id) {
               None => {}
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 2773710ca98fc..946bf26fd2713 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -779,9 +779,9 @@ pub fn Resolver(session: Session,
         unresolved_imports: 0,
 
         current_module: current_module,
-        value_ribs: ~[],
-        type_ribs: ~[],
-        label_ribs: ~[],
+        value_ribs: @mut ~[],
+        type_ribs: @mut ~[],
+        label_ribs: @mut ~[],
 
         xray_context: NoXray,
         current_trait_refs: None,
@@ -830,13 +830,13 @@ pub struct Resolver {
 
     // The current set of local scopes, for values.
     // FIXME #4948: Reuse ribs to avoid allocation.
-    value_ribs: ~[@Rib],
+    value_ribs: @mut ~[@Rib],
 
     // The current set of local scopes, for types.
-    type_ribs: ~[@Rib],
+    type_ribs: @mut ~[@Rib],
 
     // The current set of local scopes, for labels.
-    label_ribs: ~[@Rib],
+    label_ribs: @mut ~[@Rib],
 
     // Whether the current context is an X-ray context. An X-ray context is
     // allowed to access private names of any module.
@@ -971,7 +971,7 @@ pub impl Resolver {
                 module_.children.insert(name, child);
                 return (child, new_parent);
             }
-            Some(child) => {
+            Some(&child) => {
                 // Enforce the duplicate checking mode:
                 //
                 // * If we're requesting duplicate module checking, check that
@@ -1033,7 +1033,7 @@ pub impl Resolver {
                                   *self.session.str_of(name)));
                     }
                 }
-                return (*child, new_parent);
+                return (child, new_parent);
             }
         }
     }
@@ -1864,7 +1864,7 @@ pub impl Resolver {
                        *self.session.str_of(target));
 
                 match module_.import_resolutions.find(&target) {
-                    Some(resolution) => {
+                    Some(&resolution) => {
                         debug!("(building import directive) bumping \
                                 reference");
                         resolution.outstanding_references += 1;
@@ -2395,7 +2395,7 @@ pub impl Resolver {
                         (*ident, new_import_resolution);
                 }
                 None => { /* continue ... */ }
-                Some(dest_import_resolution) => {
+                Some(&dest_import_resolution) => {
                     // Merge the two import resolutions at a finer-grained
                     // level.
 
@@ -2433,8 +2433,8 @@ pub impl Resolver {
                     module_.import_resolutions.insert
                         (*ident, dest_import_resolution);
                 }
-                Some(existing_import_resolution) => {
-                    dest_import_resolution = *existing_import_resolution;
+                Some(&existing_import_resolution) => {
+                    dest_import_resolution = existing_import_resolution;
                 }
             }
 
@@ -4294,19 +4294,18 @@ pub impl Resolver {
                 }
 
                 pat_struct(path, _, _) => {
-                    let structs: &mut HashSet<def_id> = &mut self.structs;
                     match self.resolve_path(path, TypeNS, false, visitor) {
                         Some(def_ty(class_id))
-                                if structs.contains(&class_id) => {
+                                if self.structs.contains(&class_id) => {
                             let class_def = def_struct(class_id);
                             self.record_def(pattern.id, class_def);
                         }
-                        Some(definition @ def_struct(class_id))
-                                if structs.contains(&class_id) => {
+                        Some(definition @ def_struct(class_id)) => {
+                            assert!(self.structs.contains(&class_id));
                             self.record_def(pattern.id, definition);
                         }
                         Some(definition @ def_variant(_, variant_id))
-                                if structs.contains(&variant_id) => {
+                                if self.structs.contains(&variant_id) => {
                             self.record_def(pattern.id, definition);
                         }
                         result => {
@@ -4608,12 +4607,12 @@ pub impl Resolver {
         let search_result;
         match namespace {
             ValueNS => {
-                search_result = self.search_ribs(&mut self.value_ribs, ident,
+                search_result = self.search_ribs(self.value_ribs, ident,
                                                  span,
                                                  DontAllowCapturingSelf);
             }
             TypeNS => {
-                search_result = self.search_ribs(&mut self.type_ribs, ident,
+                search_result = self.search_ribs(self.type_ribs, ident,
                                                  span, AllowCapturingSelf);
             }
         }
@@ -4803,15 +4802,14 @@ pub impl Resolver {
 
             expr_struct(path, _, _) => {
                 // Resolve the path to the structure it goes to.
-                let structs: &mut HashSet<def_id> = &mut self.structs;
                 match self.resolve_path(path, TypeNS, false, visitor) {
                     Some(def_ty(class_id)) | Some(def_struct(class_id))
-                            if structs.contains(&class_id) => {
+                            if self.structs.contains(&class_id) => {
                         let class_def = def_struct(class_id);
                         self.record_def(expr.id, class_def);
                     }
                     Some(definition @ def_variant(_, class_id))
-                            if structs.contains(&class_id) => {
+                            if self.structs.contains(&class_id) => {
                         self.record_def(expr.id, definition);
                     }
                     _ => {
@@ -4827,17 +4825,19 @@ pub impl Resolver {
 
             expr_loop(_, Some(label)) => {
                 do self.with_label_rib {
-                    let this = &mut *self;
-                    let def_like = dl_def(def_label(expr.id));
-                    let rib = this.label_ribs[this.label_ribs.len() - 1];
-                    rib.bindings.insert(label, def_like);
+                    {
+                        let this = &mut *self;
+                        let def_like = dl_def(def_label(expr.id));
+                        let rib = this.label_ribs[this.label_ribs.len() - 1];
+                        rib.bindings.insert(label, def_like);
+                    }
 
                     visit_expr(expr, (), visitor);
                 }
             }
 
             expr_break(Some(label)) | expr_again(Some(label)) => {
-                match self.search_ribs(&mut self.label_ribs, label, expr.span,
+                match self.search_ribs(self.label_ribs, label, expr.span,
                                        DontAllowCapturingSelf) {
                     None =>
                         self.session.span_err(expr.span,
@@ -5248,7 +5248,7 @@ pub impl Resolver {
 
         debug!("Import resolutions:");
         for module_.import_resolutions.each |name, import_resolution| {
-            let mut value_repr;
+            let value_repr;
             match import_resolution.target_for_namespace(ValueNS) {
                 None => { value_repr = ~""; }
                 Some(_) => {
@@ -5257,7 +5257,7 @@ pub impl Resolver {
                 }
             }
 
-            let mut type_repr;
+            let type_repr;
             match import_resolution.target_for_namespace(TypeNS) {
                 None => { type_repr = ~""; }
                 Some(_) => {
diff --git a/src/librustc/middle/resolve_stage0.rs b/src/librustc/middle/resolve_stage0.rs
new file mode 100644
index 0000000000000..2773710ca98fc
--- /dev/null
+++ b/src/librustc/middle/resolve_stage0.rs
@@ -0,0 +1,5294 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use driver::session;
+use driver::session::Session;
+use metadata::csearch::{each_path, get_trait_method_def_ids};
+use metadata::csearch::get_method_name_and_self_ty;
+use metadata::csearch::get_static_methods_if_impl;
+use metadata::csearch::get_type_name_if_impl;
+use metadata::cstore::find_extern_mod_stmt_cnum;
+use metadata::decoder::{def_like, dl_def, dl_field, dl_impl};
+use middle::lang_items::LanguageItems;
+use middle::lint::{allow, level, unused_imports};
+use middle::lint::{get_lint_level, get_lint_settings_level};
+use middle::pat_util::pat_bindings;
+
+use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
+use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk};
+use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy};
+use syntax::ast::{crate, decl_item, def, def_arg, def_binding};
+use syntax::ast::{def_const, def_foreign_mod, def_fn, def_id, def_label};
+use syntax::ast::{def_local, def_mod, def_prim_ty, def_region, def_self};
+use syntax::ast::{def_self_ty, def_static_method, def_struct, def_ty};
+use syntax::ast::{def_ty_param, def_typaram_binder, def_trait};
+use syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
+use syntax::ast::{expr_binary, expr_break, expr_field};
+use syntax::ast::{expr_fn_block, expr_index, expr_method_call, expr_path};
+use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
+use syntax::ast::{def_upvar, def_use, def_variant, div, eq};
+use syntax::ast::{expr, expr_again, expr_assign_op};
+use syntax::ast::{expr_index, expr_loop};
+use syntax::ast::{expr_path, expr_struct, expr_unary, fn_decl};
+use syntax::ast::{foreign_item, foreign_item_const, foreign_item_fn, ge};
+use syntax::ast::Generics;
+use syntax::ast::{gt, ident, inherited, item, item_struct};
+use syntax::ast::{item_const, item_enum, item_fn, item_foreign_mod};
+use syntax::ast::{item_impl, item_mac, item_mod, item_trait, item_ty, le};
+use syntax::ast::{local, local_crate, lt, method, mul};
+use syntax::ast::{named_field, ne, neg, node_id, pat, pat_enum, pat_ident};
+use syntax::ast::{Path, pat_lit, pat_range, pat_struct};
+use syntax::ast::{prim_ty, private, provided};
+use syntax::ast::{public, required, rem, self_ty_, shl, shr, stmt_decl};
+use syntax::ast::{struct_field, struct_variant_kind};
+use syntax::ast::{sty_static, subtract, trait_ref, tuple_variant_kind, Ty};
+use syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i};
+use syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, TyParam, ty_path};
+use syntax::ast::{ty_str, ty_u, ty_u16, ty_u32, ty_u64, ty_u8, ty_uint};
+use syntax::ast::unnamed_field;
+use syntax::ast::{variant, view_item, view_item_extern_mod};
+use syntax::ast::{view_item_use, view_path_glob, view_path_list};
+use syntax::ast::{view_path_simple, anonymous, named, not};
+use syntax::ast::{unsafe_fn};
+use syntax::ast_util::{def_id_of_def, local_def};
+use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
+use syntax::ast_util::{Privacy, Public, Private};
+use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy};
+use syntax::attr::{attr_metas, contains_name, attrs_contains_name};
+use syntax::parse::token::ident_interner;
+use syntax::parse::token::special_idents;
+use syntax::print::pprust::path_to_str;
+use syntax::codemap::{span, dummy_sp};
+use syntax::visit::{default_visitor, mk_vt, Visitor, visit_block};
+use syntax::visit::{visit_crate, visit_expr, visit_expr_opt};
+use syntax::visit::{visit_foreign_item, visit_item};
+use syntax::visit::{visit_mod, visit_ty, vt};
+use syntax::opt_vec::OptVec;
+
+use core::option::Some;
+use core::str::each_split_str;
+use core::hashmap::{HashMap, HashSet};
+use core::util;
+
+// Definition mapping
+pub type DefMap = @mut HashMap<node_id,def>;
+
+pub struct binding_info {
+    span: span,
+    binding_mode: binding_mode,
+}
+
+// Map from the name in a pattern to its binding mode.
+pub type BindingMap = HashMap<ident,binding_info>;
+
+// Implementation resolution
+//
+// FIXME #4946: This kind of duplicates information kept in
+// ty::method. Maybe it should go away.
+
+pub struct MethodInfo {
+    did: def_id,
+    n_tps: uint,
+    ident: ident,
+    self_type: self_ty_
+}
+
+pub struct Impl {
+    did: def_id,
+    ident: ident,
+    methods: ~[@MethodInfo]
+}
+
+// Trait method resolution
+pub type TraitMap = HashMap<node_id,@mut ~[def_id]>;
+
+// This is the replacement export map. It maps a module to all of the exports
+// within.
+pub type ExportMap2 = @mut HashMap<node_id, ~[Export2]>;
+
+pub struct Export2 {
+    name: @~str,        // The name of the target.
+    def_id: def_id,     // The definition of the target.
+    reexport: bool,     // Whether this is a reexport.
+}
+
+#[deriving(Eq)]
+pub enum PatternBindingMode {
+    RefutableMode,
+    LocalIrrefutableMode,
+    ArgumentIrrefutableMode,
+}
+
+#[deriving(Eq)]
+pub enum Namespace {
+    TypeNS,
+    ValueNS
+}
+
+/// A NamespaceResult represents the result of resolving an import in
+/// a particular namespace. The result is either definitely-resolved,
+/// definitely- unresolved, or unknown.
+pub enum NamespaceResult {
+    /// Means that resolve hasn't gathered enough information yet to determine
+    /// whether the name is bound in this namespace. (That is, it hasn't
+    /// resolved all `use` directives yet.)
+    UnknownResult,
+    /// Means that resolve has determined that the name is definitely
+    /// not bound in the namespace.
+    UnboundResult,
+    /// Means that resolve has determined that the name is bound in the Module
+    /// argument, and specified by the NameBindings argument.
+    BoundResult(@mut Module, @mut NameBindings)
+}
+
+pub impl NamespaceResult {
+    fn is_unknown(&self) -> bool {
+        match *self {
+            UnknownResult => true,
+            _ => false
+        }
+    }
+}
+
+pub enum NameDefinition {
+    NoNameDefinition,           //< The name was unbound.
+    ChildNameDefinition(def),   //< The name identifies an immediate child.
+    ImportNameDefinition(def)   //< The name identifies an import.
+}
+
+#[deriving(Eq)]
+pub enum Mutability {
+    Mutable,
+    Immutable
+}
+
+pub enum SelfBinding {
+    NoSelfBinding,
+    HasSelfBinding(node_id, bool /* is implicit */)
+}
+
+pub type ResolveVisitor = vt<()>;
+
+/// Contains data for specific types of import directives.
+pub enum ImportDirectiveSubclass {
+    SingleImport(ident /* target */, ident /* source */),
+    GlobImport
+}
+
+/// The context that we thread through while building the reduced graph.
+pub enum ReducedGraphParent {
+    ModuleReducedGraphParent(@mut Module)
+}
+
+pub enum ResolveResult<T> {
+    Failed,         // Failed to resolve the name.
+    Indeterminate,  // Couldn't determine due to unresolved globs.
+    Success(T)      // Successfully resolved the import.
+}
+
+pub impl<T> ResolveResult<T> {
+    fn failed(&self) -> bool {
+        match *self { Failed => true, _ => false }
+    }
+    fn indeterminate(&self) -> bool {
+        match *self { Indeterminate => true, _ => false }
+    }
+}
+
+pub enum TypeParameters<'self> {
+    NoTypeParameters,                   //< No type parameters.
+    HasTypeParameters(&'self Generics,  //< Type parameters.
+                      node_id,          //< ID of the enclosing item
+
+                      // The index to start numbering the type parameters at.
+                      // This is zero if this is the outermost set of type
+                      // parameters, or equal to the number of outer type
+                      // parameters. For example, if we have:
+                      //
+                      //   impl I<T> {
+                      //     fn method<U>() { ... }
+                      //   }
+                      //
+                      // The index at the method site will be 1, because the
+                      // outer T had index 0.
+                      uint,
+
+                      // The kind of the rib used for type parameters.
+                      RibKind)
+}
+
+// The rib kind controls the translation of argument or local definitions
+// (`def_arg` or `def_local`) to upvars (`def_upvar`).
+
+pub enum RibKind {
+    // No translation needs to be applied.
+    NormalRibKind,
+
+    // We passed through a function scope at the given node ID. Translate
+    // upvars as appropriate.
+    FunctionRibKind(node_id /* func id */, node_id /* body id */),
+
+    // We passed through an impl or trait and are now in one of its
+    // methods. Allow references to ty params that that impl or trait
+    // binds. Disallow any other upvars (including other ty params that are
+    // upvars).
+              // parent;   method itself
+    MethodRibKind(node_id, MethodSort),
+
+    // We passed through a function *item* scope. Disallow upvars.
+    OpaqueFunctionRibKind,
+
+    // We're in a constant item. Can't refer to dynamic stuff.
+    ConstantItemRibKind
+}
+
+// Methods can be required or provided. Required methods only occur in traits.
+pub enum MethodSort {
+    Required,
+    Provided(node_id)
+}
+
+// The X-ray flag indicates that a context has the X-ray privilege, which
+// allows it to reference private names. Currently, this is used for the test
+// runner.
+//
+// FIXME #4947: The X-ray flag is kind of questionable in the first
+// place. It might be better to introduce an expr_xray_path instead.
+
+#[deriving(Eq)]
+pub enum XrayFlag {
+    NoXray,     //< Private items cannot be accessed.
+    Xray        //< Private items can be accessed.
+}
+
+pub enum UseLexicalScopeFlag {
+    DontUseLexicalScope,
+    UseLexicalScope
+}
+
+pub enum SearchThroughModulesFlag {
+    DontSearchThroughModules,
+    SearchThroughModules
+}
+
+pub enum ModulePrefixResult {
+    NoPrefixFound,
+    PrefixFound(@mut Module, uint)
+}
+
+#[deriving(Eq)]
+pub enum AllowCapturingSelfFlag {
+    AllowCapturingSelf,         //< The "self" definition can be captured.
+    DontAllowCapturingSelf,     //< The "self" definition cannot be captured.
+}
+
+#[deriving(Eq)]
+enum NameSearchType {
+    SearchItemsAndPublicImports,    //< Search items and public imports.
+    SearchItemsAndAllImports,       //< Search items and all imports.
+}
+
+pub enum BareIdentifierPatternResolution {
+    FoundStructOrEnumVariant(def),
+    FoundConst(def),
+    BareIdentifierPatternUnresolved
+}
+
+// Specifies how duplicates should be handled when adding a child item if
+// another item exists with the same name in some namespace.
+#[deriving(Eq)]
+pub enum DuplicateCheckingMode {
+    ForbidDuplicateModules,
+    ForbidDuplicateTypes,
+    ForbidDuplicateValues,
+    ForbidDuplicateTypesAndValues,
+    OverwriteDuplicates
+}
+
+// Returns the namespace associated with the given duplicate checking mode,
+// or fails for OverwriteDuplicates. This is used for error messages.
+pub fn namespace_for_duplicate_checking_mode(mode: DuplicateCheckingMode)
+                                          -> Namespace {
+    match mode {
+        ForbidDuplicateModules | ForbidDuplicateTypes |
+        ForbidDuplicateTypesAndValues => TypeNS,
+        ForbidDuplicateValues => ValueNS,
+        OverwriteDuplicates => fail!(~"OverwriteDuplicates has no namespace")
+    }
+}
+
+/// One local scope.
+pub struct Rib {
+    bindings: @mut HashMap<ident,def_like>,
+    kind: RibKind,
+}
+
+pub fn Rib(kind: RibKind) -> Rib {
+    Rib {
+        bindings: @mut HashMap::new(),
+        kind: kind
+    }
+}
+
+
+/// One import directive.
+pub struct ImportDirective {
+    privacy: Privacy,
+    module_path: ~[ident],
+    subclass: @ImportDirectiveSubclass,
+    span: span,
+}
+
+pub fn ImportDirective(privacy: Privacy,
+                       module_path: ~[ident],
+                       subclass: @ImportDirectiveSubclass,
+                       span: span)
+                    -> ImportDirective {
+    ImportDirective {
+        privacy: privacy,
+        module_path: module_path,
+        subclass: subclass,
+        span: span
+    }
+}
+
+/// The item that an import resolves to.
+pub struct Target {
+    target_module: @mut Module,
+    bindings: @mut NameBindings,
+}
+
+pub fn Target(target_module: @mut Module,
+              bindings: @mut NameBindings)
+           -> Target {
+    Target {
+        target_module: target_module,
+        bindings: bindings
+    }
+}
+
+/// An ImportResolution represents a particular `use` directive.
+pub struct ImportResolution {
+    /// The privacy of this `use` directive (whether it's `use` or
+    /// `pub use`.
+    privacy: Privacy,
+    span: span,
+
+    // The number of outstanding references to this name. When this reaches
+    // zero, outside modules can count on the targets being correct. Before
+    // then, all bets are off; future imports could override this name.
+
+    outstanding_references: uint,
+
+    /// The value that this `use` directive names, if there is one.
+    value_target: Option<Target>,
+    /// The type that this `use` directive names, if there is one.
+    type_target: Option<Target>,
+
+    /// There exists one state per import statement
+    state: @mut ImportState,
+}
+
+pub fn ImportResolution(privacy: Privacy,
+                        span: span,
+                        state: @mut ImportState) -> ImportResolution {
+    ImportResolution {
+        privacy: privacy,
+        span: span,
+        outstanding_references: 0,
+        value_target: None,
+        type_target: None,
+        state: state,
+    }
+}
+
+pub impl ImportResolution {
+    fn target_for_namespace(&self, namespace: Namespace) -> Option<Target> {
+        match namespace {
+            TypeNS      => return copy self.type_target,
+            ValueNS     => return copy self.value_target
+        }
+    }
+}
+
+pub struct ImportState {
+    used: bool,
+    warned: bool
+}
+
+pub fn ImportState() -> ImportState {
+    ImportState{ used: false, warned: false }
+}
+
+/// The link from a module up to its nearest parent node.
+pub enum ParentLink {
+    NoParentLink,
+    ModuleParentLink(@mut Module, ident),
+    BlockParentLink(@mut Module, node_id)
+}
+
+/// The type of module this is.
+pub enum ModuleKind {
+    NormalModuleKind,
+    ExternModuleKind,
+    TraitModuleKind,
+    AnonymousModuleKind,
+}
+
+/// One node in the tree of modules.
+pub struct Module {
+    parent_link: ParentLink,
+    def_id: Option<def_id>,
+    kind: ModuleKind,
+
+    children: @mut HashMap<ident, @mut NameBindings>,
+    imports: @mut ~[@ImportDirective],
+
+    // The external module children of this node that were declared with
+    // `extern mod`.
+    external_module_children: @mut HashMap<ident, @mut Module>,
+
+    // The anonymous children of this node. Anonymous children are pseudo-
+    // modules that are implicitly created around items contained within
+    // blocks.
+    //
+    // For example, if we have this:
+    //
+    //  fn f() {
+    //      fn g() {
+    //          ...
+    //      }
+    //  }
+    //
+    // There will be an anonymous module created around `g` with the ID of the
+    // entry block for `f`.
+
+    anonymous_children: @mut HashMap<node_id,@mut Module>,
+
+    // The status of resolving each import in this module.
+    import_resolutions: @mut HashMap<ident, @mut ImportResolution>,
+
+    // The number of unresolved globs that this module exports.
+    glob_count: uint,
+
+    // The index of the import we're resolving.
+    resolved_import_count: uint,
+}
+
+pub fn Module(parent_link: ParentLink,
+              def_id: Option<def_id>,
+              kind: ModuleKind)
+           -> Module {
+    Module {
+        parent_link: parent_link,
+        def_id: def_id,
+        kind: kind,
+        children: @mut HashMap::new(),
+        imports: @mut ~[],
+        external_module_children: @mut HashMap::new(),
+        anonymous_children: @mut HashMap::new(),
+        import_resolutions: @mut HashMap::new(),
+        glob_count: 0,
+        resolved_import_count: 0
+    }
+}
+
+pub impl Module {
+    fn all_imports_resolved(&self) -> bool {
+        let imports = &mut *self.imports;
+        return imports.len() == self.resolved_import_count;
+    }
+}
+
+// Records a possibly-private type definition.
+pub struct TypeNsDef {
+    privacy: Privacy,
+    module_def: Option<@mut Module>,
+    type_def: Option<def>
+}
+
+// Records a possibly-private value definition.
+pub struct ValueNsDef {
+    privacy: Privacy,
+    def: def,
+}
+
+// Records the definitions (at most one for each namespace) that a name is
+// bound to.
+pub struct NameBindings {
+    type_def: Option<TypeNsDef>,    //< Meaning in type namespace.
+    value_def: Option<ValueNsDef>,  //< Meaning in value namespace.
+
+    // For error reporting
+    // FIXME (#3783): Merge me into TypeNsDef and ValueNsDef.
+    type_span: Option<span>,
+    value_span: Option<span>,
+}
+
+pub impl NameBindings {
+    /// Creates a new module in this set of name bindings.
+    fn define_module(@mut self,
+                     privacy: Privacy,
+                     parent_link: ParentLink,
+                     def_id: Option<def_id>,
+                     kind: ModuleKind,
+                     sp: span) {
+        // Merges the module with the existing type def or creates a new one.
+        let module_ = @mut Module(parent_link, def_id, kind);
+        match self.type_def {
+            None => {
+                self.type_def = Some(TypeNsDef {
+                    privacy: privacy,
+                    module_def: Some(module_),
+                    type_def: None
+                });
+            }
+            Some(copy type_def) => {
+                self.type_def = Some(TypeNsDef {
+                    privacy: privacy,
+                    module_def: Some(module_),
+                    .. type_def
+                });
+            }
+        }
+        self.type_span = Some(sp);
+    }
+
+    /// Records a type definition.
+    fn define_type(@mut self, privacy: Privacy, def: def, sp: span) {
+        // Merges the type with the existing type def or creates a new one.
+        match self.type_def {
+            None => {
+                self.type_def = Some(TypeNsDef {
+                    privacy: privacy,
+                    module_def: None,
+                    type_def: Some(def)
+                });
+            }
+            Some(copy type_def) => {
+                self.type_def = Some(TypeNsDef {
+                    privacy: privacy,
+                    type_def: Some(def),
+                    .. type_def
+                });
+            }
+        }
+        self.type_span = Some(sp);
+    }
+
+    /// Records a value definition.
+    fn define_value(@mut self, privacy: Privacy, def: def, sp: span) {
+        self.value_def = Some(ValueNsDef { privacy: privacy, def: def });
+        self.value_span = Some(sp);
+    }
+
+    /// Returns the module node if applicable.
+    fn get_module_if_available(&self) -> Option<@mut Module> {
+        match self.type_def {
+            Some(ref type_def) => (*type_def).module_def,
+            None => None
+        }
+    }
+
+    /**
+     * Returns the module node. Fails if this node does not have a module
+     * definition.
+     */
+    fn get_module(@mut self) -> @mut Module {
+        match self.get_module_if_available() {
+            None => {
+                fail!(~"get_module called on a node with no module \
+                       definition!")
+            }
+            Some(module_def) => module_def
+        }
+    }
+
+    fn defined_in_namespace(&self, namespace: Namespace) -> bool {
+        match namespace {
+            TypeNS   => return self.type_def.is_some(),
+            ValueNS  => return self.value_def.is_some()
+        }
+    }
+
+    fn defined_in_public_namespace(&self, namespace: Namespace) -> bool {
+        match namespace {
+            TypeNS => match self.type_def {
+                Some(def) => def.privacy != Private,
+                None => false
+            },
+            ValueNS => match self.value_def {
+                Some(def) => def.privacy != Private,
+                None => false
+            }
+        }
+    }
+
+    fn def_for_namespace(&self, namespace: Namespace) -> Option<def> {
+        match namespace {
+            TypeNS => {
+                match self.type_def {
+                    None => None,
+                    Some(ref type_def) => {
+                        // FIXME (#3784): This is reallllly questionable.
+                        // Perhaps the right thing to do is to merge def_mod
+                        // and def_ty.
+                        match (*type_def).type_def {
+                            Some(type_def) => Some(type_def),
+                            None => {
+                                match (*type_def).module_def {
+                                    Some(module_def) => {
+                                        let module_def = &mut *module_def;
+                                        module_def.def_id.map(|def_id|
+                                            def_mod(*def_id))
+                                    }
+                                    None => None
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            ValueNS => {
+                match self.value_def {
+                    None => None,
+                    Some(value_def) => Some(value_def.def)
+                }
+            }
+        }
+    }
+
+    fn privacy_for_namespace(&self, namespace: Namespace) -> Option<Privacy> {
+        match namespace {
+            TypeNS => {
+                match self.type_def {
+                    None => None,
+                    Some(ref type_def) => Some((*type_def).privacy)
+                }
+            }
+            ValueNS => {
+                match self.value_def {
+                    None => None,
+                    Some(value_def) => Some(value_def.privacy)
+                }
+            }
+        }
+    }
+
+    fn span_for_namespace(&self, namespace: Namespace) -> Option<span> {
+        if self.defined_in_namespace(namespace) {
+            match namespace {
+                TypeNS  => self.type_span,
+                ValueNS => self.value_span,
+            }
+        } else {
+            None
+        }
+    }
+}
+
+pub fn NameBindings() -> NameBindings {
+    NameBindings {
+        type_def: None,
+        value_def: None,
+        type_span: None,
+        value_span: None
+    }
+}
+
+/// Interns the names of the primitive types.
+pub struct PrimitiveTypeTable {
+    primitive_types: HashMap<ident,prim_ty>,
+}
+
+pub impl PrimitiveTypeTable {
+    fn intern(&mut self, intr: @ident_interner, string: @~str,
+              primitive_type: prim_ty) {
+        let ident = intr.intern(string);
+        self.primitive_types.insert(ident, primitive_type);
+    }
+}
+
+pub fn PrimitiveTypeTable(intr: @ident_interner) -> PrimitiveTypeTable {
+    let mut table = PrimitiveTypeTable {
+        primitive_types: HashMap::new()
+    };
+
+    table.intern(intr, @~"bool",    ty_bool);
+    table.intern(intr, @~"char",    ty_int(ty_char));
+    table.intern(intr, @~"float",   ty_float(ty_f));
+    table.intern(intr, @~"f32",     ty_float(ty_f32));
+    table.intern(intr, @~"f64",     ty_float(ty_f64));
+    table.intern(intr, @~"int",     ty_int(ty_i));
+    table.intern(intr, @~"i8",      ty_int(ty_i8));
+    table.intern(intr, @~"i16",     ty_int(ty_i16));
+    table.intern(intr, @~"i32",     ty_int(ty_i32));
+    table.intern(intr, @~"i64",     ty_int(ty_i64));
+    table.intern(intr, @~"str",     ty_str);
+    table.intern(intr, @~"uint",    ty_uint(ty_u));
+    table.intern(intr, @~"u8",      ty_uint(ty_u8));
+    table.intern(intr, @~"u16",     ty_uint(ty_u16));
+    table.intern(intr, @~"u32",     ty_uint(ty_u32));
+    table.intern(intr, @~"u64",     ty_uint(ty_u64));
+
+    return table;
+}
+
+
+pub fn namespace_to_str(ns: Namespace) -> ~str {
+    match ns {
+        TypeNS  => ~"type",
+        ValueNS => ~"value",
+    }
+}
+
+pub fn Resolver(session: Session,
+                lang_items: LanguageItems,
+                crate: @crate)
+             -> Resolver {
+    let graph_root = @mut NameBindings();
+
+    graph_root.define_module(Public,
+                             NoParentLink,
+                             Some(def_id { crate: 0, node: 0 }),
+                             NormalModuleKind,
+                             crate.span);
+
+    let current_module = graph_root.get_module();
+
+    let self = Resolver {
+        session: @session,
+        lang_items: copy lang_items,
+        crate: crate,
+
+        // The outermost module has def ID 0; this is not reflected in the
+        // AST.
+
+        graph_root: graph_root,
+
+        trait_info: HashMap::new(),
+        structs: HashSet::new(),
+
+        unresolved_imports: 0,
+
+        current_module: current_module,
+        value_ribs: ~[],
+        type_ribs: ~[],
+        label_ribs: ~[],
+
+        xray_context: NoXray,
+        current_trait_refs: None,
+
+        self_ident: special_idents::self_,
+        type_self_ident: special_idents::type_self,
+
+        primitive_type_table: @PrimitiveTypeTable(session.
+                                                  parse_sess.interner),
+
+        namespaces: ~[ TypeNS, ValueNS ],
+
+        attr_main_fn: None,
+        main_fns: ~[],
+
+        start_fn: None,
+
+        def_map: @mut HashMap::new(),
+        export_map2: @mut HashMap::new(),
+        trait_map: HashMap::new(),
+
+        intr: session.intr()
+    };
+
+    self
+}
+
+/// The main resolver class.
+pub struct Resolver {
+    session: @Session,
+    lang_items: LanguageItems,
+    crate: @crate,
+
+    intr: @ident_interner,
+
+    graph_root: @mut NameBindings,
+
+    trait_info: HashMap<def_id, HashSet<ident>>,
+    structs: HashSet<def_id>,
+
+    // The number of imports that are currently unresolved.
+    unresolved_imports: uint,
+
+    // The module that represents the current item scope.
+    current_module: @mut Module,
+
+    // The current set of local scopes, for values.
+    // FIXME #4948: Reuse ribs to avoid allocation.
+    value_ribs: ~[@Rib],
+
+    // The current set of local scopes, for types.
+    type_ribs: ~[@Rib],
+
+    // The current set of local scopes, for labels.
+    label_ribs: ~[@Rib],
+
+    // Whether the current context is an X-ray context. An X-ray context is
+    // allowed to access private names of any module.
+    xray_context: XrayFlag,
+
+    // The trait that the current context can refer to.
+    current_trait_refs: Option<~[def_id]>,
+
+    // The ident for the keyword "self".
+    self_ident: ident,
+    // The ident for the non-keyword "Self".
+    type_self_ident: ident,
+
+    // The idents for the primitive types.
+    primitive_type_table: @PrimitiveTypeTable,
+
+    // The four namespaces.
+    namespaces: ~[Namespace],
+
+    // The function that has attribute named 'main'
+    attr_main_fn: Option<(node_id, span)>,
+
+    // The functions that could be main functions
+    main_fns: ~[Option<(node_id, span)>],
+
+    // The function that has the attribute 'start' on it
+    start_fn: Option<(node_id, span)>,
+
+    def_map: DefMap,
+    export_map2: ExportMap2,
+    trait_map: TraitMap,
+}
+
+pub impl Resolver {
+    /// The main name resolution procedure.
+    fn resolve(@mut self) {
+        self.build_reduced_graph();
+        self.session.abort_if_errors();
+
+        self.resolve_imports();
+        self.session.abort_if_errors();
+
+        self.record_exports();
+        self.session.abort_if_errors();
+
+        self.resolve_crate();
+        self.session.abort_if_errors();
+
+        self.check_duplicate_main();
+        self.check_for_unused_imports_if_necessary();
+    }
+
+    //
+    // Reduced graph building
+    //
+    // Here we build the "reduced graph": the graph of the module tree without
+    // any imports resolved.
+    //
+
+    /// Constructs the reduced graph for the entire crate.
+    fn build_reduced_graph(@mut self) {
+        let initial_parent =
+            ModuleReducedGraphParent(self.graph_root.get_module());
+        visit_crate(self.crate, initial_parent, mk_vt(@Visitor {
+            visit_item: |item, context, visitor|
+                self.build_reduced_graph_for_item(item, context, visitor),
+
+            visit_foreign_item: |foreign_item, context, visitor|
+                self.build_reduced_graph_for_foreign_item(foreign_item,
+                                                             context,
+                                                             visitor),
+
+            visit_view_item: |view_item, context, visitor|
+                self.build_reduced_graph_for_view_item(view_item,
+                                                          context,
+                                                          visitor),
+
+            visit_block: |block, context, visitor|
+                self.build_reduced_graph_for_block(block,
+                                                      context,
+                                                      visitor),
+
+            .. *default_visitor()
+        }));
+    }
+
+    /// Returns the current module tracked by the reduced graph parent.
+    fn get_module_from_parent(@mut self,
+                              reduced_graph_parent: ReducedGraphParent)
+                           -> @mut Module {
+        match reduced_graph_parent {
+            ModuleReducedGraphParent(module_) => {
+                return module_;
+            }
+        }
+    }
+
+    /**
+     * Adds a new child item to the module definition of the parent node and
+     * returns its corresponding name bindings as well as the current parent.
+     * Or, if we're inside a block, creates (or reuses) an anonymous module
+     * corresponding to the innermost block ID and returns the name bindings
+     * as well as the newly-created parent.
+     *
+     * If this node does not have a module definition and we are not inside
+     * a block, fails.
+     */
+    fn add_child(@mut self,
+                 name: ident,
+                 reduced_graph_parent: ReducedGraphParent,
+                 duplicate_checking_mode: DuplicateCheckingMode,
+                 // For printing errors
+                 sp: span)
+              -> (@mut NameBindings, ReducedGraphParent) {
+
+        // If this is the immediate descendant of a module, then we add the
+        // child name directly. Otherwise, we create or reuse an anonymous
+        // module and add the child to that.
+
+        let module_;
+        match reduced_graph_parent {
+            ModuleReducedGraphParent(parent_module) => {
+                module_ = parent_module;
+            }
+        }
+
+        // Add or reuse the child.
+        let new_parent = ModuleReducedGraphParent(module_);
+        match module_.children.find(&name) {
+            None => {
+                let child = @mut NameBindings();
+                module_.children.insert(name, child);
+                return (child, new_parent);
+            }
+            Some(child) => {
+                // Enforce the duplicate checking mode:
+                //
+                // * If we're requesting duplicate module checking, check that
+                //   there isn't a module in the module with the same name.
+                //
+                // * If we're requesting duplicate type checking, check that
+                //   there isn't a type in the module with the same name.
+                //
+                // * If we're requesting duplicate value checking, check that
+                //   there isn't a value in the module with the same name.
+                //
+                // * If we're requesting duplicate type checking and duplicate
+                //   value checking, check that there isn't a duplicate type
+                //   and a duplicate value with the same name.
+                //
+                // * If no duplicate checking was requested at all, do
+                //   nothing.
+
+                let mut is_duplicate = false;
+                match duplicate_checking_mode {
+                    ForbidDuplicateModules => {
+                        is_duplicate =
+                            child.get_module_if_available().is_some();
+                    }
+                    ForbidDuplicateTypes => {
+                        match child.def_for_namespace(TypeNS) {
+                            Some(def_mod(_)) | None => {}
+                            Some(_) => is_duplicate = true
+                        }
+                    }
+                    ForbidDuplicateValues => {
+                        is_duplicate = child.defined_in_namespace(ValueNS);
+                    }
+                    ForbidDuplicateTypesAndValues => {
+                        match child.def_for_namespace(TypeNS) {
+                            Some(def_mod(_)) | None => {}
+                            Some(_) => is_duplicate = true
+                        };
+                        if child.defined_in_namespace(ValueNS) {
+                            is_duplicate = true;
+                        }
+                    }
+                    OverwriteDuplicates => {}
+                }
+                if duplicate_checking_mode != OverwriteDuplicates &&
+                        is_duplicate {
+                    // Return an error here by looking up the namespace that
+                    // had the duplicate.
+                    let ns = namespace_for_duplicate_checking_mode(
+                        duplicate_checking_mode);
+                    self.session.span_err(sp,
+                        fmt!("duplicate definition of %s %s",
+                             namespace_to_str(ns),
+                             *self.session.str_of(name)));
+                    for child.span_for_namespace(ns).each |sp| {
+                        self.session.span_note(*sp,
+                             fmt!("first definition of %s %s here:",
+                                  namespace_to_str(ns),
+                                  *self.session.str_of(name)));
+                    }
+                }
+                return (*child, new_parent);
+            }
+        }
+    }
+
+    fn block_needs_anonymous_module(@mut self, block: &blk) -> bool {
+        // If the block has view items, we need an anonymous module.
+        if block.node.view_items.len() > 0 {
+            return true;
+        }
+
+        // Check each statement.
+        for block.node.stmts.each |statement| {
+            match statement.node {
+                stmt_decl(declaration, _) => {
+                    match declaration.node {
+                        decl_item(_) => {
+                            return true;
+                        }
+                        _ => {
+                            // Keep searching.
+                        }
+                    }
+                }
+                _ => {
+                    // Keep searching.
+                }
+            }
+        }
+
+        // If we found neither view items nor items, we don't need to create
+        // an anonymous module.
+
+        return false;
+    }
+
+    fn get_parent_link(@mut self,
+                       parent: ReducedGraphParent,
+                       name: ident)
+                    -> ParentLink {
+        match parent {
+            ModuleReducedGraphParent(module_) => {
+                return ModuleParentLink(module_, name);
+            }
+        }
+    }
+
+    /// Constructs the reduced graph for one item.
+    fn build_reduced_graph_for_item(@mut self,
+                                    item: @item,
+                                    parent: ReducedGraphParent,
+                                    visitor: vt<ReducedGraphParent>) {
+        let ident = item.ident;
+        let sp = item.span;
+        let privacy = visibility_to_privacy(item.vis);
+
+        match item.node {
+            item_mod(ref module_) => {
+                let (name_bindings, new_parent) =
+                    self.add_child(ident, parent, ForbidDuplicateModules, sp);
+
+                let parent_link = self.get_parent_link(new_parent, ident);
+                let def_id = def_id { crate: 0, node: item.id };
+                name_bindings.define_module(privacy,
+                                            parent_link,
+                                            Some(def_id),
+                                            NormalModuleKind,
+                                            sp);
+
+                let new_parent =
+                    ModuleReducedGraphParent(name_bindings.get_module());
+
+                visit_mod(module_, sp, item.id, new_parent, visitor);
+            }
+
+            item_foreign_mod(ref fm) => {
+                let new_parent = match fm.sort {
+                    named => {
+                        let (name_bindings, new_parent) =
+                            self.add_child(ident, parent,
+                                           ForbidDuplicateModules, sp);
+
+                        let parent_link = self.get_parent_link(new_parent,
+                                                               ident);
+                        let def_id = def_id { crate: 0, node: item.id };
+                        name_bindings.define_module(privacy,
+                                                    parent_link,
+                                                    Some(def_id),
+                                                    ExternModuleKind,
+                                                    sp);
+
+                        ModuleReducedGraphParent(name_bindings.get_module())
+                    }
+
+                    // For anon foreign mods, the contents just go in the
+                    // current scope
+                    anonymous => parent
+                };
+
+                visit_item(item, new_parent, visitor);
+            }
+
+            // These items live in the value namespace.
+            item_const(*) => {
+                let (name_bindings, _) =
+                    self.add_child(ident, parent, ForbidDuplicateValues, sp);
+
+                name_bindings.define_value
+                    (privacy, def_const(local_def(item.id)), sp);
+            }
+            item_fn(_, purity, _, _, _) => {
+              let (name_bindings, new_parent) =
+                self.add_child(ident, parent, ForbidDuplicateValues, sp);
+
+                let def = def_fn(local_def(item.id), purity);
+                name_bindings.define_value(privacy, def, sp);
+                visit_item(item, new_parent, visitor);
+            }
+
+            // These items live in the type namespace.
+            item_ty(*) => {
+                let (name_bindings, _) =
+                    self.add_child(ident, parent, ForbidDuplicateTypes, sp);
+
+                name_bindings.define_type
+                    (privacy, def_ty(local_def(item.id)), sp);
+            }
+
+            item_enum(ref enum_definition, _) => {
+                let (name_bindings, new_parent) =
+                    self.add_child(ident, parent, ForbidDuplicateTypes, sp);
+
+                name_bindings.define_type
+                    (privacy, def_ty(local_def(item.id)), sp);
+
+                for (*enum_definition).variants.each |variant| {
+                    self.build_reduced_graph_for_variant(variant,
+                        local_def(item.id),
+                        // inherited => privacy of the enum item
+                        variant_visibility_to_privacy(variant.node.vis,
+                                                      privacy == Public),
+                        new_parent,
+                        visitor);
+                }
+            }
+
+            // These items live in both the type and value namespaces.
+            item_struct(struct_def, _) => {
+                let (name_bindings, new_parent) =
+                    self.add_child(ident, parent, ForbidDuplicateTypes, sp);
+
+                name_bindings.define_type(
+                    privacy, def_ty(local_def(item.id)), sp);
+
+                // If this struct is tuple-like or enum-like, define a name
+                // in the value namespace.
+                match struct_def.ctor_id {
+                    None => {}
+                    Some(ctor_id) => {
+                        name_bindings.define_value(
+                            privacy,
+                            def_struct(local_def(ctor_id)),
+                            sp);
+                    }
+                }
+
+                // Record the def ID of this struct.
+                self.structs.insert(local_def(item.id));
+
+                visit_item(item, new_parent, visitor);
+            }
+
+            item_impl(_, trait_ref_opt, ty, ref methods) => {
+                // If this implements an anonymous trait and it has static
+                // methods, then add all the static methods within to a new
+                // module, if the type was defined within this module.
+                //
+                // FIXME (#3785): This is quite unsatisfactory. Perhaps we
+                // should modify anonymous traits to only be implementable in
+                // the same module that declared the type.
+
+                // Bail out early if there are no static methods.
+                let mut has_static_methods = false;
+                for methods.each |method| {
+                    match method.self_ty.node {
+                        sty_static => has_static_methods = true,
+                        _ => {}
+                    }
+                }
+
+                // If there are static methods, then create the module
+                // and add them.
+                match (trait_ref_opt, ty) {
+                    (None, @Ty { node: ty_path(path, _), _ }) if
+                            has_static_methods && path.idents.len() == 1 => {
+                        // Create the module.
+                        let name = path_to_ident(path);
+                        let (name_bindings, new_parent) =
+                            self.add_child(name,
+                                           parent,
+                                           ForbidDuplicateModules,
+                                           sp);
+
+                        let parent_link = self.get_parent_link(new_parent,
+                                                               ident);
+                        let def_id = local_def(item.id);
+                        name_bindings.define_module(Public,
+                                                    parent_link,
+                                                    Some(def_id),
+                                                    TraitModuleKind,
+                                                    sp);
+
+                        let new_parent = ModuleReducedGraphParent(
+                            name_bindings.get_module());
+
+                        // For each static method...
+                        for methods.each |method| {
+                            match method.self_ty.node {
+                                sty_static => {
+                                    // Add the static method to the
+                                    // module.
+                                    let ident = method.ident;
+                                    let (method_name_bindings, _) =
+                                        self.add_child(
+                                            ident,
+                                            new_parent,
+                                            ForbidDuplicateValues,
+                                            method.span);
+                                    let def = def_fn(local_def(method.id),
+                                                     method.purity);
+                                    method_name_bindings.define_value(
+                                        Public, def, method.span);
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+                    _ => {}
+                }
+
+                visit_item(item, parent, visitor);
+            }
+
+            item_trait(_, _, ref methods) => {
+                let (name_bindings, new_parent) =
+                    self.add_child(ident, parent, ForbidDuplicateTypes, sp);
+
+                // If the trait has static methods, then add all the static
+                // methods within to a new module.
+                //
+                // We only need to create the module if the trait has static
+                // methods, so check that first.
+                let mut has_static_methods = false;
+                for (*methods).each |method| {
+                    let ty_m = trait_method_to_ty_method(method);
+                    match ty_m.self_ty.node {
+                        sty_static => {
+                            has_static_methods = true;
+                            break;
+                        }
+                        _ => {}
+                    }
+                }
+
+                // Create the module if necessary.
+                let module_parent_opt;
+                if has_static_methods {
+                    let parent_link = self.get_parent_link(parent, ident);
+                    name_bindings.define_module(privacy,
+                                                parent_link,
+                                                Some(local_def(item.id)),
+                                                TraitModuleKind,
+                                                sp);
+                    module_parent_opt = Some(ModuleReducedGraphParent(
+                        name_bindings.get_module()));
+                } else {
+                    module_parent_opt = None;
+                }
+
+                // Add the names of all the methods to the trait info.
+                let mut method_names = HashSet::new();
+                for methods.each |method| {
+                    let ty_m = trait_method_to_ty_method(method);
+
+                    let ident = ty_m.ident;
+                    // Add it to the trait info if not static,
+                    // add it as a name in the trait module otherwise.
+                    match ty_m.self_ty.node {
+                        sty_static => {
+                            let def = def_static_method(
+                                local_def(ty_m.id),
+                                Some(local_def(item.id)),
+                                ty_m.purity);
+
+                            let (method_name_bindings, _) =
+                                self.add_child(ident,
+                                               module_parent_opt.get(),
+                                               ForbidDuplicateValues,
+                                               ty_m.span);
+                            method_name_bindings.define_value(Public,
+                                                              def,
+                                                              ty_m.span);
+                        }
+                        _ => {
+                            method_names.insert(ident);
+                        }
+                    }
+                }
+
+                let def_id = local_def(item.id);
+                self.trait_info.insert(def_id, method_names);
+
+                name_bindings.define_type(privacy, def_trait(def_id), sp);
+                visit_item(item, new_parent, visitor);
+            }
+
+            item_mac(*) => {
+                fail!(~"item macros unimplemented")
+            }
+        }
+    }
+
+    // Constructs the reduced graph for one variant. Variants exist in the
+    // type and/or value namespaces.
+    fn build_reduced_graph_for_variant(@mut self,
+                                       variant: &variant,
+                                       item_id: def_id,
+                                       parent_privacy: Privacy,
+                                       parent: ReducedGraphParent,
+                                       _visitor: vt<ReducedGraphParent>) {
+        let ident = variant.node.name;
+        let (child, _) = self.add_child(ident, parent, ForbidDuplicateValues,
+                                        variant.span);
+
+        let privacy;
+        match variant.node.vis {
+            public => privacy = Public,
+            private => privacy = Private,
+            inherited => privacy = parent_privacy
+        }
+
+        match variant.node.kind {
+            tuple_variant_kind(_) => {
+                child.define_value(privacy,
+                                   def_variant(item_id,
+                                               local_def(variant.node.id)),
+                                   variant.span);
+            }
+            struct_variant_kind(_) => {
+                child.define_type(privacy,
+                                  def_variant(item_id,
+                                              local_def(variant.node.id)),
+                                  variant.span);
+                self.structs.insert(local_def(variant.node.id));
+            }
+        }
+    }
+
+    /**
+     * Constructs the reduced graph for one 'view item'. View items consist
+     * of imports and use directives.
+     */
+    fn build_reduced_graph_for_view_item(@mut self,
+                                         view_item: @view_item,
+                                         parent: ReducedGraphParent,
+                                         _visitor: vt<ReducedGraphParent>) {
+        let privacy = visibility_to_privacy(view_item.vis);
+        match view_item.node {
+            view_item_use(ref view_paths) => {
+                for view_paths.each |view_path| {
+                    // Extract and intern the module part of the path. For
+                    // globs and lists, the path is found directly in the AST;
+                    // for simple paths we have to munge the path a little.
+
+                    let mut module_path = ~[];
+                    match view_path.node {
+                        view_path_simple(_, full_path, _) => {
+                            let path_len = full_path.idents.len();
+                            assert!(path_len != 0);
+
+                            for full_path.idents.eachi |i, ident| {
+                                if i != path_len - 1 {
+                                    module_path.push(*ident);
+                                }
+                            }
+                        }
+
+                        view_path_glob(module_ident_path, _) |
+                        view_path_list(module_ident_path, _, _) => {
+                            for module_ident_path.idents.each |ident| {
+                                module_path.push(*ident);
+                            }
+                        }
+                    }
+
+                    // Build up the import directives.
+                    let module_ = self.get_module_from_parent(parent);
+                    match view_path.node {
+                        view_path_simple(binding, full_path, _) => {
+                            let source_ident = *full_path.idents.last();
+                            let subclass = @SingleImport(binding,
+                                                         source_ident);
+                            self.build_import_directive(privacy,
+                                                        module_,
+                                                        module_path,
+                                                        subclass,
+                                                        view_path.span);
+                        }
+                        view_path_list(_, ref source_idents, _) => {
+                            for source_idents.each |source_ident| {
+                                let name = source_ident.node.name;
+                                let subclass = @SingleImport(name, name);
+                                self.build_import_directive(privacy,
+                                                            module_,
+                                                            copy module_path,
+                                                            subclass,
+                                                            source_ident.span);
+                            }
+                        }
+                        view_path_glob(_, _) => {
+                            self.build_import_directive(privacy,
+                                                        module_,
+                                                        module_path,
+                                                        @GlobImport,
+                                                        view_path.span);
+                        }
+                    }
+                }
+            }
+
+            view_item_extern_mod(name, _, node_id) => {
+                match find_extern_mod_stmt_cnum(self.session.cstore,
+                                                node_id) {
+                    Some(crate_id) => {
+                        let def_id = def_id { crate: crate_id, node: 0 };
+                        let parent_link = ModuleParentLink
+                            (self.get_module_from_parent(parent), name);
+                        let external_module = @mut Module(parent_link,
+                                                          Some(def_id),
+                                                          NormalModuleKind);
+
+                        parent.external_module_children.insert(
+                            name,
+                            external_module);
+
+                        self.build_reduced_graph_for_external_crate(
+                            external_module);
+                    }
+                    None => {}  // Ignore.
+                }
+            }
+        }
+    }
+
+    /// Constructs the reduced graph for one foreign item.
+    fn build_reduced_graph_for_foreign_item(@mut self,
+                                            foreign_item: @foreign_item,
+                                            parent: ReducedGraphParent,
+                                            visitor:
+                                                vt<ReducedGraphParent>) {
+        let name = foreign_item.ident;
+        let (name_bindings, new_parent) =
+            self.add_child(name, parent, ForbidDuplicateValues,
+                           foreign_item.span);
+
+        match foreign_item.node {
+            foreign_item_fn(_, _, ref generics) => {
+                let def = def_fn(local_def(foreign_item.id), unsafe_fn);
+                name_bindings.define_value(Public, def, foreign_item.span);
+
+                do self.with_type_parameter_rib(
+                    HasTypeParameters(
+                        generics, foreign_item.id, 0, NormalRibKind))
+                {
+                    visit_foreign_item(foreign_item, new_parent, visitor);
+                }
+            }
+            foreign_item_const(*) => {
+                let def = def_const(local_def(foreign_item.id));
+                name_bindings.define_value(Public, def, foreign_item.span);
+
+                visit_foreign_item(foreign_item, new_parent, visitor);
+            }
+        }
+    }
+
+    fn build_reduced_graph_for_block(@mut self,
+                                     block: &blk,
+                                     parent: ReducedGraphParent,
+                                     visitor: vt<ReducedGraphParent>) {
+        let new_parent;
+        if self.block_needs_anonymous_module(block) {
+            let block_id = block.node.id;
+
+            debug!("(building reduced graph for block) creating a new \
+                    anonymous module for block %d",
+                   block_id);
+
+            let parent_module = self.get_module_from_parent(parent);
+            let new_module = @mut Module(
+                BlockParentLink(parent_module, block_id),
+                None,
+                AnonymousModuleKind);
+            parent_module.anonymous_children.insert(block_id, new_module);
+            new_parent = ModuleReducedGraphParent(new_module);
+        } else {
+            new_parent = parent;
+        }
+
+        visit_block(block, new_parent, visitor);
+    }
+
+    fn handle_external_def(@mut self,
+                           def: def,
+                           modules: &mut HashMap<def_id, @mut Module>,
+                           child_name_bindings: @mut NameBindings,
+                           final_ident: &str,
+                           ident: ident,
+                           new_parent: ReducedGraphParent) {
+        match def {
+          def_mod(def_id) | def_foreign_mod(def_id) => {
+            match child_name_bindings.type_def {
+              Some(TypeNsDef { module_def: Some(copy module_def), _ }) => {
+                debug!("(building reduced graph for external crate) \
+                        already created module");
+                module_def.def_id = Some(def_id);
+                modules.insert(def_id, module_def);
+              }
+              Some(_) | None => {
+                debug!("(building reduced graph for \
+                        external crate) building module \
+                        %s", final_ident);
+                let parent_link = self.get_parent_link(new_parent, ident);
+
+                // FIXME (#5074): this should be a match on find
+                if !modules.contains_key(&def_id) {
+                    child_name_bindings.define_module(Public,
+                                                      parent_link,
+                                                      Some(def_id),
+                                                      NormalModuleKind,
+                                                      dummy_sp());
+                    modules.insert(def_id,
+                                   child_name_bindings.get_module());
+                } else {
+                    let existing_module = *modules.get(&def_id);
+                    // Create an import resolution to
+                    // avoid creating cycles in the
+                    // module graph.
+
+                    let resolution =
+                        @mut ImportResolution(Public,
+                                              dummy_sp(),
+                                              @mut ImportState());
+                    resolution.outstanding_references = 0;
+
+                    match existing_module.parent_link {
+                      NoParentLink |
+                      BlockParentLink(*) => {
+                        fail!(~"can't happen");
+                      }
+                      ModuleParentLink(parent_module, ident) => {
+                        let name_bindings = parent_module.children.get(
+                            &ident);
+                        resolution.type_target =
+                            Some(Target(parent_module, *name_bindings));
+                      }
+                    }
+
+                    debug!("(building reduced graph for external crate) \
+                            ... creating import resolution");
+
+                    new_parent.import_resolutions.insert(ident, resolution);
+                }
+              }
+            }
+          }
+          def_fn(*) | def_static_method(*) | def_const(*) |
+          def_variant(*) => {
+            debug!("(building reduced graph for external \
+                    crate) building value %s", final_ident);
+            child_name_bindings.define_value(Public, def, dummy_sp());
+          }
+          def_trait(def_id) => {
+              debug!("(building reduced graph for external \
+                      crate) building type %s", final_ident);
+
+              // If this is a trait, add all the method names
+              // to the trait info.
+
+              let method_def_ids = get_trait_method_def_ids(self.session.cstore,
+                                                            def_id);
+              let mut interned_method_names = HashSet::new();
+              for method_def_ids.each |&method_def_id| {
+                  let (method_name, self_ty) =
+                      get_method_name_and_self_ty(self.session.cstore,
+                                                  method_def_id);
+
+                  debug!("(building reduced graph for \
+                          external crate) ... adding \
+                          trait method '%s'",
+                         *self.session.str_of(method_name));
+
+                  // Add it to the trait info if not static.
+                  if self_ty != sty_static {
+                      interned_method_names.insert(method_name);
+                  }
+              }
+              self.trait_info.insert(def_id, interned_method_names);
+
+              child_name_bindings.define_type(Public, def, dummy_sp());
+          }
+          def_ty(_) => {
+              debug!("(building reduced graph for external \
+                      crate) building type %s", final_ident);
+
+              child_name_bindings.define_type(Public, def, dummy_sp());
+          }
+          def_struct(def_id) => {
+            debug!("(building reduced graph for external \
+                    crate) building type %s",
+                   final_ident);
+            child_name_bindings.define_type(Public, def, dummy_sp());
+            self.structs.insert(def_id);
+          }
+          def_self(*) | def_arg(*) | def_local(*) |
+          def_prim_ty(*) | def_ty_param(*) | def_binding(*) |
+          def_use(*) | def_upvar(*) | def_region(*) |
+          def_typaram_binder(*) | def_label(*) | def_self_ty(*) => {
+            fail!(fmt!("didn't expect `%?`", def));
+          }
+        }
+    }
+
+    /**
+     * Builds the reduced graph rooted at the 'use' directive for an external
+     * crate.
+     */
+    fn build_reduced_graph_for_external_crate(@mut self, root: @mut Module) {
+        let mut modules = HashMap::new();
+
+        // Create all the items reachable by paths.
+        for each_path(self.session.cstore, root.def_id.get().crate)
+                |path_string, def_like| {
+
+            debug!("(building reduced graph for external crate) found path \
+                        entry: %s (%?)",
+                    path_string, def_like);
+
+            let mut pieces = ~[];
+            for each_split_str(path_string, "::") |s| { pieces.push(s.to_owned()) }
+            let final_ident_str = pieces.pop();
+            let final_ident = self.session.ident_of(final_ident_str);
+
+            // Find the module we need, creating modules along the way if we
+            // need to.
+
+            let mut current_module = root;
+            for pieces.each |ident_str| {
+                let ident = self.session.ident_of(/*bad*/copy *ident_str);
+                // Create or reuse a graph node for the child.
+                let (child_name_bindings, new_parent) =
+                    self.add_child(ident,
+                                   ModuleReducedGraphParent(current_module),
+                                   OverwriteDuplicates,
+                                   dummy_sp());
+
+                // Define or reuse the module node.
+                match child_name_bindings.type_def {
+                    None => {
+                        debug!("(building reduced graph for external crate) \
+                                autovivifying missing type def %s",
+                                *ident_str);
+                        let parent_link = self.get_parent_link(new_parent,
+                                                               ident);
+                        child_name_bindings.define_module(Public,
+                                                          parent_link,
+                                                          None,
+                                                          NormalModuleKind,
+                                                          dummy_sp());
+                    }
+                    Some(copy type_ns_def)
+                            if type_ns_def.module_def.is_none() => {
+                        debug!("(building reduced graph for external crate) \
+                                autovivifying missing module def %s",
+                                *ident_str);
+                        let parent_link = self.get_parent_link(new_parent,
+                                                               ident);
+                        child_name_bindings.define_module(Public,
+                                                          parent_link,
+                                                          None,
+                                                          NormalModuleKind,
+                                                          dummy_sp());
+                    }
+                    _ => {} // Fall through.
+                }
+
+                current_module = child_name_bindings.get_module();
+            }
+
+            match def_like {
+                dl_def(def) => {
+                    // Add the new child item.
+                    let (child_name_bindings, new_parent) =
+                        self.add_child(final_ident,
+                                       ModuleReducedGraphParent(
+                                            current_module),
+                                       OverwriteDuplicates,
+                                       dummy_sp());
+
+                    self.handle_external_def(def,
+                                             &mut modules,
+                                             child_name_bindings,
+                                             *self.session.str_of(
+                                                 final_ident),
+                                             final_ident,
+                                             new_parent);
+                }
+                dl_impl(def) => {
+                    // We only process static methods of impls here.
+                    match get_type_name_if_impl(self.session.cstore, def) {
+                        None => {}
+                        Some(final_ident) => {
+                            let static_methods_opt =
+                                get_static_methods_if_impl(
+                                    self.session.cstore, def);
+                            match static_methods_opt {
+                                Some(ref static_methods) if
+                                    static_methods.len() >= 1 => {
+                                    debug!("(building reduced graph for \
+                                            external crate) processing \
+                                            static methods for type name %s",
+                                            *self.session.str_of(
+                                                final_ident));
+
+                                    let (child_name_bindings, new_parent) =
+                                        self.add_child(final_ident,
+                                            ModuleReducedGraphParent(
+                                                            current_module),
+                                            OverwriteDuplicates,
+                                            dummy_sp());
+
+                                    // Process the static methods. First,
+                                    // create the module.
+                                    let type_module;
+                                    match child_name_bindings.type_def {
+                                        Some(TypeNsDef {
+                                            module_def: Some(copy module_def),
+                                            _
+                                        }) => {
+                                            // We already have a module. This
+                                            // is OK.
+                                            type_module = module_def;
+                                        }
+                                        Some(_) | None => {
+                                            let parent_link =
+                                                self.get_parent_link(
+                                                    new_parent, final_ident);
+                                            child_name_bindings.define_module(
+                                                Public,
+                                                parent_link,
+                                                Some(def),
+                                                NormalModuleKind,
+                                                dummy_sp());
+                                            type_module =
+                                                child_name_bindings.
+                                                    get_module();
+                                        }
+                                    }
+
+                                    // Add each static method to the module.
+                                    let new_parent = ModuleReducedGraphParent(
+                                        type_module);
+                                    for static_methods.each
+                                            |static_method_info| {
+                                        let ident = static_method_info.ident;
+                                        debug!("(building reduced graph for \
+                                                 external crate) creating \
+                                                 static method '%s'",
+                                               *self.session.str_of(ident));
+
+                                        let (method_name_bindings, _) =
+                                            self.add_child(
+                                                ident,
+                                                new_parent,
+                                                OverwriteDuplicates,
+                                                dummy_sp());
+                                        let def = def_fn(
+                                            static_method_info.def_id,
+                                            static_method_info.purity);
+                                        method_name_bindings.define_value(
+                                            Public, def, dummy_sp());
+                                    }
+                                }
+
+                                // Otherwise, do nothing.
+                                Some(_) | None => {}
+                            }
+                        }
+                    }
+                }
+                dl_field => {
+                    debug!("(building reduced graph for external crate) \
+                            ignoring field");
+                }
+            }
+        }
+    }
+
+    /// Creates and adds an import directive to the given module.
+    fn build_import_directive(@mut self,
+                              privacy: Privacy,
+                              module_: @mut Module,
+                              module_path: ~[ident],
+                              subclass: @ImportDirectiveSubclass,
+                              span: span) {
+        let directive = @ImportDirective(privacy, module_path,
+                                         subclass, span);
+        module_.imports.push(directive);
+
+        // Bump the reference count on the name. Or, if this is a glob, set
+        // the appropriate flag.
+
+        match *subclass {
+            SingleImport(target, _) => {
+                debug!("(building import directive) building import \
+                        directive: privacy %? %s::%s",
+                       privacy,
+                       self.idents_to_str(directive.module_path),
+                       *self.session.str_of(target));
+
+                match module_.import_resolutions.find(&target) {
+                    Some(resolution) => {
+                        debug!("(building import directive) bumping \
+                                reference");
+                        resolution.outstanding_references += 1;
+                    }
+                    None => {
+                        debug!("(building import directive) creating new");
+                        let state = @mut ImportState();
+                        let resolution = @mut ImportResolution(privacy,
+                                                               span,
+                                                               state);
+                        let name = self.idents_to_str(directive.module_path);
+                        // Don't warn about unused intrinsics because they're
+                        // automatically appended to all files
+                        if name == ~"intrinsic::rusti" {
+                            resolution.state.warned = true;
+                        }
+                        resolution.outstanding_references = 1;
+                        module_.import_resolutions.insert(target, resolution);
+                    }
+                }
+            }
+            GlobImport => {
+                // Set the glob flag. This tells us that we don't know the
+                // module's exports ahead of time.
+
+                module_.glob_count += 1;
+            }
+        }
+
+        self.unresolved_imports += 1;
+    }
+
+    // Import resolution
+    //
+    // This is a fixed-point algorithm. We resolve imports until our efforts
+    // are stymied by an unresolved import; then we bail out of the current
+    // module and continue. We terminate successfully once no more imports
+    // remain or unsuccessfully when no forward progress in resolving imports
+    // is made.
+
+    /**
+     * Resolves all imports for the crate. This method performs the fixed-
+     * point iteration.
+     */
+    fn resolve_imports(@mut self) {
+        let mut i = 0;
+        let mut prev_unresolved_imports = 0;
+        loop {
+            debug!("(resolving imports) iteration %u, %u imports left",
+                   i, self.unresolved_imports);
+
+            let module_root = self.graph_root.get_module();
+            self.resolve_imports_for_module_subtree(module_root);
+
+            if self.unresolved_imports == 0 {
+                debug!("(resolving imports) success");
+                break;
+            }
+
+            if self.unresolved_imports == prev_unresolved_imports {
+                self.session.err(~"failed to resolve imports");
+                self.report_unresolved_imports(module_root);
+                break;
+            }
+
+            i += 1;
+            prev_unresolved_imports = self.unresolved_imports;
+        }
+    }
+
+    /// Attempts to resolve imports for the given module and all of its
+    /// submodules.
+    fn resolve_imports_for_module_subtree(@mut self, module_: @mut Module) {
+        debug!("(resolving imports for module subtree) resolving %s",
+               self.module_to_str(module_));
+        self.resolve_imports_for_module(module_);
+
+        for module_.children.each_value |&child_node| {
+            match child_node.get_module_if_available() {
+                None => {
+                    // Nothing to do.
+                }
+                Some(child_module) => {
+                    self.resolve_imports_for_module_subtree(child_module);
+                }
+            }
+        }
+
+        for module_.anonymous_children.each_value |&child_module| {
+            self.resolve_imports_for_module_subtree(child_module);
+        }
+    }
+
+    /// Attempts to resolve imports for the given module only.
+    fn resolve_imports_for_module(@mut self, module: @mut Module) {
+        if module.all_imports_resolved() {
+            debug!("(resolving imports for module) all imports resolved for \
+                   %s",
+                   self.module_to_str(module));
+            return;
+        }
+
+        let imports = &mut *module.imports;
+        let import_count = imports.len();
+        while module.resolved_import_count < import_count {
+            let import_index = module.resolved_import_count;
+            let import_directive = imports[import_index];
+            match self.resolve_import_for_module(module, import_directive) {
+                Failed => {
+                    // We presumably emitted an error. Continue.
+                    let msg = fmt!("failed to resolve import: %s",
+                                   *self.import_path_to_str(
+                                       import_directive.module_path,
+                                       *import_directive.subclass));
+                    self.session.span_err(import_directive.span, msg);
+                }
+                Indeterminate => {
+                    // Bail out. We'll come around next time.
+                    break;
+                }
+                Success(()) => {
+                    // Good. Continue.
+                }
+            }
+
+            module.resolved_import_count += 1;
+        }
+    }
+
+    fn idents_to_str(@mut self, idents: &[ident]) -> ~str {
+        let mut first = true;
+        let mut result = ~"";
+        for idents.each |ident| {
+            if first { first = false; } else { result += "::" };
+            result += *self.session.str_of(*ident);
+        };
+        return result;
+    }
+
+    fn import_directive_subclass_to_str(@mut self,
+                                        subclass: ImportDirectiveSubclass)
+                                     -> @~str {
+        match subclass {
+            SingleImport(_target, source) => self.session.str_of(source),
+            GlobImport => @~"*"
+        }
+    }
+
+    fn import_path_to_str(@mut self,
+                          idents: &[ident],
+                          subclass: ImportDirectiveSubclass)
+                       -> @~str {
+        if idents.is_empty() {
+            self.import_directive_subclass_to_str(subclass)
+        } else {
+            @fmt!("%s::%s",
+                 self.idents_to_str(idents),
+                 *self.import_directive_subclass_to_str(subclass))
+        }
+    }
+
+    /// Attempts to resolve the given import. The return value indicates
+    /// failure if we're certain the name does not exist, indeterminate if we
+    /// don't know whether the name exists at the moment due to other
+    /// currently-unresolved imports, or success if we know the name exists.
+    /// If successful, the resolved bindings are written into the module.
+    fn resolve_import_for_module(@mut self, module_: @mut Module,
+                                 import_directive: @ImportDirective)
+                              -> ResolveResult<()> {
+        let mut resolution_result = Failed;
+        let module_path = &import_directive.module_path;
+
+        debug!("(resolving import for module) resolving import `%s::...` in \
+                `%s`",
+               self.idents_to_str(*module_path),
+               self.module_to_str(module_));
+
+        // First, resolve the module path for the directive, if necessary.
+        let containing_module = if module_path.len() == 0 {
+            // Use the crate root.
+            Some(self.graph_root.get_module())
+        } else {
+            match self.resolve_module_path_for_import(module_,
+                                                      *module_path,
+                                                      DontUseLexicalScope,
+                                                      import_directive.span) {
+
+                Failed => None,
+                Indeterminate => {
+                    resolution_result = Indeterminate;
+                    None
+                }
+                Success(containing_module) => Some(containing_module),
+            }
+        };
+
+        match containing_module {
+            None => {}
+            Some(containing_module) => {
+                // We found the module that the target is contained
+                // within. Attempt to resolve the import within it.
+
+                match *import_directive.subclass {
+                    SingleImport(target, source) => {
+                        resolution_result =
+                            self.resolve_single_import(module_,
+                                                       containing_module,
+                                                       target,
+                                                       source);
+                    }
+                    GlobImport => {
+                        let span = import_directive.span;
+                        let privacy = import_directive.privacy;
+                        resolution_result =
+                            self.resolve_glob_import(privacy,
+                                                     module_,
+                                                     containing_module,
+                                                     span);
+                    }
+                }
+            }
+        }
+
+        // Decrement the count of unresolved imports.
+        match resolution_result {
+            Success(()) => {
+                assert!(self.unresolved_imports >= 1);
+                self.unresolved_imports -= 1;
+            }
+            _ => {
+                // Nothing to do here; just return the error.
+            }
+        }
+
+        // Decrement the count of unresolved globs if necessary. But only if
+        // the resolution result is indeterminate -- otherwise we'll stop
+        // processing imports here. (See the loop in
+        // resolve_imports_for_module.)
+
+        if !resolution_result.indeterminate() {
+            match *import_directive.subclass {
+                GlobImport => {
+                    assert!(module_.glob_count >= 1);
+                    module_.glob_count -= 1;
+                }
+                SingleImport(*) => {
+                    // Ignore.
+                }
+            }
+        }
+
+        return resolution_result;
+    }
+
+    fn create_name_bindings_from_module(module: @mut Module) -> NameBindings {
+        NameBindings {
+            type_def: Some(TypeNsDef {
+                privacy: Public,
+                module_def: Some(module),
+                type_def: None,
+            }),
+            value_def: None,
+            type_span: None,
+            value_span: None,
+        }
+    }
+
+    fn resolve_single_import(@mut self,
+                             module_: @mut Module,
+                             containing_module: @mut Module,
+                             target: ident,
+                             source: ident)
+                          -> ResolveResult<()> {
+        debug!("(resolving single import) resolving `%s` = `%s::%s` from \
+                `%s`",
+               *self.session.str_of(target),
+               self.module_to_str(containing_module),
+               *self.session.str_of(source),
+               self.module_to_str(module_));
+
+        // We need to resolve both namespaces for this to succeed.
+        //
+        // FIXME #4949: See if there's some way of handling namespaces in
+        // a more generic way. We have two of them; it seems worth
+        // doing...
+
+        let mut value_result = UnknownResult;
+        let mut type_result = UnknownResult;
+
+        // Search for direct children of the containing module.
+        match containing_module.children.find(&source) {
+            None => {
+                // Continue.
+            }
+            Some(child_name_bindings) => {
+                if child_name_bindings.defined_in_namespace(ValueNS) {
+                    value_result = BoundResult(containing_module,
+                                               *child_name_bindings);
+                }
+                if child_name_bindings.defined_in_namespace(TypeNS) {
+                    type_result = BoundResult(containing_module,
+                                              *child_name_bindings);
+                }
+            }
+        }
+
+        // Unless we managed to find a result in both namespaces (unlikely),
+        // search imports as well.
+        match (value_result, type_result) {
+            (BoundResult(*), BoundResult(*)) => {
+                // Continue.
+            }
+            _ => {
+                // If there is an unresolved glob at this point in the
+                // containing module, bail out. We don't know enough to be
+                // able to resolve this import.
+
+                if containing_module.glob_count > 0 {
+                    debug!("(resolving single import) unresolved glob; \
+                            bailing out");
+                    return Indeterminate;
+                }
+
+                // Now search the exported imports within the containing
+                // module.
+
+                match containing_module.import_resolutions.find(&source) {
+                    None => {
+                        // The containing module definitely doesn't have an
+                        // exported import with the name in question. We can
+                        // therefore accurately report that the names are
+                        // unbound.
+
+                        if value_result.is_unknown() {
+                            value_result = UnboundResult;
+                        }
+                        if type_result.is_unknown() {
+                            type_result = UnboundResult;
+                        }
+                    }
+                    Some(import_resolution)
+                            if import_resolution.outstanding_references
+                                == 0 => {
+
+                        fn get_binding(import_resolution:
+                                          @mut ImportResolution,
+                                       namespace: Namespace)
+                                    -> NamespaceResult {
+
+                            // Import resolutions must be declared with "pub"
+                            // in order to be exported.
+                            if import_resolution.privacy == Private {
+                                return UnboundResult;
+                            }
+
+                            match (*import_resolution).
+                                    target_for_namespace(namespace) {
+                                None => {
+                                    return UnboundResult;
+                                }
+                                Some(target) => {
+                                    import_resolution.state.used = true;
+                                    return BoundResult(target.target_module,
+                                                    target.bindings);
+                                }
+                            }
+                        }
+
+                        // The name is an import which has been fully
+                        // resolved. We can, therefore, just follow it.
+                        if value_result.is_unknown() {
+                            value_result = get_binding(*import_resolution,
+                                                       ValueNS);
+                        }
+                        if type_result.is_unknown() {
+                            type_result = get_binding(*import_resolution,
+                                                      TypeNS);
+                        }
+                    }
+                    Some(_) => {
+                        // The import is unresolved. Bail out.
+                        debug!("(resolving single import) unresolved import; \
+                                bailing out");
+                        return Indeterminate;
+                    }
+                }
+            }
+        }
+
+        // If we didn't find a result in the type namespace, search the
+        // external modules.
+        match type_result {
+            BoundResult(*) => {}
+            _ => {
+                match containing_module.external_module_children
+                                       .find(&source) {
+                    None => {} // Continue.
+                    Some(module) => {
+                        let name_bindings =
+                            @mut Resolver::create_name_bindings_from_module(
+                                *module);
+                        type_result = BoundResult(containing_module,
+                                                  name_bindings);
+                    }
+                }
+            }
+        }
+
+        // We've successfully resolved the import. Write the results in.
+        assert!(module_.import_resolutions.contains_key(&target));
+        let import_resolution = module_.import_resolutions.get(&target);
+
+        match value_result {
+            BoundResult(target_module, name_bindings) => {
+                import_resolution.value_target =
+                    Some(Target(target_module, name_bindings));
+            }
+            UnboundResult => { /* Continue. */ }
+            UnknownResult => {
+                fail!(~"value result should be known at this point");
+            }
+        }
+        match type_result {
+            BoundResult(target_module, name_bindings) => {
+                import_resolution.type_target =
+                    Some(Target(target_module, name_bindings));
+            }
+            UnboundResult => { /* Continue. */ }
+            UnknownResult => {
+                fail!(~"type result should be known at this point");
+            }
+        }
+
+        let i = import_resolution;
+        match (i.value_target, i.type_target) {
+            // If this name wasn't found in either namespace, it's definitely
+            // unresolved.
+            (None, None) => { return Failed; }
+            // If it's private, it's also unresolved.
+            (Some(t), None) | (None, Some(t)) => {
+                let bindings = &mut *t.bindings;
+                match bindings.type_def {
+                    Some(ref type_def) => {
+                        if type_def.privacy == Private {
+                            return Failed;
+                        }
+                    }
+                    _ => ()
+                }
+                match bindings.value_def {
+                    Some(ref value_def) => {
+                        if value_def.privacy == Private {
+                            return Failed;
+                        }
+                    }
+                    _ => ()
+                }
+            }
+            // It's also an error if there's both a type and a value with this
+            // name, but both are private
+            (Some(val), Some(ty)) => {
+                match (val.bindings.value_def, ty.bindings.value_def) {
+                    (Some(ref value_def), Some(ref type_def)) =>
+                        if value_def.privacy == Private
+                            && type_def.privacy == Private {
+                            return Failed;
+                        },
+                    _ => ()
+                }
+            }
+        }
+
+        assert!(import_resolution.outstanding_references >= 1);
+        import_resolution.outstanding_references -= 1;
+
+        debug!("(resolving single import) successfully resolved import");
+        return Success(());
+    }
+
+    // Resolves a glob import. Note that this function cannot fail; it either
+    // succeeds or bails out (as importing * from an empty module or a module
+    // that exports nothing is valid).
+    fn resolve_glob_import(@mut self,
+                           privacy: Privacy,
+                           module_: @mut Module,
+                           containing_module: @mut Module,
+                           span: span)
+                        -> ResolveResult<()> {
+        // This function works in a highly imperative manner; it eagerly adds
+        // everything it can to the list of import resolutions of the module
+        // node.
+        debug!("(resolving glob import) resolving %? glob import", privacy);
+        let state = @mut ImportState();
+
+        // We must bail out if the node has unresolved imports of any kind
+        // (including globs).
+        if !(*containing_module).all_imports_resolved() {
+            debug!("(resolving glob import) target module has unresolved \
+                    imports; bailing out");
+            return Indeterminate;
+        }
+
+        assert!(containing_module.glob_count == 0);
+
+        // Add all resolved imports from the containing module.
+        for containing_module.import_resolutions.each
+                |ident, target_import_resolution| {
+
+            debug!("(resolving glob import) writing module resolution \
+                    %? into `%s`",
+                   target_import_resolution.type_target.is_none(),
+                   self.module_to_str(module_));
+
+            // Here we merge two import resolutions.
+            match module_.import_resolutions.find(ident) {
+                None if target_import_resolution.privacy == Public => {
+                    // Simple: just copy the old import resolution.
+                    let new_import_resolution =
+                        @mut ImportResolution(privacy,
+                                              target_import_resolution.span,
+                                              state);
+                    new_import_resolution.value_target =
+                        copy target_import_resolution.value_target;
+                    new_import_resolution.type_target =
+                        copy target_import_resolution.type_target;
+
+                    module_.import_resolutions.insert
+                        (*ident, new_import_resolution);
+                }
+                None => { /* continue ... */ }
+                Some(dest_import_resolution) => {
+                    // Merge the two import resolutions at a finer-grained
+                    // level.
+
+                    match target_import_resolution.value_target {
+                        None => {
+                            // Continue.
+                        }
+                        Some(copy value_target) => {
+                            dest_import_resolution.value_target =
+                                Some(value_target);
+                        }
+                    }
+                    match target_import_resolution.type_target {
+                        None => {
+                            // Continue.
+                        }
+                        Some(copy type_target) => {
+                            dest_import_resolution.type_target =
+                                Some(type_target);
+                        }
+                    }
+                }
+            }
+        }
+
+        let merge_import_resolution = |ident,
+                                       name_bindings: @mut NameBindings| {
+            let dest_import_resolution;
+            match module_.import_resolutions.find(ident) {
+                None => {
+                    // Create a new import resolution from this child.
+                    dest_import_resolution = @mut ImportResolution(privacy,
+                                                                   span,
+                                                                   state);
+                    module_.import_resolutions.insert
+                        (*ident, dest_import_resolution);
+                }
+                Some(existing_import_resolution) => {
+                    dest_import_resolution = *existing_import_resolution;
+                }
+            }
+
+            debug!("(resolving glob import) writing resolution `%s` in `%s` \
+                    to `%s`, privacy=%?",
+                   *self.session.str_of(*ident),
+                   self.module_to_str(containing_module),
+                   self.module_to_str(module_),
+                   copy dest_import_resolution.privacy);
+
+            // Merge the child item into the import resolution.
+            if name_bindings.defined_in_public_namespace(ValueNS) {
+                debug!("(resolving glob import) ... for value target");
+                dest_import_resolution.value_target =
+                    Some(Target(containing_module, name_bindings));
+            }
+            if name_bindings.defined_in_public_namespace(TypeNS) {
+                debug!("(resolving glob import) ... for type target");
+                dest_import_resolution.type_target =
+                    Some(Target(containing_module, name_bindings));
+            }
+        };
+
+        // Add all children from the containing module.
+        for containing_module.children.each |ident, name_bindings| {
+            merge_import_resolution(ident, *name_bindings);
+        }
+
+        // Add external module children from the containing module.
+        for containing_module.external_module_children.each
+                |ident, module| {
+            let name_bindings =
+                @mut Resolver::create_name_bindings_from_module(*module);
+            merge_import_resolution(ident, name_bindings);
+        }
+
+        debug!("(resolving glob import) successfully resolved import");
+        return Success(());
+    }
+
+    /// Resolves the given module path from the given root `module_`.
+    fn resolve_module_path_from_root(@mut self,
+                                     module_: @mut Module,
+                                     module_path: &[ident],
+                                     index: uint,
+                                     span: span,
+                                     mut name_search_type: NameSearchType)
+                                  -> ResolveResult<@mut Module> {
+        let mut search_module = module_;
+        let mut index = index;
+        let module_path_len = module_path.len();
+
+        // Resolve the module part of the path. This does not involve looking
+        // upward though scope chains; we simply resolve names directly in
+        // modules as we go.
+
+        while index < module_path_len {
+            let name = module_path[index];
+            match self.resolve_name_in_module(search_module,
+                                              name,
+                                              TypeNS,
+                                              name_search_type) {
+                Failed => {
+                    self.session.span_err(span, ~"unresolved name");
+                    return Failed;
+                }
+                Indeterminate => {
+                    debug!("(resolving module path for import) module \
+                            resolution is indeterminate: %s",
+                            *self.session.str_of(name));
+                    return Indeterminate;
+                }
+                Success(target) => {
+                    // Check to see whether there are type bindings, and, if
+                    // so, whether there is a module within.
+                    match target.bindings.type_def {
+                        Some(copy type_def) => {
+                            match type_def.module_def {
+                                None => {
+                                    // Not a module.
+                                    self.session.span_err(span,
+                                                          fmt!("not a \
+                                                                module: %s",
+                                                               *self.session.
+                                                                   str_of(
+                                                                    name)));
+                                    return Failed;
+                                }
+                                Some(copy module_def) => {
+                                    search_module = module_def;
+                                }
+                            }
+                        }
+                        None => {
+                            // There are no type bindings at all.
+                            self.session.span_err(span,
+                                                  fmt!("not a module: %s",
+                                                       *self.session.str_of(
+                                                            name)));
+                            return Failed;
+                        }
+                    }
+                }
+            }
+
+            index += 1;
+
+            // After the first element of the path, allow searching through
+            // items and imports unconditionally. This allows things like:
+            //
+            // pub mod core {
+            //     pub use vec;
+            // }
+            //
+            // pub mod something_else {
+            //     use core::vec;
+            // }
+
+            name_search_type = SearchItemsAndPublicImports;
+        }
+
+        return Success(search_module);
+    }
+
+    /// Attempts to resolve the module part of an import directive or path
+    /// rooted at the given module.
+    fn resolve_module_path_for_import(@mut self,
+                                      module_: @mut Module,
+                                      module_path: &[ident],
+                                      use_lexical_scope: UseLexicalScopeFlag,
+                                      span: span)
+                                   -> ResolveResult<@mut Module> {
+        let module_path_len = module_path.len();
+        assert!(module_path_len > 0);
+
+        debug!("(resolving module path for import) processing `%s` rooted at \
+               `%s`",
+               self.idents_to_str(module_path),
+               self.module_to_str(module_));
+
+        // Resolve the module prefix, if any.
+        let module_prefix_result = self.resolve_module_prefix(module_,
+                                                              module_path);
+
+        let search_module;
+        let start_index;
+        match module_prefix_result {
+            Failed => {
+                self.session.span_err(span, ~"unresolved name");
+                return Failed;
+            }
+            Indeterminate => {
+                debug!("(resolving module path for import) indeterminate; \
+                        bailing");
+                return Indeterminate;
+            }
+            Success(NoPrefixFound) => {
+                // There was no prefix, so we're considering the first element
+                // of the path. How we handle this depends on whether we were
+                // instructed to use lexical scope or not.
+                match use_lexical_scope {
+                    DontUseLexicalScope => {
+                        // This is a crate-relative path. We will start the
+                        // resolution process at index zero.
+                        search_module = self.graph_root.get_module();
+                        start_index = 0;
+                    }
+                    UseLexicalScope => {
+                        // This is not a crate-relative path. We resolve the
+                        // first component of the path in the current lexical
+                        // scope and then proceed to resolve below that.
+                        let result = self.resolve_module_in_lexical_scope(
+                            module_,
+                            module_path[0]);
+                        match result {
+                            Failed => {
+                                self.session.span_err(span,
+                                                      ~"unresolved name");
+                                return Failed;
+                            }
+                            Indeterminate => {
+                                debug!("(resolving module path for import) \
+                                        indeterminate; bailing");
+                                return Indeterminate;
+                            }
+                            Success(containing_module) => {
+                                search_module = containing_module;
+                                start_index = 1;
+                            }
+                        }
+                    }
+                }
+            }
+            Success(PrefixFound(containing_module, index)) => {
+                search_module = containing_module;
+                start_index = index;
+            }
+        }
+
+        self.resolve_module_path_from_root(search_module,
+                                           module_path,
+                                           start_index,
+                                           span,
+                                           SearchItemsAndPublicImports)
+    }
+
+    /// Invariant: This must only be called during main resolution, not during
+    /// import resolution.
+    fn resolve_item_in_lexical_scope(@mut self,
+                                     module_: @mut Module,
+                                     name: ident,
+                                     namespace: Namespace,
+                                     search_through_modules:
+                                        SearchThroughModulesFlag)
+                                  -> ResolveResult<Target> {
+        debug!("(resolving item in lexical scope) resolving `%s` in \
+                namespace %? in `%s`",
+               *self.session.str_of(name),
+               namespace,
+               self.module_to_str(module_));
+
+        // The current module node is handled specially. First, check for
+        // its immediate children.
+        match module_.children.find(&name) {
+            Some(name_bindings)
+                    if name_bindings.defined_in_namespace(namespace) => {
+                return Success(Target(module_, *name_bindings));
+            }
+            Some(_) | None => { /* Not found; continue. */ }
+        }
+
+        // Now check for its import directives. We don't have to have resolved
+        // all its imports in the usual way; this is because chains of
+        // adjacent import statements are processed as though they mutated the
+        // current scope.
+        match module_.import_resolutions.find(&name) {
+            None => {
+                // Not found; continue.
+            }
+            Some(import_resolution) => {
+                match (*import_resolution).target_for_namespace(namespace) {
+                    None => {
+                        // Not found; continue.
+                        debug!("(resolving item in lexical scope) found \
+                                import resolution, but not in namespace %?",
+                               namespace);
+                    }
+                    Some(target) => {
+                        debug!("(resolving item in lexical scope) using \
+                                import resolution");
+                        import_resolution.state.used = true;
+                        return Success(copy target);
+                    }
+                }
+            }
+        }
+
+        // Search for external modules.
+        if namespace == TypeNS {
+            match module_.external_module_children.find(&name) {
+                None => {}
+                Some(module) => {
+                    let name_bindings =
+                        @mut Resolver::create_name_bindings_from_module(
+                            *module);
+                    return Success(Target(module_, name_bindings));
+                }
+            }
+        }
+
+        // Finally, proceed up the scope chain looking for parent modules.
+        let mut search_module = module_;
+        loop {
+            // Go to the next parent.
+            match search_module.parent_link {
+                NoParentLink => {
+                    // No more parents. This module was unresolved.
+                    debug!("(resolving item in lexical scope) unresolved \
+                            module");
+                    return Failed;
+                }
+                ModuleParentLink(parent_module_node, _) => {
+                    match search_through_modules {
+                        DontSearchThroughModules => {
+                            match search_module.kind {
+                                NormalModuleKind => {
+                                    // We stop the search here.
+                                    debug!("(resolving item in lexical \
+                                            scope) unresolved module: not \
+                                            searching through module \
+                                            parents");
+                                    return Failed;
+                                }
+                                ExternModuleKind |
+                                TraitModuleKind |
+                                AnonymousModuleKind => {
+                                    search_module = parent_module_node;
+                                }
+                            }
+                        }
+                        SearchThroughModules => {
+                            search_module = parent_module_node;
+                        }
+                    }
+                }
+                BlockParentLink(parent_module_node, _) => {
+                    search_module = parent_module_node;
+                }
+            }
+
+            // Resolve the name in the parent module.
+            match self.resolve_name_in_module(search_module,
+                                              name,
+                                              namespace,
+                                              SearchItemsAndAllImports) {
+                Failed => {
+                    // Continue up the search chain.
+                }
+                Indeterminate => {
+                    // We couldn't see through the higher scope because of an
+                    // unresolved import higher up. Bail.
+
+                    debug!("(resolving item in lexical scope) indeterminate \
+                            higher scope; bailing");
+                    return Indeterminate;
+                }
+                Success(target) => {
+                    // We found the module.
+                    return Success(copy target);
+                }
+            }
+        }
+    }
+
+    /** Resolves a module name in the current lexical scope. */
+    fn resolve_module_in_lexical_scope(@mut self,
+                                       module_: @mut Module,
+                                       name: ident)
+                                    -> ResolveResult<@mut Module> {
+        // If this module is an anonymous module, resolve the item in the
+        // lexical scope. Otherwise, resolve the item from the crate root.
+        let resolve_result = self.resolve_item_in_lexical_scope(
+            module_, name, TypeNS, DontSearchThroughModules);
+        match resolve_result {
+            Success(target) => {
+                let bindings = &mut *target.bindings;
+                match bindings.type_def {
+                    Some(ref type_def) => {
+                        match (*type_def).module_def {
+                            None => {
+                                error!("!!! (resolving module in lexical \
+                                        scope) module wasn't actually a \
+                                        module!");
+                                return Failed;
+                            }
+                            Some(module_def) => {
+                                return Success(module_def);
+                            }
+                        }
+                    }
+                    None => {
+                        error!("!!! (resolving module in lexical scope) module
+                                wasn't actually a module!");
+                        return Failed;
+                    }
+                }
+            }
+            Indeterminate => {
+                debug!("(resolving module in lexical scope) indeterminate; \
+                        bailing");
+                return Indeterminate;
+            }
+            Failed => {
+                debug!("(resolving module in lexical scope) failed to \
+                        resolve");
+                return Failed;
+            }
+        }
+    }
+
+    /**
+     * Returns the nearest normal module parent of the given module.
+     */
+    fn get_nearest_normal_module_parent(@mut self, module_: @mut Module)
+                                     -> Option<@mut Module> {
+        let mut module_ = module_;
+        loop {
+            match module_.parent_link {
+                NoParentLink => return None,
+                ModuleParentLink(new_module, _) |
+                BlockParentLink(new_module, _) => {
+                    match new_module.kind {
+                        NormalModuleKind => return Some(new_module),
+                        ExternModuleKind |
+                        TraitModuleKind |
+                        AnonymousModuleKind => module_ = new_module,
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the nearest normal module parent of the given module, or the
+     * module itself if it is a normal module.
+     */
+    fn get_nearest_normal_module_parent_or_self(@mut self,
+                                                module_: @mut Module)
+                                             -> @mut Module {
+        match module_.kind {
+            NormalModuleKind => return module_,
+            ExternModuleKind | TraitModuleKind | AnonymousModuleKind => {
+                match self.get_nearest_normal_module_parent(module_) {
+                    None => module_,
+                    Some(new_module) => new_module
+                }
+            }
+        }
+    }
+
+    /**
+     * Resolves a "module prefix". A module prefix is one of (a) `self::`;
+     * (b) some chain of `super::`.
+     */
+    fn resolve_module_prefix(@mut self,
+                             module_: @mut Module,
+                             module_path: &[ident])
+                          -> ResolveResult<ModulePrefixResult> {
+        let interner = self.session.parse_sess.interner;
+
+        // Start at the current module if we see `self` or `super`, or at the
+        // top of the crate otherwise.
+        let mut containing_module;
+        let mut i;
+        if *interner.get(module_path[0]) == ~"self" {
+            containing_module =
+                self.get_nearest_normal_module_parent_or_self(module_);
+            i = 1;
+        } else if *interner.get(module_path[0]) == ~"super" {
+            containing_module =
+                self.get_nearest_normal_module_parent_or_self(module_);
+            i = 0;  // We'll handle `super` below.
+        } else {
+            return Success(NoPrefixFound);
+        }
+
+        // Now loop through all the `super`s we find.
+        while i < module_path.len() &&
+                *interner.get(module_path[i]) == ~"super" {
+            debug!("(resolving module prefix) resolving `super` at %s",
+                   self.module_to_str(containing_module));
+            match self.get_nearest_normal_module_parent(containing_module) {
+                None => return Failed,
+                Some(new_module) => {
+                    containing_module = new_module;
+                    i += 1;
+                }
+            }
+        }
+
+        debug!("(resolving module prefix) finished resolving prefix at %s",
+               self.module_to_str(containing_module));
+
+        return Success(PrefixFound(containing_module, i));
+    }
+
+    /// Attempts to resolve the supplied name in the given module for the
+    /// given namespace. If successful, returns the target corresponding to
+    /// the name.
+    fn resolve_name_in_module(@mut self,
+                              module_: @mut Module,
+                              name: ident,
+                              namespace: Namespace,
+                              name_search_type: NameSearchType)
+                           -> ResolveResult<Target> {
+        debug!("(resolving name in module) resolving `%s` in `%s`",
+               *self.session.str_of(name),
+               self.module_to_str(module_));
+
+        // First, check the direct children of the module.
+        match module_.children.find(&name) {
+            Some(name_bindings)
+                    if name_bindings.defined_in_namespace(namespace) => {
+                debug!("(resolving name in module) found node as child");
+                return Success(Target(module_, *name_bindings));
+            }
+            Some(_) | None => {
+                // Continue.
+            }
+        }
+
+        // Next, check the module's imports if necessary.
+
+        // If this is a search of all imports, we should be done with glob
+        // resolution at this point.
+        if name_search_type == SearchItemsAndAllImports {
+            assert!(module_.glob_count == 0);
+        }
+
+        // Check the list of resolved imports.
+        match module_.import_resolutions.find(&name) {
+            Some(import_resolution) => {
+                if import_resolution.privacy == Public &&
+                        import_resolution.outstanding_references != 0 {
+                    debug!("(resolving name in module) import \
+                            unresolved; bailing out");
+                    return Indeterminate;
+                }
+
+                match import_resolution.target_for_namespace(namespace) {
+                    None => {
+                        debug!("(resolving name in module) name found, \
+                                but not in namespace %?",
+                               namespace);
+                    }
+                    Some(target)
+                            if name_search_type ==
+                                SearchItemsAndAllImports ||
+                            import_resolution.privacy == Public => {
+                        debug!("(resolving name in module) resolved to \
+                                import");
+                        import_resolution.state.used = true;
+                        return Success(copy target);
+                    }
+                    Some(_) => {
+                        debug!("(resolving name in module) name found, \
+                                but not public");
+                    }
+                }
+            }
+            None => {} // Continue.
+        }
+
+        // Finally, search through external children.
+        if namespace == TypeNS {
+            match module_.external_module_children.find(&name) {
+                None => {}
+                Some(module) => {
+                    let name_bindings =
+                        @mut Resolver::create_name_bindings_from_module(
+                            *module);
+                    return Success(Target(module_, name_bindings));
+                }
+            }
+        }
+
+        // We're out of luck.
+        debug!("(resolving name in module) failed to resolve %s",
+               *self.session.str_of(name));
+        return Failed;
+    }
+
+    fn report_unresolved_imports(@mut self, module_: @mut Module) {
+        let index = module_.resolved_import_count;
+        let imports: &mut ~[@ImportDirective] = &mut *module_.imports;
+        let import_count = imports.len();
+        if index != import_count {
+            let sn = self.session.codemap.span_to_snippet(imports[index].span);
+            if str::contains(sn, "::") {
+                self.session.span_err(imports[index].span, ~"unresolved import");
+            } else {
+                let err = fmt!("unresolved import (maybe you meant `%s::*`?)",
+                               sn.slice(0, sn.len() - 1)); // -1 to adjust for semicolon
+                self.session.span_err(imports[index].span, err);
+            }
+        }
+
+        // Descend into children and anonymous children.
+        for module_.children.each_value |&child_node| {
+            match child_node.get_module_if_available() {
+                None => {
+                    // Continue.
+                }
+                Some(child_module) => {
+                    self.report_unresolved_imports(child_module);
+                }
+            }
+        }
+
+        for module_.anonymous_children.each_value |&module_| {
+            self.report_unresolved_imports(module_);
+        }
+    }
+
+    // Export recording
+    //
+    // This pass simply determines what all "export" keywords refer to and
+    // writes the results into the export map.
+    //
+    // FIXME #4953 This pass will be removed once exports change to per-item.
+    // Then this operation can simply be performed as part of item (or import)
+    // processing.
+
+    fn record_exports(@mut self) {
+        let root_module = self.graph_root.get_module();
+        self.record_exports_for_module_subtree(root_module);
+    }
+
+    fn record_exports_for_module_subtree(@mut self, module_: @mut Module) {
+        // If this isn't a local crate, then bail out. We don't need to record
+        // exports for nonlocal crates.
+
+        match module_.def_id {
+            Some(def_id) if def_id.crate == local_crate => {
+                // OK. Continue.
+                debug!("(recording exports for module subtree) recording \
+                        exports for local module");
+            }
+            None => {
+                // Record exports for the root module.
+                debug!("(recording exports for module subtree) recording \
+                        exports for root module");
+            }
+            Some(_) => {
+                // Bail out.
+                debug!("(recording exports for module subtree) not recording \
+                        exports for `%s`",
+                       self.module_to_str(module_));
+                return;
+            }
+        }
+
+        self.record_exports_for_module(module_);
+
+        for module_.children.each_value |&child_name_bindings| {
+            match child_name_bindings.get_module_if_available() {
+                None => {
+                    // Nothing to do.
+                }
+                Some(child_module) => {
+                    self.record_exports_for_module_subtree(child_module);
+                }
+            }
+        }
+
+        for module_.anonymous_children.each_value |&child_module| {
+            self.record_exports_for_module_subtree(child_module);
+        }
+    }
+
+    fn record_exports_for_module(@mut self, module_: @mut Module) {
+        let mut exports2 = ~[];
+
+        self.add_exports_for_module(&mut exports2, module_);
+        match /*bad*/copy module_.def_id {
+            Some(def_id) => {
+                self.export_map2.insert(def_id.node, exports2);
+                debug!("(computing exports) writing exports for %d (some)",
+                       def_id.node);
+            }
+            None => {}
+        }
+    }
+
+    fn add_exports_of_namebindings(@mut self,
+                                   exports2: &mut ~[Export2],
+                                   ident: ident,
+                                   namebindings: @mut NameBindings,
+                                   ns: Namespace,
+                                   reexport: bool) {
+        match (namebindings.def_for_namespace(ns),
+               namebindings.privacy_for_namespace(ns)) {
+            (Some(d), Some(Public)) => {
+                debug!("(computing exports) YES: %s '%s' => %?",
+                       if reexport { ~"reexport" } else { ~"export"},
+                       *self.session.str_of(ident),
+                       def_id_of_def(d));
+                exports2.push(Export2 {
+                    reexport: reexport,
+                    name: self.session.str_of(ident),
+                    def_id: def_id_of_def(d)
+                });
+            }
+            (Some(_), Some(privacy)) => {
+                debug!("(computing reexports) NO: privacy %?", privacy);
+            }
+            (d_opt, p_opt) => {
+                debug!("(computing reexports) NO: %?, %?", d_opt, p_opt);
+            }
+        }
+    }
+
+    fn add_exports_for_module(@mut self,
+                              exports2: &mut ~[Export2],
+                              module_: @mut Module) {
+        for module_.children.each |ident, namebindings| {
+            debug!("(computing exports) maybe export '%s'",
+                   *self.session.str_of(*ident));
+            self.add_exports_of_namebindings(&mut *exports2,
+                                             *ident,
+                                             *namebindings,
+                                             TypeNS,
+                                             false);
+            self.add_exports_of_namebindings(&mut *exports2,
+                                             *ident,
+                                             *namebindings,
+                                             ValueNS,
+                                             false);
+        }
+
+        for module_.import_resolutions.each |ident, importresolution| {
+            if importresolution.privacy != Public {
+                debug!("(computing exports) not reexporting private `%s`",
+                       *self.session.str_of(*ident));
+                loop;
+            }
+            for [ TypeNS, ValueNS ].each |ns| {
+                match importresolution.target_for_namespace(*ns) {
+                    Some(target) => {
+                        debug!("(computing exports) maybe reexport '%s'",
+                               *self.session.str_of(*ident));
+                        self.add_exports_of_namebindings(&mut *exports2,
+                                                         *ident,
+                                                         target.bindings,
+                                                         *ns,
+                                                         true)
+                    }
+                    _ => ()
+                }
+            }
+        }
+    }
+
+    // AST resolution
+    //
+    // We maintain a list of value ribs and type ribs.
+    //
+    // Simultaneously, we keep track of the current position in the module
+    // graph in the `current_module` pointer. When we go to resolve a name in
+    // the value or type namespaces, we first look through all the ribs and
+    // then query the module graph. When we resolve a name in the module
+    // namespace, we can skip all the ribs (since nested modules are not
+    // allowed within blocks in Rust) and jump straight to the current module
+    // graph node.
+    //
+    // Named implementations are handled separately. When we find a method
+    // call, we consult the module node to find all of the implementations in
+    // scope. This information is lazily cached in the module node. We then
+    // generate a fake "implementation scope" containing all the
+    // implementations thus found, for compatibility with old resolve pass.
+
+    fn with_scope(@mut self, name: Option<ident>, f: &fn()) {
+        let orig_module = self.current_module;
+
+        // Move down in the graph.
+        match name {
+            None => {
+                // Nothing to do.
+            }
+            Some(name) => {
+                match orig_module.children.find(&name) {
+                    None => {
+                        debug!("!!! (with scope) didn't find `%s` in `%s`",
+                               *self.session.str_of(name),
+                               self.module_to_str(orig_module));
+                    }
+                    Some(name_bindings) => {
+                        match (*name_bindings).get_module_if_available() {
+                            None => {
+                                debug!("!!! (with scope) didn't find module \
+                                        for `%s` in `%s`",
+                                       *self.session.str_of(name),
+                                       self.module_to_str(orig_module));
+                            }
+                            Some(module_) => {
+                                self.current_module = module_;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        f();
+
+        self.current_module = orig_module;
+    }
+
+    // Wraps the given definition in the appropriate number of `def_upvar`
+    // wrappers.
+
+    fn upvarify(@mut self,
+                ribs: &mut ~[@Rib],
+                rib_index: uint,
+                def_like: def_like,
+                span: span,
+                allow_capturing_self: AllowCapturingSelfFlag)
+             -> Option<def_like> {
+        let mut def;
+        let is_ty_param;
+
+        match def_like {
+            dl_def(d @ def_local(*)) | dl_def(d @ def_upvar(*)) |
+            dl_def(d @ def_arg(*)) | dl_def(d @ def_binding(*)) => {
+                def = d;
+                is_ty_param = false;
+            }
+            dl_def(d @ def_ty_param(*)) => {
+                def = d;
+                is_ty_param = true;
+            }
+            dl_def(d @ def_self(*))
+                    if allow_capturing_self == DontAllowCapturingSelf => {
+                def = d;
+                is_ty_param = false;
+            }
+            _ => {
+                return Some(def_like);
+            }
+        }
+
+        let mut rib_index = rib_index + 1;
+        while rib_index < ribs.len() {
+            match ribs[rib_index].kind {
+                NormalRibKind => {
+                    // Nothing to do. Continue.
+                }
+                FunctionRibKind(function_id, body_id) => {
+                    if !is_ty_param {
+                        def = def_upvar(def_id_of_def(def).node,
+                                        @def,
+                                        function_id,
+                                        body_id);
+                    }
+                }
+                MethodRibKind(item_id, _) => {
+                  // If the def is a ty param, and came from the parent
+                  // item, it's ok
+                  match def {
+                    def_ty_param(did, _)
+                        if self.def_map.find(&did.node).map_consume(|x| *x)
+                            == Some(def_typaram_binder(item_id)) => {
+                      // ok
+                    }
+                    _ => {
+                    if !is_ty_param {
+                        // This was an attempt to access an upvar inside a
+                        // named function item. This is not allowed, so we
+                        // report an error.
+
+                        self.session.span_err(
+                            span,
+                            ~"attempted dynamic environment-capture");
+                    } else {
+                        // This was an attempt to use a type parameter outside
+                        // its scope.
+
+                        self.session.span_err(span,
+                                              ~"attempt to use a type \
+                                               argument out of scope");
+                    }
+
+                    return None;
+                    }
+                  }
+                }
+                OpaqueFunctionRibKind => {
+                    if !is_ty_param {
+                        // This was an attempt to access an upvar inside a
+                        // named function item. This is not allowed, so we
+                        // report an error.
+
+                        self.session.span_err(
+                            span,
+                            ~"attempted dynamic environment-capture");
+                    } else {
+                        // This was an attempt to use a type parameter outside
+                        // its scope.
+
+                        self.session.span_err(span,
+                                              ~"attempt to use a type \
+                                               argument out of scope");
+                    }
+
+                    return None;
+                }
+                ConstantItemRibKind => {
+                    // Still doesn't deal with upvars
+                    self.session.span_err(span,
+                                          ~"attempt to use a non-constant \
+                                            value in a constant");
+
+                }
+            }
+
+            rib_index += 1;
+        }
+
+        return Some(dl_def(def));
+    }
+
+    fn search_ribs(@mut self,
+                   ribs: &mut ~[@Rib],
+                   name: ident,
+                   span: span,
+                   allow_capturing_self: AllowCapturingSelfFlag)
+                -> Option<def_like> {
+        // FIXME #4950: This should not use a while loop.
+        // FIXME #4950: Try caching?
+
+        let mut i = ribs.len();
+        while i != 0 {
+            i -= 1;
+            match ribs[i].bindings.find(&name) {
+                Some(&def_like) => {
+                    return self.upvarify(ribs, i, def_like, span,
+                                         allow_capturing_self);
+                }
+                None => {
+                    // Continue.
+                }
+            }
+        }
+
+        return None;
+    }
+
+    fn resolve_crate(@mut self) {
+        debug!("(resolving crate) starting");
+
+        visit_crate(self.crate, (), mk_vt(@Visitor {
+            visit_item: |item, _context, visitor|
+                self.resolve_item(item, visitor),
+            visit_arm: |arm, _context, visitor|
+                self.resolve_arm(arm, visitor),
+            visit_block: |block, _context, visitor|
+                self.resolve_block(block, visitor),
+            visit_expr: |expr, _context, visitor|
+                self.resolve_expr(expr, visitor),
+            visit_local: |local, _context, visitor|
+                self.resolve_local(local, visitor),
+            visit_ty: |ty, _context, visitor|
+                self.resolve_type(ty, visitor),
+            .. *default_visitor()
+        }));
+    }
+
+    fn resolve_item(@mut self, item: @item, visitor: ResolveVisitor) {
+        debug!("(resolving item) resolving %s",
+               *self.session.str_of(item.ident));
+
+        // Items with the !resolve_unexported attribute are X-ray contexts.
+        // This is used to allow the test runner to run unexported tests.
+        let orig_xray_flag = self.xray_context;
+        if contains_name(attr_metas(item.attrs),
+                         ~"!resolve_unexported") {
+            self.xray_context = Xray;
+        }
+
+        match item.node {
+
+            // enum item: resolve all the variants' discrs,
+            // then resolve the ty params
+            item_enum(ref enum_def, ref generics) => {
+                for (*enum_def).variants.each() |variant| {
+                    for variant.node.disr_expr.each |dis_expr| {
+                        // resolve the discriminator expr
+                        // as a constant
+                        self.with_constant_rib(|| {
+                            self.resolve_expr(*dis_expr, visitor);
+                        });
+                    }
+                }
+
+                // n.b. the discr expr gets visted twice.
+                // but maybe it's okay since the first time will signal an
+                // error if there is one? -- tjc
+                do self.with_type_parameter_rib(
+                    HasTypeParameters(
+                        generics, item.id, 0, NormalRibKind)) {
+                    visit_item(item, (), visitor);
+                }
+            }
+
+            item_ty(_, ref generics) => {
+                do self.with_type_parameter_rib
+                        (HasTypeParameters(generics, item.id, 0,
+                                           NormalRibKind))
+                        || {
+
+                    visit_item(item, (), visitor);
+                }
+            }
+
+            item_impl(ref generics,
+                      implemented_traits,
+                      self_type,
+                      ref methods) => {
+                self.resolve_implementation(item.id,
+                                            generics,
+                                            implemented_traits,
+                                            self_type,
+                                            *methods,
+                                            visitor);
+            }
+
+            item_trait(ref generics, ref traits, ref methods) => {
+                // Create a new rib for the self type.
+                let self_type_rib = @Rib(NormalRibKind);
+                self.type_ribs.push(self_type_rib);
+                self_type_rib.bindings.insert(self.type_self_ident,
+                                              dl_def(def_self_ty(item.id)));
+
+                // Create a new rib for the trait-wide type parameters.
+                do self.with_type_parameter_rib
+                        (HasTypeParameters(generics, item.id, 0,
+                                           NormalRibKind)) {
+
+                    self.resolve_type_parameters(&generics.ty_params,
+                                                 visitor);
+
+                    // Resolve derived traits.
+                    for traits.each |trt| {
+                        match self.resolve_path(trt.path, TypeNS, true,
+                                                visitor) {
+                            None =>
+                                self.session.span_err(trt.path.span,
+                                                      ~"attempt to derive a \
+                                                       nonexistent trait"),
+                            Some(def) => {
+                                // Write a mapping from the trait ID to the
+                                // definition of the trait into the definition
+                                // map.
+
+                                debug!("(resolving trait) found trait def: \
+                                       %?", def);
+
+                                self.record_def(trt.ref_id, def);
+                            }
+                        }
+                    }
+
+                    for (*methods).each |method| {
+                        // Create a new rib for the method-specific type
+                        // parameters.
+                        //
+                        // FIXME #4951: Do we need a node ID here?
+
+                        match *method {
+                          required(ref ty_m) => {
+                            do self.with_type_parameter_rib
+                                (HasTypeParameters(&ty_m.generics,
+                                                   item.id,
+                                                   generics.ty_params.len(),
+                                        MethodRibKind(item.id, Required))) {
+
+                                // Resolve the method-specific type
+                                // parameters.
+                                self.resolve_type_parameters(
+                                    &ty_m.generics.ty_params,
+                                    visitor);
+
+                                for ty_m.decl.inputs.each |argument| {
+                                    self.resolve_type(argument.ty, visitor);
+                                }
+
+                                self.resolve_type(ty_m.decl.output, visitor);
+                            }
+                          }
+                          provided(m) => {
+                              self.resolve_method(MethodRibKind(item.id,
+                                                     Provided(m.id)),
+                                                  m,
+                                                  generics.ty_params.len(),
+                                                  visitor)
+                          }
+                        }
+                    }
+                }
+
+                self.type_ribs.pop();
+            }
+
+            item_struct(ref struct_def, ref generics) => {
+                self.resolve_struct(item.id,
+                                    generics,
+                                    struct_def.fields,
+                                    visitor);
+            }
+
+            item_mod(ref module_) => {
+                do self.with_scope(Some(item.ident)) {
+                    self.resolve_module(module_, item.span, item.ident,
+                                        item.id, visitor);
+                }
+            }
+
+            item_foreign_mod(ref foreign_module) => {
+                do self.with_scope(Some(item.ident)) {
+                    for foreign_module.items.each |foreign_item| {
+                        match foreign_item.node {
+                            foreign_item_fn(_, _, ref generics) => {
+                                self.with_type_parameter_rib(
+                                    HasTypeParameters(
+                                        generics, foreign_item.id, 0,
+                                        NormalRibKind),
+                                    || visit_foreign_item(*foreign_item, (),
+                                                          visitor));
+                            }
+                            foreign_item_const(_) => {
+                                visit_foreign_item(*foreign_item, (),
+                                                   visitor);
+                            }
+                        }
+                    }
+                }
+            }
+
+            item_fn(ref fn_decl, _, _, ref generics, ref block) => {
+                // If this is the main function, we must record it in the
+                // session.
+
+                // FIXME #4404 android JNI hacks
+                if !*self.session.building_library ||
+                    self.session.targ_cfg.os == session::os_android {
+
+                    if self.attr_main_fn.is_none() &&
+                           item.ident == special_idents::main {
+
+                        self.main_fns.push(Some((item.id, item.span)));
+                    }
+
+                    if attrs_contains_name(item.attrs, ~"main") {
+                        if self.attr_main_fn.is_none() {
+                            self.attr_main_fn = Some((item.id, item.span));
+                        } else {
+                            self.session.span_err(
+                                    item.span,
+                                    ~"multiple 'main' functions");
+                        }
+                    }
+
+                    if attrs_contains_name(item.attrs, ~"start") {
+                        if self.start_fn.is_none() {
+                            self.start_fn = Some((item.id, item.span));
+                        } else {
+                            self.session.span_err(
+                                    item.span,
+                                    ~"multiple 'start' functions");
+                        }
+                    }
+                }
+
+                self.resolve_function(OpaqueFunctionRibKind,
+                                      Some(fn_decl),
+                                      HasTypeParameters
+                                        (generics,
+                                         item.id,
+                                         0,
+                                         OpaqueFunctionRibKind),
+                                      block,
+                                      NoSelfBinding,
+                                      visitor);
+            }
+
+            item_const(*) => {
+                self.with_constant_rib(|| {
+                    visit_item(item, (), visitor);
+                });
+            }
+
+          item_mac(*) => {
+            fail!(~"item macros unimplemented")
+          }
+        }
+
+        self.xray_context = orig_xray_flag;
+    }
+
+    fn with_type_parameter_rib(@mut self,
+                               type_parameters: TypeParameters,
+                               f: &fn()) {
+        match type_parameters {
+            HasTypeParameters(generics, node_id, initial_index,
+                              rib_kind) => {
+
+                let function_type_rib = @Rib(rib_kind);
+                self.type_ribs.push(function_type_rib);
+
+                for generics.ty_params.eachi |index, type_parameter| {
+                    let name = type_parameter.ident;
+                    debug!("with_type_parameter_rib: %d %d", node_id,
+                           type_parameter.id);
+                    let def_like = dl_def(def_ty_param
+                        (local_def(type_parameter.id),
+                         index + initial_index));
+                    // Associate this type parameter with
+                    // the item that bound it
+                    self.record_def(type_parameter.id,
+                                    def_typaram_binder(node_id));
+                    function_type_rib.bindings.insert(name, def_like);
+                }
+            }
+
+            NoTypeParameters => {
+                // Nothing to do.
+            }
+        }
+
+        f();
+
+        match type_parameters {
+            HasTypeParameters(*) => {
+                self.type_ribs.pop();
+            }
+
+            NoTypeParameters => {
+                // Nothing to do.
+            }
+        }
+    }
+
+    fn with_label_rib(@mut self, f: &fn()) {
+        self.label_ribs.push(@Rib(NormalRibKind));
+        f();
+        self.label_ribs.pop();
+    }
+
+    fn with_constant_rib(@mut self, f: &fn()) {
+        self.value_ribs.push(@Rib(ConstantItemRibKind));
+        f();
+        self.value_ribs.pop();
+    }
+
+    fn resolve_function(@mut self,
+                        rib_kind: RibKind,
+                        optional_declaration: Option<&fn_decl>,
+                        type_parameters: TypeParameters,
+                        block: &blk,
+                        self_binding: SelfBinding,
+                        visitor: ResolveVisitor) {
+        // Create a value rib for the function.
+        let function_value_rib = @Rib(rib_kind);
+        self.value_ribs.push(function_value_rib);
+
+        // Create a label rib for the function.
+        let function_label_rib = @Rib(rib_kind);
+        self.label_ribs.push(function_label_rib);
+
+        // If this function has type parameters, add them now.
+        do self.with_type_parameter_rib(type_parameters) {
+            // Resolve the type parameters.
+            match type_parameters {
+                NoTypeParameters => {
+                    // Continue.
+                }
+                HasTypeParameters(ref generics, _, _, _) => {
+                    self.resolve_type_parameters(&generics.ty_params,
+                                                 visitor);
+                }
+            }
+
+            // Add self to the rib, if necessary.
+            match self_binding {
+                NoSelfBinding => {
+                    // Nothing to do.
+                }
+                HasSelfBinding(self_node_id, is_implicit) => {
+                    let def_like = dl_def(def_self(self_node_id,
+                                                   is_implicit));
+                    (*function_value_rib).bindings.insert(self.self_ident,
+                                                          def_like);
+                }
+            }
+
+            // Add each argument to the rib.
+            match optional_declaration {
+                None => {
+                    // Nothing to do.
+                }
+                Some(declaration) => {
+                    for declaration.inputs.each |argument| {
+                        let binding_mode = ArgumentIrrefutableMode;
+                        let mutability =
+                            if argument.is_mutbl {Mutable} else {Immutable};
+                        self.resolve_pattern(argument.pat,
+                                             binding_mode,
+                                             mutability,
+                                             None,
+                                             visitor);
+
+                        self.resolve_type(argument.ty, visitor);
+
+                        debug!("(resolving function) recorded argument");
+                    }
+
+                    self.resolve_type(declaration.output, visitor);
+                }
+            }
+
+            // Resolve the function body.
+            self.resolve_block(block, visitor);
+
+            debug!("(resolving function) leaving function");
+        }
+
+        self.label_ribs.pop();
+        self.value_ribs.pop();
+    }
+
+    fn resolve_type_parameters(@mut self,
+                               type_parameters: &OptVec<TyParam>,
+                               visitor: ResolveVisitor) {
+        for type_parameters.each |type_parameter| {
+            for type_parameter.bounds.each |&bound| {
+                match bound {
+                    TraitTyParamBound(tref) => {
+                        self.resolve_trait_reference(tref, visitor)
+                    }
+                    RegionTyParamBound => {}
+                }
+            }
+        }
+    }
+
+    fn resolve_trait_reference(@mut self,
+                               trait_reference: &trait_ref,
+                               visitor: ResolveVisitor) {
+        match self.resolve_path(trait_reference.path, TypeNS, true, visitor) {
+            None => {
+                self.session.span_err(trait_reference.path.span,
+                                      ~"attempt to implement an \
+                                        unknown trait");
+            }
+            Some(def) => {
+                self.record_def(trait_reference.ref_id, def);
+            }
+        }
+    }
+
+    fn resolve_struct(@mut self,
+                      id: node_id,
+                      generics: &Generics,
+                      fields: &[@struct_field],
+                      visitor: ResolveVisitor) {
+        // If applicable, create a rib for the type parameters.
+        do self.with_type_parameter_rib(HasTypeParameters
+                                        (generics, id, 0,
+                                         OpaqueFunctionRibKind)) {
+
+            // Resolve the type parameters.
+            self.resolve_type_parameters(&generics.ty_params, visitor);
+
+            // Resolve fields.
+            for fields.each |field| {
+                self.resolve_type(field.node.ty, visitor);
+            }
+        }
+    }
+
+    // Does this really need to take a RibKind or is it always going
+    // to be NormalRibKind?
+    fn resolve_method(@mut self,
+                      rib_kind: RibKind,
+                      method: @method,
+                      outer_type_parameter_count: uint,
+                      visitor: ResolveVisitor) {
+        let method_generics = &method.generics;
+        let type_parameters =
+            HasTypeParameters(method_generics,
+                              method.id,
+                              outer_type_parameter_count,
+                              rib_kind);
+        // we only have self ty if it is a non static method
+        let self_binding = match method.self_ty.node {
+          sty_static => { NoSelfBinding }
+          _ => { HasSelfBinding(method.self_id, false) }
+        };
+
+        self.resolve_function(rib_kind,
+                              Some(&method.decl),
+                              type_parameters,
+                              &method.body,
+                              self_binding,
+                              visitor);
+    }
+
+    fn resolve_implementation(@mut self,
+                              id: node_id,
+                              generics: &Generics,
+                              opt_trait_reference: Option<@trait_ref>,
+                              self_type: @Ty,
+                              methods: &[@method],
+                              visitor: ResolveVisitor) {
+        // If applicable, create a rib for the type parameters.
+        let outer_type_parameter_count = generics.ty_params.len();
+        do self.with_type_parameter_rib(HasTypeParameters
+                                        (generics, id, 0,
+                                         NormalRibKind)) {
+            // Resolve the type parameters.
+            self.resolve_type_parameters(&generics.ty_params,
+                                         visitor);
+
+            // Resolve the trait reference, if necessary.
+            let original_trait_refs;
+            match opt_trait_reference {
+                Some(trait_reference) => {
+                    self.resolve_trait_reference(trait_reference, visitor);
+
+                    // Record the current set of trait references.
+                    let mut new_trait_refs = ~[];
+                    for self.def_map.find(&trait_reference.ref_id).each |&def| {
+                        new_trait_refs.push(def_id_of_def(*def));
+                    }
+                    original_trait_refs = Some(util::replace(
+                        &mut self.current_trait_refs,
+                        Some(new_trait_refs)));
+                }
+                None => {
+                    original_trait_refs = None;
+                }
+            }
+
+            // Resolve the self type.
+            self.resolve_type(self_type, visitor);
+
+            for methods.each |method| {
+                // We also need a new scope for the method-specific
+                // type parameters.
+                self.resolve_method(MethodRibKind(
+                    id,
+                    Provided(method.id)),
+                    *method,
+                    outer_type_parameter_count,
+                    visitor);
+/*
+                    let borrowed_type_parameters = &method.tps;
+                    self.resolve_function(MethodRibKind(
+                                          id,
+                                          Provided(method.id)),
+                                          Some(@method.decl),
+                                          HasTypeParameters
+                                            (borrowed_type_parameters,
+                                             method.id,
+                                             outer_type_parameter_count,
+                                             NormalRibKind),
+                                          method.body,
+                                          HasSelfBinding(method.self_id),
+                                          visitor);
+*/
+            }
+
+            // Restore the original trait references.
+            match original_trait_refs {
+                Some(r) => { self.current_trait_refs = r; }
+                None => ()
+            }
+        }
+    }
+
+    fn resolve_module(@mut self,
+                      module_: &_mod,
+                      span: span,
+                      _name: ident,
+                      id: node_id,
+                      visitor: ResolveVisitor) {
+        // Write the implementations in scope into the module metadata.
+        debug!("(resolving module) resolving module ID %d", id);
+        visit_mod(module_, span, id, (), visitor);
+    }
+
+    fn resolve_local(@mut self, local: @local, visitor: ResolveVisitor) {
+        let mutability = if local.node.is_mutbl {Mutable} else {Immutable};
+
+        // Resolve the type.
+        self.resolve_type(local.node.ty, visitor);
+
+        // Resolve the initializer, if necessary.
+        match local.node.init {
+            None => {
+                // Nothing to do.
+            }
+            Some(initializer) => {
+                self.resolve_expr(initializer, visitor);
+            }
+        }
+
+        // Resolve the pattern.
+        self.resolve_pattern(local.node.pat, LocalIrrefutableMode, mutability,
+                             None, visitor);
+    }
+
+    fn binding_mode_map(@mut self, pat: @pat) -> BindingMap {
+        let mut result = HashMap::new();
+        do pat_bindings(self.def_map, pat) |binding_mode, _id, sp, path| {
+            let ident = path_to_ident(path);
+            result.insert(ident,
+                          binding_info {span: sp,
+                                        binding_mode: binding_mode});
+        }
+        return result;
+    }
+
+    fn check_consistent_bindings(@mut self, arm: &arm) {
+        if arm.pats.len() == 0 { return; }
+        let map_0 = self.binding_mode_map(arm.pats[0]);
+        for arm.pats.eachi() |i, p| {
+            let map_i = self.binding_mode_map(*p);
+
+            for map_0.each |&key, &binding_0| {
+                match map_i.find(&key) {
+                  None => {
+                    self.session.span_err(
+                        p.span,
+                        fmt!("variable `%s` from pattern #1 is \
+                                  not bound in pattern #%u",
+                             *self.session.str_of(key), i + 1));
+                  }
+                  Some(binding_i) => {
+                    if binding_0.binding_mode != binding_i.binding_mode {
+                        self.session.span_err(
+                            binding_i.span,
+                            fmt!("variable `%s` is bound with different \
+                                      mode in pattern #%u than in pattern #1",
+                                 *self.session.str_of(key), i + 1));
+                    }
+                  }
+                }
+            }
+
+            for map_i.each |&key, &binding| {
+                if !map_0.contains_key(&key) {
+                    self.session.span_err(
+                        binding.span,
+                        fmt!("variable `%s` from pattern #%u is \
+                                  not bound in pattern #1",
+                             *self.session.str_of(key), i + 1));
+                }
+            }
+        }
+    }
+
+    fn resolve_arm(@mut self, arm: &arm, visitor: ResolveVisitor) {
+        self.value_ribs.push(@Rib(NormalRibKind));
+
+        let bindings_list = @mut HashMap::new();
+        for arm.pats.each |pattern| {
+            self.resolve_pattern(*pattern, RefutableMode, Immutable,
+                                 Some(bindings_list), visitor);
+        }
+
+        // This has to happen *after* we determine which
+        // pat_idents are variants
+        self.check_consistent_bindings(arm);
+
+        visit_expr_opt(arm.guard, (), visitor);
+        self.resolve_block(&arm.body, visitor);
+
+        self.value_ribs.pop();
+    }
+
+    fn resolve_block(@mut self, block: &blk, visitor: ResolveVisitor) {
+        debug!("(resolving block) entering block");
+        self.value_ribs.push(@Rib(NormalRibKind));
+
+        // Move down in the graph, if there's an anonymous module rooted here.
+        let orig_module = self.current_module;
+        match self.current_module.anonymous_children.find(&block.node.id) {
+            None => { /* Nothing to do. */ }
+            Some(&anonymous_module) => {
+                debug!("(resolving block) found anonymous module, moving \
+                        down");
+                self.current_module = anonymous_module;
+            }
+        }
+
+        // Descend into the block.
+        visit_block(block, (), visitor);
+
+        // Move back up.
+        self.current_module = orig_module;
+
+        self.value_ribs.pop();
+        debug!("(resolving block) leaving block");
+    }
+
+    fn resolve_type(@mut self, ty: @Ty, visitor: ResolveVisitor) {
+        match ty.node {
+            // Like path expressions, the interpretation of path types depends
+            // on whether the path has multiple elements in it or not.
+
+            ty_path(path, path_id) => {
+                // This is a path in the type namespace. Walk through scopes
+                // scopes looking for it.
+                let mut result_def = None;
+
+                // First, check to see whether the name is a primitive type.
+                if path.idents.len() == 1 {
+                    let name = *path.idents.last();
+
+                    match self.primitive_type_table
+                            .primitive_types
+                            .find(&name) {
+
+                        Some(&primitive_type) => {
+                            result_def =
+                                Some(def_prim_ty(primitive_type));
+                        }
+                        None => {
+                            // Continue.
+                        }
+                    }
+                }
+
+                match result_def {
+                    None => {
+                        match self.resolve_path(path, TypeNS, true, visitor) {
+                            Some(def) => {
+                                debug!("(resolving type) resolved `%s` to \
+                                        type %?",
+                                       *self.session.str_of(
+                                            *path.idents.last()),
+                                       def);
+                                result_def = Some(def);
+                            }
+                            None => {
+                                result_def = None;
+                            }
+                        }
+                    }
+                    Some(_) => {
+                        // Continue.
+                    }
+                }
+
+                match result_def {
+                    Some(def) => {
+                        // Write the result into the def map.
+                        debug!("(resolving type) writing resolution for `%s` \
+                                (id %d)",
+                               self.idents_to_str(path.idents),
+                               path_id);
+                        self.record_def(path_id, def);
+                    }
+                    None => {
+                        self.session.span_err
+                            (ty.span, fmt!("use of undeclared type name `%s`",
+                                           self.idents_to_str(path.idents)));
+                    }
+                }
+            }
+
+            _ => {
+                // Just resolve embedded types.
+                visit_ty(ty, (), visitor);
+            }
+        }
+    }
+
+    fn resolve_pattern(@mut self,
+                       pattern: @pat,
+                       mode: PatternBindingMode,
+                       mutability: Mutability,
+                       // Maps idents to the node ID for the (outermost)
+                       // pattern that binds them
+                       bindings_list: Option<@mut HashMap<ident,node_id>>,
+                       visitor: ResolveVisitor) {
+        let pat_id = pattern.id;
+        do walk_pat(pattern) |pattern| {
+            match pattern.node {
+                pat_ident(binding_mode, path, _)
+                        if !path.global && path.idents.len() == 1 => {
+
+                    // The meaning of pat_ident with no type parameters
+                    // depends on whether an enum variant or unit-like struct
+                    // with that name is in scope. The probing lookup has to
+                    // be careful not to emit spurious errors. Only matching
+                    // patterns (match) can match nullary variants or
+                    // unit-like structs. For binding patterns (let), matching
+                    // such a value is simply disallowed (since it's rarely
+                    // what you want).
+
+                    let ident = path.idents[0];
+
+                    match self.resolve_bare_identifier_pattern(ident) {
+                        FoundStructOrEnumVariant(def)
+                                if mode == RefutableMode => {
+                            debug!("(resolving pattern) resolving `%s` to \
+                                    struct or enum variant",
+                                    *self.session.str_of(ident));
+
+                            self.enforce_default_binding_mode(
+                                pattern,
+                                binding_mode,
+                                "an enum variant");
+                            self.record_def(pattern.id, def);
+                        }
+                        FoundStructOrEnumVariant(_) => {
+                            self.session.span_err(pattern.span,
+                                                  fmt!("declaration of `%s` \
+                                                        shadows an enum \
+                                                        variant or unit-like \
+                                                        struct in scope",
+                                                        *self.session
+                                                            .str_of(ident)));
+                        }
+                        FoundConst(def) if mode == RefutableMode => {
+                            debug!("(resolving pattern) resolving `%s` to \
+                                    constant",
+                                    *self.session.str_of(ident));
+
+                            self.enforce_default_binding_mode(
+                                pattern,
+                                binding_mode,
+                                "a constant");
+                            self.record_def(pattern.id, def);
+                        }
+                        FoundConst(_) => {
+                            self.session.span_err(pattern.span,
+                                                  ~"only refutable patterns \
+                                                    allowed here");
+                        }
+                        BareIdentifierPatternUnresolved => {
+                            debug!("(resolving pattern) binding `%s`",
+                                   *self.session.str_of(ident));
+
+                            let is_mutable = mutability == Mutable;
+
+                            let def = match mode {
+                                RefutableMode => {
+                                    // For pattern arms, we must use
+                                    // `def_binding` definitions.
+
+                                    def_binding(pattern.id, binding_mode)
+                                }
+                                LocalIrrefutableMode => {
+                                    // But for locals, we use `def_local`.
+                                    def_local(pattern.id, is_mutable)
+                                }
+                                ArgumentIrrefutableMode => {
+                                    // And for function arguments, `def_arg`.
+                                    def_arg(pattern.id, is_mutable)
+                                }
+                            };
+
+                            // Record the definition so that later passes
+                            // will be able to distinguish variants from
+                            // locals in patterns.
+
+                            self.record_def(pattern.id, def);
+
+                            // Add the binding to the local ribs, if it
+                            // doesn't already exist in the bindings list. (We
+                            // must not add it if it's in the bindings list
+                            // because that breaks the assumptions later
+                            // passes make about or-patterns.)
+
+                            match bindings_list {
+                                Some(bindings_list)
+                                if !bindings_list.contains_key(&ident) => {
+                                    let this = &mut *self;
+                                    let last_rib = this.value_ribs[
+                                            this.value_ribs.len() - 1];
+                                    last_rib.bindings.insert(ident,
+                                                             dl_def(def));
+                                    bindings_list.insert(ident, pat_id);
+                                }
+                                Some(b) => {
+                                  if b.find(&ident) == Some(&pat_id) {
+                                      // Then this is a duplicate variable
+                                      // in the same disjunct, which is an
+                                      // error
+                                     self.session.span_err(pattern.span,
+                                       fmt!("Identifier %s is bound more \
+                                             than once in the same pattern",
+                                            path_to_str(path, self.session
+                                                        .intr())));
+                                  }
+                                  // Not bound in the same pattern: do nothing
+                                }
+                                None => {
+                                    let this = &mut *self;
+                                    let last_rib = this.value_ribs[
+                                            this.value_ribs.len() - 1];
+                                    last_rib.bindings.insert(ident,
+                                                             dl_def(def));
+                                }
+                            }
+                        }
+                    }
+
+                    // Check the types in the path pattern.
+                    for path.types.each |ty| {
+                        self.resolve_type(*ty, visitor);
+                    }
+                }
+
+                pat_ident(binding_mode, path, _) => {
+                    // This must be an enum variant, struct, or constant.
+                    match self.resolve_path(path, ValueNS, false, visitor) {
+                        Some(def @ def_variant(*)) |
+                                Some(def @ def_struct(*)) => {
+                            self.record_def(pattern.id, def);
+                        }
+                        Some(def @ def_const(*)) => {
+                            self.enforce_default_binding_mode(
+                                pattern,
+                                binding_mode,
+                                "a constant");
+                            self.record_def(pattern.id, def);
+                        }
+                        Some(_) => {
+                            self.session.span_err(
+                                path.span,
+                                fmt!("not an enum variant or constant: %s",
+                                     *self.session.str_of(
+                                         *path.idents.last())));
+                        }
+                        None => {
+                            self.session.span_err(path.span,
+                                                  ~"unresolved enum variant");
+                        }
+                    }
+
+                    // Check the types in the path pattern.
+                    for path.types.each |ty| {
+                        self.resolve_type(*ty, visitor);
+                    }
+                }
+
+                pat_enum(path, _) => {
+                    // This must be an enum variant, struct or const.
+                    match self.resolve_path(path, ValueNS, false, visitor) {
+                        Some(def @ def_fn(*))      |
+                        Some(def @ def_variant(*)) |
+                        Some(def @ def_struct(*))  |
+                        Some(def @ def_const(*)) => {
+                            self.record_def(pattern.id, def);
+                        }
+                        Some(_) => {
+                            self.session.span_err(
+                                path.span,
+                                fmt!("not an enum variant, struct or const: %s",
+                                     *self.session.str_of(
+                                         *path.idents.last())));
+                        }
+                        None => {
+                            self.session.span_err(path.span,
+                                                  ~"unresolved enum variant, \
+                                                    struct or const");
+                        }
+                    }
+
+                    // Check the types in the path pattern.
+                    for path.types.each |ty| {
+                        self.resolve_type(*ty, visitor);
+                    }
+                }
+
+                pat_lit(expr) => {
+                    self.resolve_expr(expr, visitor);
+                }
+
+                pat_range(first_expr, last_expr) => {
+                    self.resolve_expr(first_expr, visitor);
+                    self.resolve_expr(last_expr, visitor);
+                }
+
+                pat_struct(path, _, _) => {
+                    let structs: &mut HashSet<def_id> = &mut self.structs;
+                    match self.resolve_path(path, TypeNS, false, visitor) {
+                        Some(def_ty(class_id))
+                                if structs.contains(&class_id) => {
+                            let class_def = def_struct(class_id);
+                            self.record_def(pattern.id, class_def);
+                        }
+                        Some(definition @ def_struct(class_id))
+                                if structs.contains(&class_id) => {
+                            self.record_def(pattern.id, definition);
+                        }
+                        Some(definition @ def_variant(_, variant_id))
+                                if structs.contains(&variant_id) => {
+                            self.record_def(pattern.id, definition);
+                        }
+                        result => {
+                            debug!("(resolving pattern) didn't find struct \
+                                    def: %?", result);
+                            self.session.span_err(
+                                path.span,
+                                fmt!("`%s` does not name a structure",
+                                     self.idents_to_str(path.idents)));
+                        }
+                    }
+                }
+
+                _ => {
+                    // Nothing to do.
+                }
+            }
+        }
+    }
+
+    fn resolve_bare_identifier_pattern(@mut self, name: ident)
+                                    -> BareIdentifierPatternResolution {
+        match self.resolve_item_in_lexical_scope(self.current_module,
+                                                 name,
+                                                 ValueNS,
+                                                 SearchThroughModules) {
+            Success(target) => {
+                match target.bindings.value_def {
+                    None => {
+                        fail!(~"resolved name in the value namespace to a \
+                              set of name bindings with no def?!");
+                    }
+                    Some(def) => {
+                        match def.def {
+                            def @ def_variant(*) | def @ def_struct(*) => {
+                                return FoundStructOrEnumVariant(def);
+                            }
+                            def @ def_const(*) => {
+                                return FoundConst(def);
+                            }
+                            _ => {
+                                return BareIdentifierPatternUnresolved;
+                            }
+                        }
+                    }
+                }
+            }
+
+            Indeterminate => {
+                fail!(~"unexpected indeterminate result");
+            }
+
+            Failed => {
+                return BareIdentifierPatternUnresolved;
+            }
+        }
+    }
+
+    /// If `check_ribs` is true, checks the local definitions first; i.e.
+    /// doesn't skip straight to the containing module.
+    fn resolve_path(@mut self,
+                    path: @Path,
+                    namespace: Namespace,
+                    check_ribs: bool,
+                    visitor: ResolveVisitor)
+                 -> Option<def> {
+        // First, resolve the types.
+        for path.types.each |ty| {
+            self.resolve_type(*ty, visitor);
+        }
+
+        if path.global {
+            return self.resolve_crate_relative_path(path,
+                                                 self.xray_context,
+                                                 namespace);
+        }
+
+        if path.idents.len() > 1 {
+            return self.resolve_module_relative_path(path,
+                                                     self.xray_context,
+                                                     namespace);
+        }
+
+        return self.resolve_identifier(*path.idents.last(),
+                                       namespace,
+                                       check_ribs,
+                                       path.span);
+    }
+
+    fn resolve_identifier(@mut self,
+                          identifier: ident,
+                          namespace: Namespace,
+                          check_ribs: bool,
+                          span: span)
+                       -> Option<def> {
+        if check_ribs {
+            match self.resolve_identifier_in_local_ribs(identifier,
+                                                      namespace,
+                                                      span) {
+                Some(def) => {
+                    return Some(def);
+                }
+                None => {
+                    // Continue.
+                }
+            }
+        }
+
+        return self.resolve_item_by_identifier_in_lexical_scope(identifier,
+                                                                namespace);
+    }
+
+    // FIXME #4952: Merge me with resolve_name_in_module?
+    fn resolve_definition_of_name_in_module(@mut self,
+                                            containing_module: @mut Module,
+                                            name: ident,
+                                            namespace: Namespace,
+                                            xray: XrayFlag)
+                                         -> NameDefinition {
+        // First, search children.
+        match containing_module.children.find(&name) {
+            Some(child_name_bindings) => {
+                match (child_name_bindings.def_for_namespace(namespace),
+                       child_name_bindings.privacy_for_namespace(namespace)) {
+                    (Some(def), Some(Public)) => {
+                        // Found it. Stop the search here.
+                        return ChildNameDefinition(def);
+                    }
+                    (Some(def), _) if xray == Xray => {
+                        // Found it. Stop the search here.
+                        return ChildNameDefinition(def);
+                    }
+                    (Some(_), _) | (None, _) => {
+                        // Continue.
+                    }
+                }
+            }
+            None => {
+                // Continue.
+            }
+        }
+
+        // Next, search import resolutions.
+        match containing_module.import_resolutions.find(&name) {
+            Some(import_resolution) if import_resolution.privacy == Public ||
+                                       xray == Xray => {
+                match (*import_resolution).target_for_namespace(namespace) {
+                    Some(target) => {
+                        match (target.bindings.def_for_namespace(namespace),
+                               target.bindings.privacy_for_namespace(
+                                    namespace)) {
+                            (Some(def), Some(Public)) => {
+                                // Found it.
+                                import_resolution.state.used = true;
+                                return ImportNameDefinition(def);
+                            }
+                            (Some(_), _) | (None, _) => {
+                                // This can happen with external impls, due to
+                                // the imperfect way we read the metadata.
+                            }
+                        }
+                    }
+                    None => {}
+                }
+            }
+            Some(_) | None => {}    // Continue.
+        }
+
+        // Finally, search through external children.
+        if namespace == TypeNS {
+            match containing_module.external_module_children.find(&name) {
+                None => {}
+                Some(module) => {
+                    match module.def_id {
+                        None => {} // Continue.
+                        Some(def_id) => {
+                            return ChildNameDefinition(def_mod(def_id));
+                        }
+                    }
+                }
+            }
+        }
+
+        return NoNameDefinition;
+    }
+
+    fn intern_module_part_of_path(@mut self, path: @Path) -> ~[ident] {
+        let mut module_path_idents = ~[];
+        for path.idents.eachi |index, ident| {
+            if index == path.idents.len() - 1 {
+                break;
+            }
+
+            module_path_idents.push(*ident);
+        }
+
+        return module_path_idents;
+    }
+
+    fn resolve_module_relative_path(@mut self,
+                                    path: @Path,
+                                    xray: XrayFlag,
+                                    namespace: Namespace)
+                                 -> Option<def> {
+        let module_path_idents = self.intern_module_part_of_path(path);
+
+        let containing_module;
+        match self.resolve_module_path_for_import(self.current_module,
+                                                  module_path_idents,
+                                                  UseLexicalScope,
+                                                  path.span) {
+            Failed => {
+                self.session.span_err(path.span,
+                                      fmt!("use of undeclared module `%s`",
+                                           self.idents_to_str(
+                                               module_path_idents)));
+                return None;
+            }
+
+            Indeterminate => {
+                fail!(~"indeterminate unexpected");
+            }
+
+            Success(resulting_module) => {
+                containing_module = resulting_module;
+            }
+        }
+
+        let name = *path.idents.last();
+        match self.resolve_definition_of_name_in_module(containing_module,
+                                                        name,
+                                                        namespace,
+                                                        xray) {
+            NoNameDefinition => {
+                // We failed to resolve the name. Report an error.
+                return None;
+            }
+            ChildNameDefinition(def) | ImportNameDefinition(def) => {
+                return Some(def);
+            }
+        }
+    }
+
+    /// Invariant: This must be called only during main resolution, not during
+    /// import resolution.
+    fn resolve_crate_relative_path(@mut self,
+                                   path: @Path,
+                                   xray: XrayFlag,
+                                   namespace: Namespace)
+                                -> Option<def> {
+        let module_path_idents = self.intern_module_part_of_path(path);
+
+        let root_module = self.graph_root.get_module();
+
+        let containing_module;
+        match self.resolve_module_path_from_root(root_module,
+                                                 module_path_idents,
+                                                 0,
+                                                 path.span,
+                                                 SearchItemsAndAllImports) {
+            Failed => {
+                self.session.span_err(path.span,
+                                      fmt!("use of undeclared module `::%s`",
+                                            self.idents_to_str(
+                                              module_path_idents)));
+                return None;
+            }
+
+            Indeterminate => {
+                fail!(~"indeterminate unexpected");
+            }
+
+            Success(resulting_module) => {
+                containing_module = resulting_module;
+            }
+        }
+
+        let name = *path.idents.last();
+        match self.resolve_definition_of_name_in_module(containing_module,
+                                                        name,
+                                                        namespace,
+                                                        xray) {
+            NoNameDefinition => {
+                // We failed to resolve the name. Report an error.
+                return None;
+            }
+            ChildNameDefinition(def) | ImportNameDefinition(def) => {
+                return Some(def);
+            }
+        }
+    }
+
+    fn resolve_identifier_in_local_ribs(@mut self,
+                                        ident: ident,
+                                        namespace: Namespace,
+                                        span: span)
+                                     -> Option<def> {
+        // Check the local set of ribs.
+        let search_result;
+        match namespace {
+            ValueNS => {
+                search_result = self.search_ribs(&mut self.value_ribs, ident,
+                                                 span,
+                                                 DontAllowCapturingSelf);
+            }
+            TypeNS => {
+                search_result = self.search_ribs(&mut self.type_ribs, ident,
+                                                 span, AllowCapturingSelf);
+            }
+        }
+
+        match search_result {
+            Some(dl_def(def)) => {
+                debug!("(resolving path in local ribs) resolved `%s` to \
+                        local: %?",
+                       *self.session.str_of(ident),
+                       def);
+                return Some(def);
+            }
+            Some(dl_field) | Some(dl_impl(_)) | None => {
+                return None;
+            }
+        }
+    }
+
+    fn resolve_item_by_identifier_in_lexical_scope(@mut self,
+                                                   ident: ident,
+                                                   namespace: Namespace)
+                                                -> Option<def> {
+        // Check the items.
+        match self.resolve_item_in_lexical_scope(self.current_module,
+                                                 ident,
+                                                 namespace,
+                                                 DontSearchThroughModules) {
+            Success(target) => {
+                match (*target.bindings).def_for_namespace(namespace) {
+                    None => {
+                        // This can happen if we were looking for a type and
+                        // found a module instead. Modules don't have defs.
+                        return None;
+                    }
+                    Some(def) => {
+                        debug!("(resolving item path in lexical scope) \
+                                resolved `%s` to item",
+                               *self.session.str_of(ident));
+                        return Some(def);
+                    }
+                }
+            }
+            Indeterminate => {
+                fail!(~"unexpected indeterminate result");
+            }
+            Failed => {
+                return None;
+            }
+        }
+    }
+
+    fn find_best_match_for_name(@mut self, name: &str, max_distance: uint) -> Option<~str> {
+        let this = &mut *self;
+
+        let mut maybes: ~[~str] = ~[];
+        let mut values: ~[uint] = ~[];
+
+        let mut j = this.value_ribs.len();
+        while j != 0 {
+            j -= 1;
+            for this.value_ribs[j].bindings.each_key |&k| {
+                vec::push(&mut maybes, copy *this.session.str_of(k));
+                vec::push(&mut values, uint::max_value);
+            }
+        }
+
+        let mut smallest = 0;
+        for vec::eachi(maybes) |i, &other| {
+
+            values[i] = str::levdistance(name, other);
+
+            if values[i] <= values[smallest] {
+                smallest = i;
+            }
+        }
+
+        if vec::len(values) > 0 &&
+            values[smallest] != uint::max_value &&
+            values[smallest] < str::len(name) + 2 &&
+            values[smallest] <= max_distance &&
+            maybes[smallest] != name.to_owned() {
+
+            Some(vec::swap_remove(&mut maybes, smallest))
+
+        } else {
+            None
+        }
+    }
+
+    fn name_exists_in_scope_struct(@mut self, name: &str) -> bool {
+        let this = &mut *self;
+
+        let mut i = this.type_ribs.len();
+        while i != 0 {
+          i -= 1;
+          match this.type_ribs[i].kind {
+            MethodRibKind(node_id, _) =>
+              for this.crate.node.module.items.each |item| {
+                if item.id == node_id {
+                  match item.node {
+                    item_struct(class_def, _) => {
+                      for vec::each(class_def.fields) |field| {
+                        match field.node.kind {
+                          unnamed_field => {},
+                          named_field(ident, _, _) => {
+                              if str::eq_slice(*this.session.str_of(ident),
+                                               name) {
+                                return true
+                              }
+                            }
+                        }
+                      }
+                    }
+                    _ => {}
+                  }
+                }
+            },
+          _ => {}
+        }
+      }
+      return false;
+    }
+
+    fn resolve_expr(@mut self, expr: @expr, visitor: ResolveVisitor) {
+        // First, record candidate traits for this expression if it could
+        // result in the invocation of a method call.
+
+        self.record_candidate_traits_for_expr_if_necessary(expr);
+
+        // Next, resolve the node.
+        match expr.node {
+            // The interpretation of paths depends on whether the path has
+            // multiple elements in it or not.
+
+            expr_path(path) => {
+                // This is a local path in the value namespace. Walk through
+                // scopes looking for it.
+
+                match self.resolve_path(path, ValueNS, true, visitor) {
+                    Some(def) => {
+                        // Write the result into the def map.
+                        debug!("(resolving expr) resolved `%s`",
+                               self.idents_to_str(path.idents));
+                        self.record_def(expr.id, def);
+                    }
+                    None => {
+                        let wrong_name = self.idents_to_str(
+                            path.idents);
+                        if self.name_exists_in_scope_struct(wrong_name) {
+                            self.session.span_err(expr.span,
+                                        fmt!("unresolved name: `%s`. \
+                                            Did you mean: `self.%s`?",
+                                        wrong_name,
+                                        wrong_name));
+                        }
+                        else {
+                            // limit search to 5 to reduce the number
+                            // of stupid suggestions
+                            match self.find_best_match_for_name(wrong_name, 5) {
+                                Some(m) => {
+                                    self.session.span_err(expr.span,
+                                            fmt!("unresolved name: `%s`. \
+                                                Did you mean: `%s`?",
+                                                wrong_name, m));
+                                }
+                                None => {
+                                    self.session.span_err(expr.span,
+                                            fmt!("unresolved name: `%s`.",
+                                                wrong_name));
+                                }
+                            }
+                        }
+                    }
+                }
+
+                visit_expr(expr, (), visitor);
+            }
+
+            expr_fn_block(ref fn_decl, ref block) => {
+                self.resolve_function(FunctionRibKind(expr.id, block.node.id),
+                                      Some(fn_decl),
+                                      NoTypeParameters,
+                                      block,
+                                      NoSelfBinding,
+                                      visitor);
+            }
+
+            expr_struct(path, _, _) => {
+                // Resolve the path to the structure it goes to.
+                let structs: &mut HashSet<def_id> = &mut self.structs;
+                match self.resolve_path(path, TypeNS, false, visitor) {
+                    Some(def_ty(class_id)) | Some(def_struct(class_id))
+                            if structs.contains(&class_id) => {
+                        let class_def = def_struct(class_id);
+                        self.record_def(expr.id, class_def);
+                    }
+                    Some(definition @ def_variant(_, class_id))
+                            if structs.contains(&class_id) => {
+                        self.record_def(expr.id, definition);
+                    }
+                    _ => {
+                        self.session.span_err(
+                            path.span,
+                            fmt!("`%s` does not name a structure",
+                                 self.idents_to_str(path.idents)));
+                    }
+                }
+
+                visit_expr(expr, (), visitor);
+            }
+
+            expr_loop(_, Some(label)) => {
+                do self.with_label_rib {
+                    let this = &mut *self;
+                    let def_like = dl_def(def_label(expr.id));
+                    let rib = this.label_ribs[this.label_ribs.len() - 1];
+                    rib.bindings.insert(label, def_like);
+
+                    visit_expr(expr, (), visitor);
+                }
+            }
+
+            expr_break(Some(label)) | expr_again(Some(label)) => {
+                match self.search_ribs(&mut self.label_ribs, label, expr.span,
+                                       DontAllowCapturingSelf) {
+                    None =>
+                        self.session.span_err(expr.span,
+                                              fmt!("use of undeclared label \
+                                                   `%s`",
+                                                   *self.session.str_of(
+                                                       label))),
+                    Some(dl_def(def @ def_label(_))) =>
+                        self.record_def(expr.id, def),
+                    Some(_) =>
+                        self.session.span_bug(expr.span,
+                                              ~"label wasn't mapped to a \
+                                                label def!")
+                }
+            }
+
+            _ => {
+                visit_expr(expr, (), visitor);
+            }
+        }
+    }
+
+    fn record_candidate_traits_for_expr_if_necessary(@mut self, expr: @expr) {
+        match expr.node {
+            expr_field(_, ident, _) => {
+                let traits = self.search_for_traits_containing_method(ident);
+                self.trait_map.insert(expr.id, @mut traits);
+            }
+            expr_method_call(_, ident, _, _, _) => {
+                let traits = self.search_for_traits_containing_method(ident);
+                self.trait_map.insert(expr.id, @mut traits);
+            }
+            expr_binary(add, _, _) | expr_assign_op(add, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.add_trait());
+            }
+            expr_binary(subtract, _, _) | expr_assign_op(subtract, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.sub_trait());
+            }
+            expr_binary(mul, _, _) | expr_assign_op(mul, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.mul_trait());
+            }
+            expr_binary(div, _, _) | expr_assign_op(div, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.div_trait());
+            }
+            expr_binary(rem, _, _) | expr_assign_op(rem, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.rem_trait());
+            }
+            expr_binary(bitxor, _, _) | expr_assign_op(bitxor, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.bitxor_trait());
+            }
+            expr_binary(bitand, _, _) | expr_assign_op(bitand, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.bitand_trait());
+            }
+            expr_binary(bitor, _, _) | expr_assign_op(bitor, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.bitor_trait());
+            }
+            expr_binary(shl, _, _) | expr_assign_op(shl, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.shl_trait());
+            }
+            expr_binary(shr, _, _) | expr_assign_op(shr, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.shr_trait());
+            }
+            expr_binary(lt, _, _) | expr_binary(le, _, _) |
+            expr_binary(ge, _, _) | expr_binary(gt, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.ord_trait());
+            }
+            expr_binary(eq, _, _) | expr_binary(ne, _, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.eq_trait());
+            }
+            expr_unary(neg, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.neg_trait());
+            }
+            expr_unary(not, _) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.not_trait());
+            }
+            expr_index(*) => {
+                self.add_fixed_trait_for_expr(expr.id,
+                                              self.lang_items.index_trait());
+            }
+            _ => {
+                // Nothing to do.
+            }
+        }
+    }
+
+    fn search_for_traits_containing_method(@mut self,
+                                           name: ident)
+                                        -> ~[def_id] {
+        debug!("(searching for traits containing method) looking for '%s'",
+               *self.session.str_of(name));
+
+        let mut found_traits = ~[];
+        let mut search_module = self.current_module;
+        loop {
+            // Look for the current trait.
+            match /*bad*/copy self.current_trait_refs {
+                Some(trait_def_ids) => {
+                    for trait_def_ids.each |trait_def_id| {
+                        self.add_trait_info_if_containing_method(
+                            &mut found_traits, *trait_def_id, name);
+                    }
+                }
+                None => {
+                    // Nothing to do.
+                }
+            }
+
+            // Look for trait children.
+            for search_module.children.each_value |&child_name_bindings| {
+                match child_name_bindings.def_for_namespace(TypeNS) {
+                    Some(def) => {
+                        match def {
+                            def_trait(trait_def_id) => {
+                                self.add_trait_info_if_containing_method(
+                                    &mut found_traits, trait_def_id, name);
+                            }
+                            _ => {
+                                // Continue.
+                            }
+                        }
+                    }
+                    None => {
+                        // Continue.
+                    }
+                }
+            }
+
+            // Look for imports.
+            for search_module.import_resolutions.each_value
+                    |&import_resolution| {
+
+                match import_resolution.target_for_namespace(TypeNS) {
+                    None => {
+                        // Continue.
+                    }
+                    Some(target) => {
+                        match target.bindings.def_for_namespace(TypeNS) {
+                            Some(def) => {
+                                match def {
+                                    def_trait(trait_def_id) => {
+                                        let added = self.
+                                        add_trait_info_if_containing_method(
+                                            &mut found_traits,
+                                            trait_def_id, name);
+                                        if added {
+                                            import_resolution.state.used =
+                                                true;
+                                        }
+                                    }
+                                    _ => {
+                                        // Continue.
+                                    }
+                                }
+                            }
+                            None => {
+                                // Continue.
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Move to the next parent.
+            match search_module.parent_link {
+                NoParentLink => {
+                    // Done.
+                    break;
+                }
+                ModuleParentLink(parent_module, _) |
+                BlockParentLink(parent_module, _) => {
+                    search_module = parent_module;
+                }
+            }
+        }
+
+        return found_traits;
+    }
+
+    fn add_trait_info_if_containing_method(&self,
+                                           found_traits: &mut ~[def_id],
+                                           trait_def_id: def_id,
+                                           name: ident)
+                                        -> bool {
+        debug!("(adding trait info if containing method) trying trait %d:%d \
+                for method '%s'",
+               trait_def_id.crate,
+               trait_def_id.node,
+               *self.session.str_of(name));
+
+        match self.trait_info.find(&trait_def_id) {
+            Some(trait_info) if trait_info.contains(&name) => {
+                debug!("(adding trait info if containing method) found trait \
+                        %d:%d for method '%s'",
+                       trait_def_id.crate,
+                       trait_def_id.node,
+                       *self.session.str_of(name));
+                found_traits.push(trait_def_id);
+                true
+            }
+            Some(_) | None => {
+                false
+            }
+        }
+    }
+
+    fn add_fixed_trait_for_expr(@mut self,
+                                expr_id: node_id,
+                                trait_id: def_id) {
+        self.trait_map.insert(expr_id, @mut ~[trait_id]);
+    }
+
+    fn record_def(@mut self, node_id: node_id, def: def) {
+        debug!("(recording def) recording %? for %?", def, node_id);
+        self.def_map.insert(node_id, def);
+    }
+
+    fn enforce_default_binding_mode(@mut self,
+                                    pat: @pat,
+                                    pat_binding_mode: binding_mode,
+                                    descr: &str) {
+        match pat_binding_mode {
+            bind_infer => {}
+            bind_by_copy => {
+                self.session.span_err(
+                    pat.span,
+                    fmt!("cannot use `copy` binding mode with %s",
+                         descr));
+            }
+            bind_by_ref(*) => {
+                self.session.span_err(
+                    pat.span,
+                    fmt!("cannot use `ref` binding mode with %s",
+                         descr));
+            }
+        }
+    }
+
+    //
+    // main function checking
+    //
+    // be sure that there is only one main function
+    //
+    fn check_duplicate_main(@mut self) {
+        let this = &mut *self;
+        if this.attr_main_fn.is_none() && this.start_fn.is_none() {
+            if this.main_fns.len() >= 1u {
+                let mut i = 1u;
+                while i < this.main_fns.len() {
+                    let (_, dup_main_span) = this.main_fns[i].unwrap();
+                    this.session.span_err(
+                        dup_main_span,
+                        ~"multiple 'main' functions");
+                    i += 1;
+                }
+                *this.session.entry_fn = this.main_fns[0];
+                *this.session.entry_type = Some(session::EntryMain);
+            }
+        } else if !this.start_fn.is_none() {
+            *this.session.entry_fn = this.start_fn;
+            *this.session.entry_type = Some(session::EntryStart);
+        } else {
+            *this.session.entry_fn = this.attr_main_fn;
+            *this.session.entry_type = Some(session::EntryMain);
+        }
+    }
+
+    //
+    // Unused import checking
+    //
+    // Although this is a lint pass, it lives in here because it depends on
+    // resolve data structures.
+    //
+
+    fn unused_import_lint_level(@mut self, m: @mut Module) -> level {
+        let settings = self.session.lint_settings;
+        match m.def_id {
+            Some(def) => get_lint_settings_level(settings, unused_imports,
+                                                 def.node, def.node),
+            None => get_lint_level(settings.default_settings, unused_imports)
+        }
+    }
+
+    fn check_for_unused_imports_if_necessary(@mut self) {
+        if self.unused_import_lint_level(self.current_module) == allow {
+            return;
+        }
+
+        let root_module = self.graph_root.get_module();
+        self.check_for_unused_imports_in_module_subtree(root_module);
+    }
+
+    fn check_for_unused_imports_in_module_subtree(@mut self,
+                                                  module_: @mut Module) {
+        // If this isn't a local crate, then bail out. We don't need to check
+        // for unused imports in external crates.
+
+        match module_.def_id {
+            Some(def_id) if def_id.crate == local_crate => {
+                // OK. Continue.
+            }
+            None => {
+                // Check for unused imports in the root module.
+            }
+            Some(_) => {
+                // Bail out.
+                debug!("(checking for unused imports in module subtree) not \
+                        checking for unused imports for `%s`",
+                       self.module_to_str(module_));
+                return;
+            }
+        }
+
+        self.check_for_unused_imports_in_module(module_);
+
+        for module_.children.each_value |&child_name_bindings| {
+            match (*child_name_bindings).get_module_if_available() {
+                None => {
+                    // Nothing to do.
+                }
+                Some(child_module) => {
+                    self.check_for_unused_imports_in_module_subtree
+                        (child_module);
+                }
+            }
+        }
+
+        for module_.anonymous_children.each_value |&child_module| {
+            self.check_for_unused_imports_in_module_subtree(child_module);
+        }
+    }
+
+    fn check_for_unused_imports_in_module(@mut self, module_: @mut Module) {
+        for module_.import_resolutions.each_value |&import_resolution| {
+            // Ignore dummy spans for things like automatically injected
+            // imports for the prelude, and also don't warn about the same
+            // import statement being unused more than once. Furthermore, if
+            // the import is public, then we can't be sure whether it's unused
+            // or not so don't warn about it.
+            if !import_resolution.state.used &&
+                    !import_resolution.state.warned &&
+                    import_resolution.span != dummy_sp() &&
+                    import_resolution.privacy != Public {
+                import_resolution.state.warned = true;
+                let span = import_resolution.span;
+                self.session.span_lint_level(
+                    self.unused_import_lint_level(module_),
+                    span,
+                    ~"unused import");
+            }
+        }
+    }
+
+
+    //
+    // Diagnostics
+    //
+    // Diagnostics are not particularly efficient, because they're rarely
+    // hit.
+    //
+
+    /// A somewhat inefficient routine to obtain the name of a module.
+    fn module_to_str(@mut self, module_: @mut Module) -> ~str {
+        let mut idents = ~[];
+        let mut current_module = module_;
+        loop {
+            match current_module.parent_link {
+                NoParentLink => {
+                    break;
+                }
+                ModuleParentLink(module_, name) => {
+                    idents.push(name);
+                    current_module = module_;
+                }
+                BlockParentLink(module_, _) => {
+                    idents.push(special_idents::opaque);
+                    current_module = module_;
+                }
+            }
+        }
+
+        if idents.len() == 0 {
+            return ~"???";
+        }
+        return self.idents_to_str(vec::reversed(idents));
+    }
+
+    fn dump_module(@mut self, module_: @mut Module) {
+        debug!("Dump of module `%s`:", self.module_to_str(module_));
+
+        debug!("Children:");
+        for module_.children.each_key |&name| {
+            debug!("* %s", *self.session.str_of(name));
+        }
+
+        debug!("Import resolutions:");
+        for module_.import_resolutions.each |name, import_resolution| {
+            let mut value_repr;
+            match import_resolution.target_for_namespace(ValueNS) {
+                None => { value_repr = ~""; }
+                Some(_) => {
+                    value_repr = ~" value:?";
+                    // FIXME #4954
+                }
+            }
+
+            let mut type_repr;
+            match import_resolution.target_for_namespace(TypeNS) {
+                None => { type_repr = ~""; }
+                Some(_) => {
+                    type_repr = ~" type:?";
+                    // FIXME #4954
+                }
+            }
+
+            debug!("* %s:%s%s", *self.session.str_of(*name),
+                   value_repr, type_repr);
+        }
+    }
+}
+
+pub struct CrateMap {
+    def_map: DefMap,
+    exp_map2: ExportMap2,
+    trait_map: TraitMap
+}
+
+/// Entry point to crate resolution.
+pub fn resolve_crate(session: Session,
+                     lang_items: LanguageItems,
+                     crate: @crate)
+                  -> CrateMap {
+    let resolver = @mut Resolver(session, lang_items, crate);
+    resolver.resolve();
+    let @Resolver{def_map, export_map2, trait_map, _} = resolver;
+    CrateMap {
+        def_map: def_map,
+        exp_map2: export_map2,
+        trait_map: trait_map
+    }
+}
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 3bd2a8269b763..d074a2f796f46 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -280,7 +280,7 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
 pub fn variant_opt(bcx: block, pat_id: ast::node_id)
     -> Opt {
     let ccx = bcx.ccx();
-    match *ccx.tcx.def_map.get(&pat_id) {
+    match ccx.tcx.def_map.get_copy(&pat_id) {
         ast::def_variant(enum_id, var_id) => {
             let variants = ty::enum_variants(ccx.tcx, enum_id);
             for vec::each(*variants) |v| {
@@ -516,7 +516,7 @@ pub fn enter_opt<'r>(bcx: block,
         match p.node {
             ast::pat_enum(*) |
             ast::pat_ident(_, _, None) if pat_is_const(tcx.def_map, p) => {
-                let const_def = *tcx.def_map.get(&p.id);
+                let const_def = tcx.def_map.get_copy(&p.id);
                 let const_def_id = ast_util::def_id_of_def(const_def);
                 if opt_eq(tcx, &lit(ConstLit(const_def_id)), opt) {
                     Some(~[])
@@ -552,7 +552,7 @@ pub fn enter_opt<'r>(bcx: block,
                 if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
                     // Look up the struct variant ID.
                     let struct_id;
-                    match *tcx.def_map.get(&p.id) {
+                    match tcx.def_map.get_copy(&p.id) {
                         ast::def_variant(_, found_struct_id) => {
                             struct_id = found_struct_id;
                         }
@@ -865,7 +865,18 @@ pub fn extract_variant_args(bcx: block,
     ExtractedBlock { vals: args, bcx: bcx }
 }
 
+fn match_datum(bcx: block, val: ValueRef, pat_id: ast::node_id) -> Datum {
+    //! Helper for converting from the ValueRef that we pass around in
+    //! the match code, which is always by ref, into a Datum. Eventually
+    //! we should just pass around a Datum and be done with it.
+
+    let ty = node_id_type(bcx, pat_id);
+    Datum {val: val, ty: ty, mode: datum::ByRef, source: RevokeClean}
+}
+
+
 pub fn extract_vec_elems(bcx: block,
+                         pat_span: span,
                          pat_id: ast::node_id,
                          elem_count: uint,
                          slice: Option<uint>,
@@ -873,9 +884,9 @@ pub fn extract_vec_elems(bcx: block,
                          count: ValueRef)
                       -> ExtractedBlock {
     let _icx = bcx.insn_ctxt("match::extract_vec_elems");
+    let vec_datum = match_datum(bcx, val, pat_id);
+    let (bcx, base, len) = vec_datum.get_vec_base_and_len(bcx, pat_span, pat_id);
     let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id));
-    let unboxed = load_if_immediate(bcx, val, vt.vec_ty);
-    let (base, len) = tvec::get_base_and_len(bcx, unboxed, vt.vec_ty);
 
     let mut elems = do vec::from_fn(elem_count) |i| {
         match slice {
@@ -946,30 +957,28 @@ pub fn collect_record_or_struct_fields(bcx: block,
     }
 }
 
-pub fn root_pats_as_necessary(bcx: block,
+pub fn pats_require_rooting(bcx: block,
+                            m: &[@Match],
+                            col: uint)
+                         -> bool {
+    vec::any(m, |br| {
+        let pat_id = br.pats[col].id;
+        let key = root_map_key {id: pat_id, derefs: 0u };
+        bcx.ccx().maps.root_map.contains_key(&key)
+    })
+}
+
+pub fn root_pats_as_necessary(mut bcx: block,
                               m: &[@Match],
                               col: uint,
                               val: ValueRef)
                            -> block {
-    let mut bcx = bcx;
     for vec::each(m) |br| {
         let pat_id = br.pats[col].id;
-
-        let key = root_map_key {id: pat_id, derefs: 0u };
-        match bcx.ccx().maps.root_map.find(&key) {
-            None => (),
-            Some(&root_info) => {
-                // Note: the scope_id will always be the id of the match.  See
-                // the extended comment in rustc::middle::borrowck::preserve()
-                // for details (look for the case covering cat_discr).
-
-                let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
-                                   mode: ByRef, source: ZeroMem};
-                bcx = datum.root(bcx, root_info);
-                // If we kept going, we'd only re-root the same value, so
-                // return now.
-                return bcx;
-            }
+        if pat_id != 0 {
+            let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
+                               mode: ByRef, source: ZeroMem};
+            bcx = datum.root_and_write_guard(bcx, br.pats[col].span, pat_id, 0);
         }
     }
     return bcx;
@@ -1113,7 +1122,8 @@ pub fn compare_values(cx: block,
 pub fn store_non_ref_bindings(bcx: block,
                               data: &ArmData,
                               opt_temp_cleanups: Option<&mut ~[ValueRef]>)
-                           -> block {
+    -> block
+{
     /*!
      *
      * For each copy/move binding, copy the value from the value
@@ -1124,6 +1134,7 @@ pub fn store_non_ref_bindings(bcx: block,
      */
 
     let mut bcx = bcx;
+    let mut opt_temp_cleanups = opt_temp_cleanups;
     for data.bindings_map.each_value |&binding_info| {
         match binding_info.trmode {
             TrByValue(is_move, lldest) => {
@@ -1138,9 +1149,10 @@ pub fn store_non_ref_bindings(bcx: block,
                     }
                 };
 
-                for opt_temp_cleanups.each |temp_cleanups| {
+                do opt_temp_cleanups.mutate |temp_cleanups| {
                     add_clean_temp_mem(bcx, lldest, binding_info.ty);
                     temp_cleanups.push(lldest);
+                    temp_cleanups
                 }
             }
             TrByRef | TrByImplicitRef => {}
@@ -1293,13 +1305,20 @@ pub fn compile_submatch(bcx: block,
                                 vec::slice(vals, col + 1u, vals.len()));
     let ccx = *bcx.fcx.ccx;
     let mut pat_id = 0;
+    let mut pat_span = dummy_sp();
     for vec::each(m) |br| {
         // Find a real id (we're adding placeholder wildcard patterns, but
         // each column is guaranteed to have at least one real pattern)
-        if pat_id == 0 { pat_id = br.pats[col].id; }
+        if pat_id == 0 {
+            pat_id = br.pats[col].id;
+            pat_span = br.pats[col].span;
+        }
     }
 
-    bcx = root_pats_as_necessary(bcx, m, col, val);
+    // If we are not matching against an `@T`, we should not be
+    // required to root any values.
+    assert!(any_box_pat(m, col) || !pats_require_rooting(bcx, m, col));
+
     let rec_fields = collect_record_or_struct_fields(bcx, m, col);
     if rec_fields.len() > 0 {
         let pat_ty = node_id_type(bcx, pat_id);
@@ -1360,6 +1379,7 @@ pub fn compile_submatch(bcx: block,
 
     // Unbox in case of a box field
     if any_box_pat(m, col) {
+        bcx = root_pats_as_necessary(bcx, m, col, val);
         let llbox = Load(bcx, val);
         let box_no_addrspace = non_gc_box_cast(bcx, llbox);
         let unboxed =
@@ -1560,8 +1580,8 @@ pub fn compile_submatch(bcx: block,
                     vec_len_ge(_, i) => Some(i),
                     _ => None
                 };
-                let args = extract_vec_elems(opt_cx, pat_id, n, slice,
-                    val, test_val);
+                let args = extract_vec_elems(opt_cx, pat_span, pat_id, n, slice,
+                                             val, test_val);
                 size = args.vals.len();
                 unpacked = /*bad*/copy args.vals;
                 opt_cx = args.bcx;
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index b8ab360b9e4e7..34f798ec7a631 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -34,7 +34,6 @@ use lib;
 use metadata::common::LinkMeta;
 use metadata::{csearch, cstore, encoder};
 use middle::astencode;
-use middle::borrowck::RootInfo;
 use middle::resolve;
 use middle::trans::_match;
 use middle::trans::adt;
@@ -62,7 +61,6 @@ use middle::trans::type_of::*;
 use middle::ty;
 use util::common::indenter;
 use util::ppaux::{Repr, ty_to_str};
-use util::ppaux;
 
 use core::hash;
 use core::hashmap::{HashMap, HashSet};
@@ -391,14 +389,16 @@ pub fn get_tydesc_simple(ccx: @CrateContext, t: ty::t) -> ValueRef {
 
 pub fn get_tydesc(ccx: @CrateContext, t: ty::t) -> @mut tydesc_info {
     match ccx.tydescs.find(&t) {
-      Some(&inf) => inf,
-      _ => {
-        ccx.stats.n_static_tydescs += 1u;
-        let inf = glue::declare_tydesc(ccx, t);
-        ccx.tydescs.insert(t, inf);
-        inf
-      }
+        Some(&inf) => {
+            return inf;
+        }
+        _ => { }
     }
+
+    ccx.stats.n_static_tydescs += 1u;
+    let inf = glue::declare_tydesc(ccx, t);
+    ccx.tydescs.insert(t, inf);
+    return inf;
 }
 
 pub fn set_optimize_for_size(f: ValueRef) {
@@ -885,23 +885,22 @@ pub fn need_invoke(bcx: block) -> bool {
     // Walk the scopes to look for cleanups
     let mut cur = bcx;
     loop {
-        let current = &mut *cur;
-        let kind = &mut *current.kind;
-        match *kind {
-          block_scope(ref mut inf) => {
-            for vec::each((*inf).cleanups) |cleanup| {
-                match *cleanup {
-                  clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => {
-                    if cleanup_type == normal_exit_and_unwind {
-                        return true;
+        match cur.kind {
+            block_scope(inf) => {
+                let inf = &mut *inf; // FIXME(#5074) workaround old borrowck
+                for vec::each(inf.cleanups) |cleanup| {
+                    match *cleanup {
+                        clean(_, cleanup_type) | clean_temp(_, _, cleanup_type) => {
+                            if cleanup_type == normal_exit_and_unwind {
+                                return true;
+                            }
+                        }
                     }
-                  }
                 }
             }
-          }
-          _ => ()
+            _ => ()
         }
-        cur = match current.parent {
+        cur = match cur.parent {
           Some(next) => next,
           None => return false
         }
@@ -923,11 +922,13 @@ pub fn in_lpad_scope_cx(bcx: block, f: &fn(si: &mut scope_info)) {
     let mut bcx = bcx;
     loop {
         {
-            // FIXME #4280: Borrow check bug workaround.
-            let kind: &mut block_kind = &mut *bcx.kind;
-            match *kind {
-                block_scope(ref mut inf) => {
-                    if inf.cleanups.len() > 0u || bcx.parent.is_none() {
+            match bcx.kind {
+                block_scope(inf) => {
+                    let len = { // FIXME(#5074) workaround old borrowck
+                        let inf = &mut *inf;
+                        inf.cleanups.len()
+                    };
+                    if len > 0u || bcx.parent.is_none() {
                         f(inf);
                         return;
                     }
@@ -989,57 +990,30 @@ pub fn get_landing_pad(bcx: block) -> BasicBlockRef {
     return pad_bcx.llbb;
 }
 
-// Arranges for the value found in `*root_loc` to be dropped once the scope
-// associated with `scope_id` exits.  This is used to keep boxes live when
-// there are extant region pointers pointing at the interior.
-//
-// Note that `root_loc` is not the value itself but rather a pointer to the
-// value.  Generally it in alloca'd value.  The reason for this is that the
-// value is initialized in an inner block but may be freed in some outer
-// block, so an SSA value that is valid in the inner block may not be valid in
-// the outer block.  In fact, the inner block may not even execute.  Rather
-// than generate the full SSA form, we just use an alloca'd value.
-pub fn add_root_cleanup(bcx: block,
-                        root_info: RootInfo,
-                        root_loc: ValueRef,
-                        ty: ty::t) {
-
-    debug!("add_root_cleanup(bcx=%s, \
-                             scope=%d, \
-                             freezes=%?, \
-                             root_loc=%s, \
-                             ty=%s)",
-           bcx.to_str(),
-           root_info.scope,
-           root_info.freezes,
-           val_str(bcx.ccx().tn, root_loc),
-           ppaux::ty_to_str(bcx.ccx().tcx, ty));
-
-    let bcx_scope = find_bcx_for_scope(bcx, root_info.scope);
-    if root_info.freezes {
-        add_clean_frozen_root(bcx_scope, root_loc, ty);
-    } else {
-        add_clean_temp_mem(bcx_scope, root_loc, ty);
-    }
-
-    fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block {
-        let mut bcx_sid = bcx;
-        loop {
-            bcx_sid = match bcx_sid.node_info {
-              Some(NodeInfo { id, _ }) if id == scope_id => {
+pub fn find_bcx_for_scope(bcx: block, scope_id: ast::node_id) -> block {
+    let mut bcx_sid = bcx;
+    loop {
+        bcx_sid = match bcx_sid.node_info {
+            Some(NodeInfo { id, _ }) if id == scope_id => {
                 return bcx_sid
               }
-              _ => {
-                match bcx_sid.parent {
-                  None => bcx.tcx().sess.bug(
-                      fmt!("no enclosing scope with id %d", scope_id)),
-                  Some(bcx_par) => bcx_par
+
+                // FIXME(#6268, #6248) hacky cleanup for nested method calls
+                Some(NodeInfo { callee_id: Some(id), _ }) if id == scope_id => {
+                    return bcx_sid
+                }
+
+                _ => {
+                    match bcx_sid.parent {
+                        None => bcx.tcx().sess.bug(
+                            fmt!("no enclosing scope with id %d", scope_id)),
+                        Some(bcx_par) => bcx_par
+                    }
                 }
-              }
             }
         }
     }
-}
+
 
 pub fn do_spill(bcx: block, v: ValueRef, t: ty::t) -> ValueRef {
     if ty::type_is_bot(t) {
@@ -1160,7 +1134,7 @@ pub fn trans_stmt(cx: block, s: &ast::stmt) -> block {
     let _icx = cx.insn_ctxt("trans_stmt");
     debug!("trans_stmt(%s)", stmt_to_str(s, cx.tcx().sess.intr()));
 
-    if !cx.sess().no_asm_comments() {
+    if cx.sess().asm_comments() {
         add_span_comment(cx, s.span, stmt_to_str(s, cx.ccx().sess.intr()));
     }
 
@@ -1220,7 +1194,7 @@ pub fn new_block(cx: fn_ctxt, parent: Option<block>, kind: block_kind,
 }
 
 pub fn simple_block_scope() -> block_kind {
-    block_scope(scope_info {
+    block_scope(@mut scope_info {
         loop_break: None,
         loop_label: None,
         cleanups: ~[],
@@ -1248,7 +1222,7 @@ pub fn loop_scope_block(bcx: block,
                         loop_label: Option<ident>,
                         n: ~str,
                         opt_node_info: Option<NodeInfo>) -> block {
-    return new_block(bcx.fcx, Some(bcx), block_scope(scope_info {
+    return new_block(bcx.fcx, Some(bcx), block_scope(@mut scope_info {
         loop_break: Some(loop_break),
         loop_label: loop_label,
         cleanups: ~[],
@@ -1284,7 +1258,7 @@ pub fn trans_block_cleanups(bcx: block, cleanups: ~[cleanup]) -> block {
 }
 
 pub fn trans_block_cleanups_(bcx: block,
-                             cleanups: ~[cleanup],
+                             cleanups: &[cleanup],
                              /* cleanup_cx: block, */
                              is_lpad: bool) -> block {
     let _icx = bcx.insn_ctxt("trans_block_cleanups");
@@ -1326,28 +1300,28 @@ pub fn cleanup_and_leave(bcx: block,
                 @fmt!("cleanup_and_leave(%s)", cur.to_str()));
         }
 
-        {
-            // FIXME #4280: Borrow check bug workaround.
-            let kind: &mut block_kind = &mut *cur.kind;
-            match *kind {
-              block_scope(ref mut inf) if !inf.cleanups.is_empty() => {
-                for vec::find((*inf).cleanup_paths,
-                              |cp| cp.target == leave).each |cp| {
-                    Br(bcx, cp.dest);
-                    return;
-                }
-                let sub_cx = sub_block(bcx, ~"cleanup");
-                Br(bcx, sub_cx.llbb);
-                inf.cleanup_paths.push(cleanup_path {
-                    target: leave,
-                    dest: sub_cx.llbb
-                });
+        match cur.kind {
+            block_scope(inf) if !inf.empty_cleanups() => {
+                let (sub_cx, inf_cleanups) = {
+                    let inf = &mut *inf; // FIXME(#5074) workaround stage0
+                    for vec::find((*inf).cleanup_paths,
+                                  |cp| cp.target == leave).each |cp| {
+                        Br(bcx, cp.dest);
+                        return;
+                    }
+                    let sub_cx = sub_block(bcx, ~"cleanup");
+                    Br(bcx, sub_cx.llbb);
+                    inf.cleanup_paths.push(cleanup_path {
+                        target: leave,
+                        dest: sub_cx.llbb
+                    });
+                    (sub_cx, copy inf.cleanups)
+                };
                 bcx = trans_block_cleanups_(sub_cx,
-                                            block_cleanups(cur),
+                                            inf_cleanups,
                                             is_lpad);
-              }
-              _ => ()
             }
+            _ => ()
         }
 
         match upto {
@@ -2080,7 +2054,7 @@ pub fn trans_tuple_struct(ccx: @CrateContext,
                                              fcx.llretptr.get(),
                                              0,
                                              i);
-        let llarg = match *fcx.llargs.get(&field.node.id) {
+        let llarg = match fcx.llargs.get_copy(&field.node.id) {
             local_mem(x) => x,
             _ => {
                 ccx.tcx.sess.bug(~"trans_tuple_struct: llarg wasn't \
@@ -2120,7 +2094,7 @@ pub fn trans_enum_def(ccx: @CrateContext, enum_definition: &ast::enum_def,
 
 pub fn trans_item(ccx: @CrateContext, item: &ast::item) {
     let _icx = ccx.insn_ctxt("trans_item");
-    let path = match *ccx.tcx.items.get(&item.id) {
+    let path = match ccx.tcx.items.get_copy(&item.id) {
         ast_map::node_item(_, p) => p,
         // tjc: ?
         _ => fail!(~"trans_item"),
@@ -2302,7 +2276,7 @@ pub fn create_entry_wrapper(ccx: @CrateContext,
         // Call main.
         let lloutputarg = C_null(T_ptr(T_i8()));
         let llenvarg = unsafe { llvm::LLVMGetParam(llfdecl, 1 as c_uint) };
-        let mut args = ~[lloutputarg, llenvarg];
+        let args = ~[lloutputarg, llenvarg];
         let llresult = Call(bcx, main_llfn, args);
         Store(bcx, llresult, fcx.llretptr.get());
 
@@ -2413,7 +2387,7 @@ pub fn fill_fn_pair(bcx: block, pair: ValueRef, llfn: ValueRef,
 }
 
 pub fn item_path(ccx: @CrateContext, i: @ast::item) -> path {
-    let base = match *ccx.tcx.items.get(&i.id) {
+    let base = match ccx.tcx.items.get_copy(&i.id) {
         ast_map::node_item(_, p) => p,
             // separate map for paths?
         _ => fail!(~"item_path")
@@ -2428,7 +2402,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef {
       Some(&v) => v,
       None => {
         let mut exprt = false;
-        let val = match *tcx.items.get(&id) {
+        let val = match tcx.items.get_copy(&id) {
           ast_map::node_item(i, pth) => {
             let my_path = vec::append(/*bad*/copy *pth,
                                       ~[path_name(i.ident)]);
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index a02ac96e520bd..c3dc4f1e8eb2b 100644
--- a/src/librustc/middle/trans/build.rs
+++ b/src/librustc/middle/trans/build.rs
@@ -846,7 +846,7 @@ pub fn _UndefReturn(cx: block, Fn: ValueRef) -> ValueRef {
 
 pub fn add_span_comment(bcx: block, sp: span, text: &str) {
     let ccx = bcx.ccx();
-    if !ccx.sess.no_asm_comments() {
+    if ccx.sess.asm_comments() {
         let s = fmt!("%s (%s)", text, ccx.sess.codemap.span_to_str(sp));
         debug!("%s", copy s);
         add_comment(bcx, s);
@@ -856,7 +856,7 @@ pub fn add_span_comment(bcx: block, sp: span, text: &str) {
 pub fn add_comment(bcx: block, text: &str) {
     unsafe {
         let ccx = bcx.ccx();
-        if !ccx.sess.no_asm_comments() {
+        if ccx.sess.asm_comments() {
             let sanitized = str::replace(text, ~"$", ~"");
             let comment_text = ~"# " +
                 str::replace(sanitized, ~"\n", ~"\n\t# ");
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index fc645e2bb6cd4..87322393ab983 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -39,7 +39,6 @@ use middle::trans::monomorphize;
 use middle::trans::type_of;
 use middle::ty;
 use middle::typeck;
-use util::common::indenter;
 use util::ppaux::Repr;
 
 use syntax::ast;
@@ -340,16 +339,12 @@ pub fn trans_method_call(in_cx: block,
         node_id_type(in_cx, call_ex.callee_id),
         expr_ty(in_cx, call_ex),
         |cx| {
-            match cx.ccx().maps.method_map.find(&call_ex.id) {
+            match cx.ccx().maps.method_map.find_copy(&call_ex.id) {
                 Some(origin) => {
                     debug!("origin for %s: %s",
                            call_ex.repr(in_cx.tcx()),
                            origin.repr(in_cx.tcx()));
 
-                    // FIXME(#5562): removing this copy causes a segfault
-                    //               before stage2
-                    let origin = /*bad*/ copy *origin;
-
                     meth::trans_method_callee(cx,
                                               call_ex.callee_id,
                                               rcvr,
@@ -687,7 +682,6 @@ pub fn trans_arg_expr(bcx: block,
            self_mode,
            arg_expr.repr(bcx.tcx()),
            ret_flag.map(|v| bcx.val_str(*v)));
-    let _indenter = indenter();
 
     // translate the arg expr to a datum
     let arg_datumblock = match ret_flag {
@@ -722,7 +716,7 @@ pub fn trans_arg_expr(bcx: block,
             }
         }
     };
-    let mut arg_datum = arg_datumblock.datum;
+    let arg_datum = arg_datumblock.datum;
     let bcx = arg_datumblock.bcx;
 
     debug!("   arg datum: %s", arg_datum.to_str(bcx.ccx()));
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index 84100d7195ea0..e0a20f6490715 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -208,7 +208,6 @@ pub fn store_environment(bcx: block,
 
     // allocate closure in the heap
     let Result {bcx: bcx, val: llbox} = allocate_cbox(bcx, sigil, cdata_ty);
-    let mut temp_cleanups = ~[];
 
     // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a
     // tuple.  This could be a ptr in uniq or a box or on stack,
@@ -224,7 +223,7 @@ pub fn store_environment(bcx: block,
     for vec::eachi(bound_values) |i, bv| {
         debug!("Copy %s into closure", bv.to_str(ccx));
 
-        if !ccx.sess.no_asm_comments() {
+        if ccx.sess.asm_comments() {
             add_comment(bcx, fmt!("Copy %s into closure",
                                   bv.to_str(ccx)));
         }
@@ -244,9 +243,6 @@ pub fn store_environment(bcx: block,
         }
 
     }
-    for vec::each(temp_cleanups) |cleanup| {
-        revoke_clean(bcx, *cleanup);
-    }
 
     ClosureResult { llbox: llbox, cdata_ty: cdata_ty, bcx: bcx }
 }
@@ -424,7 +420,7 @@ pub fn trans_expr_fn(bcx: block,
 
     let Result {bcx: bcx, val: closure} = match sigil {
         ast::BorrowedSigil | ast::ManagedSigil | ast::OwnedSigil => {
-            let cap_vars = *ccx.maps.capture_map.get(&user_id);
+            let cap_vars = ccx.maps.capture_map.get_copy(&user_id);
             let ret_handle = match is_loop_body {Some(x) => x,
                                                  None => None};
             let ClosureResult {llbox, cdata_ty, bcx}
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index d31caffe77a14..442b5d25c8ba2 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -27,18 +27,18 @@ use middle::resolve;
 use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::build;
-use middle::trans::callee;
 use middle::trans::datum;
 use middle::trans::debuginfo;
-use middle::trans::expr;
 use middle::trans::glue;
 use middle::trans::reachable;
 use middle::trans::shape;
 use middle::trans::type_of;
 use middle::trans::type_use;
+use middle::trans::write_guard;
 use middle::ty::substs;
 use middle::ty;
 use middle::typeck;
+use middle::borrowck::root_map_key;
 use util::ppaux::{Repr};
 
 use core::cast::transmute;
@@ -467,28 +467,35 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
         scope_clean_changed(scope_info);
     }
 }
-pub fn add_clean_frozen_root(bcx: block, val: ValueRef, t: ty::t) {
-    debug!("add_clean_frozen_root(%s, %s, %s)",
-           bcx.to_str(), val_str(bcx.ccx().tn, val),
-           t.repr(bcx.tcx()));
-    let (root, rooted) = root_for_cleanup(bcx, val, t);
-    let cleanup_type = cleanup_type(bcx.tcx(), t);
+pub fn add_clean_return_to_mut(bcx: block,
+                               root_key: root_map_key,
+                               frozen_val_ref: ValueRef,
+                               bits_val_ref: ValueRef,
+                               filename_val: ValueRef,
+                               line_val: ValueRef) {
+    //! When an `@mut` has been frozen, we have to
+    //! call the lang-item `return_to_mut` when the
+    //! freeze goes out of scope. We need to pass
+    //! in both the value which was frozen (`frozen_val`) and
+    //! the value (`bits_val_ref`) which was returned when the
+    //! box was frozen initially. Here, both `frozen_val_ref` and
+    //! `bits_val_ref` are in fact pointers to stack slots.
+
+    debug!("add_clean_return_to_mut(%s, %s, %s)",
+           bcx.to_str(),
+           val_str(bcx.ccx().tn, frozen_val_ref),
+           val_str(bcx.ccx().tn, bits_val_ref));
     do in_scope_cx(bcx) |scope_info| {
         scope_info.cleanups.push(
-            clean_temp(val, |bcx| {
-                let bcx = callee::trans_lang_call(
-                    bcx,
-                    bcx.tcx().lang_items.return_to_mut_fn(),
-                    ~[
-                        build::Load(bcx,
-                                    build::PointerCast(bcx,
-                                                       root,
-                                                       T_ptr(T_ptr(T_i8()))))
-                    ],
-                    expr::Ignore
-                );
-                glue::drop_ty_root(bcx, root, rooted, t)
-            }, cleanup_type));
+            clean_temp(
+                frozen_val_ref,
+                |bcx| write_guard::return_to_mut(bcx,
+                                                 root_key,
+                                                 frozen_val_ref,
+                                                 bits_val_ref,
+                                                 filename_val,
+                                                 line_val),
+                normal_exit_only));
         scope_clean_changed(scope_info);
     }
 }
@@ -516,6 +523,7 @@ pub fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
 // drop glue checks whether it is zero.
 pub fn revoke_clean(cx: block, val: ValueRef) {
     do in_scope_cx(cx) |scope_info| {
+        let scope_info = &mut *scope_info; // FIXME(#5074) workaround borrowck
         let cleanup_pos = vec::position(
             scope_info.cleanups,
             |cu| match *cu {
@@ -534,9 +542,9 @@ pub fn revoke_clean(cx: block, val: ValueRef) {
 }
 
 pub fn block_cleanups(bcx: block) -> ~[cleanup] {
-    match *bcx.kind {
+    match bcx.kind {
        block_non_scope  => ~[],
-       block_scope(ref mut inf) => /*bad*/copy inf.cleanups
+       block_scope(inf) => /*bad*/copy inf.cleanups
     }
 }
 
@@ -545,7 +553,7 @@ pub enum block_kind {
     // cleaned up. May correspond to an actual block in the language, but also
     // to an implicit scope, for example, calls introduce an implicit scope in
     // which the arguments are evaluated and cleaned up.
-    block_scope(scope_info),
+    block_scope(@mut scope_info),
 
     // A non-scope block is a basic block created as a translation artifact
     // from translating code that expresses conditional logic rather than by
@@ -568,19 +576,29 @@ pub struct scope_info {
     landing_pad: Option<BasicBlockRef>,
 }
 
+pub impl scope_info {
+    fn empty_cleanups(&mut self) -> bool {
+        self.cleanups.is_empty()
+    }
+}
+
 pub trait get_node_info {
     fn info(&self) -> Option<NodeInfo>;
 }
 
 impl get_node_info for @ast::expr {
     fn info(&self) -> Option<NodeInfo> {
-        Some(NodeInfo { id: self.id, span: self.span })
+        Some(NodeInfo {id: self.id,
+                       callee_id: Some(self.callee_id),
+                       span: self.span})
     }
 }
 
 impl get_node_info for ast::blk {
     fn info(&self) -> Option<NodeInfo> {
-        Some(NodeInfo { id: self.node.id, span: self.span })
+        Some(NodeInfo {id: self.node.id,
+                       callee_id: None,
+                       span: self.span})
     }
 }
 
@@ -592,6 +610,7 @@ impl get_node_info for Option<@ast::expr> {
 
 pub struct NodeInfo {
     id: ast::node_id,
+    callee_id: Option<ast::node_id>,
     span: span
 }
 
@@ -611,7 +630,7 @@ pub struct block_ {
     unreachable: bool,
     parent: Option<block>,
     // The 'kind' of basic block this is.
-    kind: @mut block_kind,
+    kind: block_kind,
     // Is this block part of a landing pad?
     is_lpad: bool,
     // info about the AST node this block originated from, if any
@@ -630,7 +649,7 @@ pub fn block_(llbb: BasicBlockRef, parent: Option<block>, kind: block_kind,
         terminated: false,
         unreachable: false,
         parent: parent,
-        kind: @mut kind,
+        kind: kind,
         is_lpad: is_lpad,
         node_info: node_info,
         fcx: fcx
@@ -678,21 +697,17 @@ pub fn val_str(tn: @TypeNames, v: ValueRef) -> @str {
     return ty_str(tn, val_ty(v));
 }
 
-pub fn in_scope_cx(cx: block, f: &fn(si: &mut scope_info)) {
+pub fn in_scope_cx(cx: block, f: &fn(si: @mut scope_info)) {
     let mut cur = cx;
     loop {
-        {
-            // XXX: Borrow check bug workaround.
-            let kind: &mut block_kind = &mut *cur.kind;
-            match *kind {
-              block_scope(ref mut inf) => {
-                  debug!("in_scope_cx: selected cur=%s (cx=%s)",
-                         cur.to_str(), cx.to_str());
-                  f(inf);
-                  return;
-              }
-              _ => ()
+        match cur.kind {
+            block_scope(inf) => {
+                debug!("in_scope_cx: selected cur=%s (cx=%s)",
+                       cur.to_str(), cx.to_str());
+                f(inf);
+                return;
             }
+            _ => ()
         }
         cur = block_parent(cur);
     }
@@ -1517,6 +1532,15 @@ pub fn dummy_substs(tps: ~[ty::t]) -> ty::substs {
     }
 }
 
+pub fn filename_and_line_num_from_span(bcx: block,
+                                       span: span) -> (ValueRef, ValueRef) {
+    let loc = bcx.sess().parse_sess.cm.lookup_char_pos(span.lo);
+    let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name);
+    let filename = build::PointerCast(bcx, filename_cstr, T_ptr(T_i8()));
+    let line = C_int(bcx.ccx(), loc.line as int);
+    (filename, line)
+}
+
 // Casts a Rust bool value to an i1.
 pub fn bool_to_i1(bcx: block, llval: ValueRef) -> ValueRef {
     build::ICmp(bcx, lib::llvm::IntNE, llval, C_bool(false))
diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index 9d04905bcc978..48c5a96c8e7b2 100644
--- a/src/librustc/middle/trans/consts.rs
+++ b/src/librustc/middle/trans/consts.rs
@@ -157,7 +157,7 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef {
         if !ast_util::is_local(def_id) {
             def_id = inline::maybe_instantiate_inline(cx, def_id, true);
         }
-        match *cx.tcx.items.get(&def_id.node) {
+        match cx.tcx.items.get_copy(&def_id.node) {
             ast_map::node_item(@ast::item {
                 node: ast::item_const(_, subexpr), _
             }, _) => {
@@ -166,7 +166,7 @@ pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef {
             _ => cx.tcx.sess.bug(~"expected a const to be an item")
         }
     }
-    *cx.const_values.get(&def_id.node)
+    cx.const_values.get_copy(&def_id.node)
 }
 
 pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
@@ -194,18 +194,19 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
             match adj.autoref {
                 None => { }
                 Some(ref autoref) => {
-                    assert!(autoref.region == ty::re_static);
-                    assert!(autoref.mutbl != ast::m_mutbl);
                     // Don't copy data to do a deref+ref.
                     let llptr = match maybe_ptr {
                         Some(ptr) => ptr,
                         None => const_addr_of(cx, llconst)
                     };
-                    match autoref.kind {
-                        ty::AutoPtr => {
+                    match *autoref {
+                        ty::AutoUnsafe(m) |
+                        ty::AutoPtr(ty::re_static, m) => {
+                            assert!(m != ast::m_mutbl);
                             llconst = llptr;
                         }
-                        ty::AutoBorrowVec => {
+                        ty::AutoBorrowVec(ty::re_static, m) => {
+                            assert!(m != ast::m_mutbl);
                             let size = machine::llsize_of(cx,
                                                           val_ty(llconst));
                             assert!(abi::slice_elt_base == 0);
@@ -550,7 +551,7 @@ pub fn trans_const(ccx: @CrateContext, _e: @ast::expr, id: ast::node_id) {
         let g = base::get_item_val(ccx, id);
         // At this point, get_item_val has already translated the
         // constant's initializer to determine its LLVM type.
-        let v = *ccx.const_values.get(&id);
+        let v = ccx.const_values.get_copy(&id);
         llvm::LLVMSetInitializer(g, v);
         llvm::LLVMSetGlobalConstant(g, True);
     }
diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs
index c9a4f078c79db..f1192488bddb0 100644
--- a/src/librustc/middle/trans/controlflow.rs
+++ b/src/librustc/middle/trans/controlflow.rs
@@ -193,7 +193,7 @@ pub fn trans_log(log_ex: @ast::expr,
     };
 
     let global = if ccx.module_data.contains_key(&modname) {
-        *ccx.module_data.get(&modname)
+        ccx.module_data.get_copy(&modname)
     } else {
         let s = link::mangle_internal_name_by_path_and_seq(
             ccx, modpath, ~"loglevel");
@@ -243,8 +243,8 @@ pub fn trans_break_cont(bcx: block,
     let mut unwind = bcx;
     let mut target;
     loop {
-        match *unwind.kind {
-          block_scope(scope_info {
+        match unwind.kind {
+          block_scope(@scope_info {
             loop_break: Some(brk),
             loop_label: l,
             _
@@ -333,7 +333,7 @@ pub fn trans_fail_expr(bcx: block,
                 bcx, expr::trans_to_datum(bcx, arg_expr));
 
             if ty::type_is_str(arg_datum.ty) {
-                let (lldata, _lllen) = arg_datum.get_base_and_len(bcx);
+                let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx);
                 return trans_fail_value(bcx, sp_opt, lldata);
             } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) {
                 return bcx;
@@ -385,13 +385,7 @@ fn trans_fail_value(bcx: block,
 pub fn trans_fail_bounds_check(bcx: block, sp: span,
                                index: ValueRef, len: ValueRef) -> block {
     let _icx = bcx.insn_ctxt("trans_fail_bounds_check");
-    let ccx = bcx.ccx();
-
-    let loc = bcx.sess().parse_sess.cm.lookup_char_pos(sp.lo);
-    let line = C_int(ccx, loc.line as int);
-    let filename_cstr = C_cstr(bcx.ccx(), @/*bad*/copy loc.file.name);
-    let filename = PointerCast(bcx, filename_cstr, T_ptr(T_i8()));
-
+    let (filename, line) = filename_and_line_num_from_span(bcx, sp);
     let args = ~[filename, line, index, len];
     let bcx = callee::trans_lang_call(
         bcx, bcx.tcx().lang_items.fail_bounds_check_fn(), args, expr::Ignore);
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index c6b32930c5f71..c19650e3b6848 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -87,17 +87,16 @@
 
 use lib;
 use lib::llvm::ValueRef;
-use middle::borrowck::{RootInfo, root_map_key};
 use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
-use middle::trans::callee;
 use middle::trans::common::*;
 use middle::trans::common;
 use middle::trans::expr;
 use middle::trans::glue;
 use middle::trans::tvec;
 use middle::trans::type_of;
+use middle::trans::write_guard;
 use middle::ty;
 use util::common::indenter;
 use util::ppaux::ty_to_str;
@@ -105,6 +104,7 @@ use util::ppaux::ty_to_str;
 use core::container::Set; // XXX: this should not be necessary
 use core::to_bytes;
 use syntax::ast;
+use syntax::codemap::span;
 use syntax::parse::token::special_idents;
 
 #[deriving(Eq)]
@@ -516,59 +516,6 @@ pub impl Datum {
         }
     }
 
-    fn root(&self, bcx: block, root_info: RootInfo) -> block {
-        /*!
-         *
-         * In some cases, borrowck will decide that an @T/@[]/@str
-         * value must be rooted for the program to be safe.  In that
-         * case, we will call this function, which will stash a copy
-         * away until we exit the scope `scope_id`. */
-
-        debug!("root(scope_id=%?, freezes=%?, self=%?)",
-               root_info.scope, root_info.freezes, self.to_str(bcx.ccx()));
-
-        if bcx.sess().trace() {
-            trans_trace(
-                bcx, None,
-                @fmt!("preserving until end of scope %d",
-                     root_info.scope));
-        }
-
-        let scratch = scratch_datum(bcx, self.ty, true);
-        self.copy_to_datum(bcx, INIT, scratch);
-        add_root_cleanup(bcx, root_info, scratch.val, scratch.ty);
-
-        // If we need to freeze the box, do that now.
-        if root_info.freezes {
-            callee::trans_lang_call(
-                bcx,
-                bcx.tcx().lang_items.borrow_as_imm_fn(),
-                ~[
-                    Load(bcx,
-                         PointerCast(bcx,
-                                     scratch.val,
-                                     T_ptr(T_ptr(T_i8()))))
-                ],
-                expr::Ignore)
-        } else {
-            bcx
-        }
-    }
-
-    fn perform_write_guard(&self, bcx: block) -> block {
-        // Create scratch space, but do not root it.
-        let llval = match self.mode {
-            ByValue => self.val,
-            ByRef => Load(bcx, self.val),
-        };
-
-        callee::trans_lang_call(
-            bcx,
-            bcx.tcx().lang_items.check_not_borrowed_fn(),
-            ~[ PointerCast(bcx, llval, T_ptr(T_i8())) ],
-            expr::Ignore)
-    }
-
     fn drop_val(&self, bcx: block) -> block {
         if !ty::type_needs_drop(bcx.tcx(), self.ty) {
             return bcx;
@@ -620,32 +567,20 @@ pub impl Datum {
 
     fn try_deref(&self,
         bcx: block,            // block wherein to generate insn's
-        expr_id: ast::node_id, // id of expr being deref'd
+        span: span,            // location where deref occurs
+        expr_id: ast::node_id, // id of deref expr
         derefs: uint,          // number of times deref'd already
         is_auto: bool)         // if true, only deref if auto-derefable
         -> (Option<Datum>, block)
     {
         let ccx = bcx.ccx();
 
-        debug!("try_deref(expr_id=%d, derefs=%?, is_auto=%b, self=%?)",
+        debug!("try_deref(expr_id=%?, derefs=%?, is_auto=%b, self=%?)",
                expr_id, derefs, is_auto, self.to_str(bcx.ccx()));
-        let _indenter = indenter();
-
-        // root the autoderef'd value, if necessary:
-        //
-        // (Note: root'd values are always boxes)
-        let key = root_map_key { id: expr_id, derefs: derefs };
-        let bcx = match ccx.maps.root_map.find(&key) {
-            None => bcx,
-            Some(&root_info) => self.root(bcx, root_info)
-        };
 
-        // Perform the write guard, if necessary.
-        //
-        // (Note: write-guarded values are always boxes)
-        let bcx = if ccx.maps.write_guard_map.contains(&key) {
-            self.perform_write_guard(bcx)
-        } else { bcx };
+        let bcx =
+            write_guard::root_and_write_guard(
+                self, bcx, span, expr_id, derefs);
 
         match ty::get(self.ty).sty {
             ty::ty_box(_) | ty::ty_uniq(_) => {
@@ -755,10 +690,10 @@ pub impl Datum {
     }
 
     fn deref(&self, bcx: block,
-             expr: @ast::expr,  // the expression whose value is being deref'd
+             expr: @ast::expr,  // the deref expression
              derefs: uint)
           -> DatumBlock {
-        match self.try_deref(bcx, expr.id, derefs, false) {
+        match self.try_deref(bcx, expr.span, expr.id, derefs, false) {
             (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres },
             (None, _) => {
                 bcx.ccx().sess.span_bug(expr.span,
@@ -768,6 +703,7 @@ pub impl Datum {
     }
 
     fn autoderef(&self, bcx: block,
+                 span: span,
                  expr_id: ast::node_id,
                  max: uint)
               -> DatumBlock {
@@ -782,7 +718,7 @@ pub impl Datum {
         let mut bcx = bcx;
         while derefs < max {
             derefs += 1u;
-            match datum.try_deref(bcx, expr_id, derefs, true) {
+            match datum.try_deref(bcx, span, expr_id, derefs, true) {
                 (None, new_bcx) => { bcx = new_bcx; break }
                 (Some(datum_deref), new_bcx) => {
                     datum = datum_deref;
@@ -798,8 +734,34 @@ pub impl Datum {
         DatumBlock { bcx: bcx, datum: datum }
     }
 
-    fn get_base_and_len(&self, bcx: block) -> (ValueRef, ValueRef) {
-        tvec::get_base_and_len(bcx, self.to_appropriate_llval(bcx), self.ty)
+    fn get_vec_base_and_len(&self,
+                            mut bcx: block,
+                            span: span,
+                            expr_id: ast::node_id)
+                            -> (block, ValueRef, ValueRef) {
+        //! Converts a vector into the slice pair. Performs rooting
+        //! and write guards checks.
+
+        // only imp't for @[] and @str, but harmless
+        bcx = write_guard::root_and_write_guard(self, bcx, span, expr_id, 0);
+        let (base, len) = self.get_vec_base_and_len_no_root(bcx);
+        (bcx, base, len)
+    }
+
+    fn get_vec_base_and_len_no_root(&self, bcx: block) -> (ValueRef, ValueRef) {
+        //! Converts a vector into the slice pair. Des not root
+        //! nor perform write guard checks.
+
+        let llval = self.to_appropriate_llval(bcx);
+        tvec::get_base_and_len(bcx, llval, self.ty)
+    }
+
+    fn root_and_write_guard(&self,
+                            bcx: block,
+                            span: span,
+                            expr_id: ast::node_id,
+                            derefs: uint) -> block {
+        write_guard::root_and_write_guard(self, bcx, span, expr_id, derefs)
     }
 
     fn to_result(&self, bcx: block) -> common::Result {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index b7a1a90741c43..1e5680aff3817 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -863,7 +863,7 @@ pub fn create_local_var(bcx: block, local: @ast::local)
         bcx.tcx().sess.span_bug(local.span, "local is bound to something weird");
       }
       option::None => {
-        match *bcx.fcx.lllocals.get(&local.node.pat.id) {
+        match bcx.fcx.lllocals.get_copy(&local.node.pat.id) {
           local_imm(v) => v,
           _ => bcx.tcx().sess.span_bug(local.span, "local is bound to something weird")
         }
@@ -915,7 +915,7 @@ pub fn create_arg(bcx: block, arg: ast::arg, sp: span)
             };
             update_cache(cache, tg, argument_metadata(mdval));
 
-            let llptr = match *fcx.llargs.get(&arg.id) {
+            let llptr = match fcx.llargs.get_copy(&arg.id) {
               local_mem(v) | local_imm(v) => v,
             };
             let declargs = ~[llmdnode(~[llptr]), mdnode];
@@ -958,7 +958,7 @@ pub fn create_function(fcx: fn_ctxt) -> @Metadata<SubProgramMetadata> {
     let sp = fcx.span.get();
     debug!("%s", cx.sess.codemap.span_to_str(sp));
 
-    let (ident, ret_ty, id) = match *cx.tcx.items.get(&fcx.id) {
+    let (ident, ret_ty, id) = match cx.tcx.items.get_copy(&fcx.id) {
       ast_map::node_item(item, _) => {
         match item.node {
           ast::item_fn(ref decl, _, _, _, _) => {
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 7cdd7c3799ec4..0e8b2e0474661 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -123,7 +123,6 @@ use back::abi;
 use lib;
 use lib::llvm::{ValueRef, TypeRef, llvm};
 use metadata::csearch;
-use middle::borrowck::root_map_key;
 use middle::trans::_match;
 use middle::trans::adt;
 use middle::trans::asm;
@@ -146,9 +145,9 @@ use middle::trans::type_of;
 use middle::ty;
 use middle::ty::struct_mutable_fields;
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn,
-                 AutoDerefRef, AutoAddEnv};
+                 AutoDerefRef, AutoAddEnv, AutoUnsafe};
 use util::common::indenter;
-use util::ppaux::ty_to_str;
+use util::ppaux::Repr;
 
 use core::cast::transmute;
 use core::hashmap::HashMap;
@@ -201,33 +200,34 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
                 trans_to_datum_unadjusted(bcx, expr)
             });
 
+            debug!("unadjusted datum: %s", datum.to_str(bcx.ccx()));
+
             if adj.autoderefs > 0 {
                 let DatumBlock { bcx: new_bcx, datum: new_datum } =
-                    datum.autoderef(bcx, expr.id, adj.autoderefs);
+                    datum.autoderef(bcx, expr.span, expr.id, adj.autoderefs);
                 datum = new_datum;
                 bcx = new_bcx;
             }
 
             datum = match adj.autoref {
-                None => datum,
-                Some(ref autoref) => {
-                    match autoref.kind {
-                        AutoPtr => {
-                            unpack_datum!(bcx, auto_ref(bcx, datum))
-                        }
-                        AutoBorrowVec => {
-                            unpack_datum!(bcx, auto_slice(bcx, datum))
-                        }
-                        AutoBorrowVecRef => {
-                            unpack_datum!(bcx, auto_slice_and_ref(bcx, datum))
-                        }
-                        AutoBorrowFn => {
-                            // currently, all closure types are
-                            // represented precisely the same, so no
-                            // runtime adjustment is required:
-                            datum
-                        }
-                    }
+                None => {
+                    datum
+                }
+                Some(AutoUnsafe(*)) | // region + unsafe ptrs have same repr
+                Some(AutoPtr(*)) => {
+                    unpack_datum!(bcx, auto_ref(bcx, datum))
+                }
+                Some(AutoBorrowVec(*)) => {
+                    unpack_datum!(bcx, auto_slice(bcx, expr, datum))
+                }
+                Some(AutoBorrowVecRef(*)) => {
+                    unpack_datum!(bcx, auto_slice_and_ref(bcx, expr, datum))
+                }
+                Some(AutoBorrowFn(*)) => {
+                    // currently, all closure types are
+                    // represented precisely the same, so no
+                    // runtime adjustment is required:
+                    datum
                 }
             };
 
@@ -241,7 +241,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)}
     }
 
-    fn auto_slice(bcx: block, datum: Datum) -> DatumBlock {
+    fn auto_slice(bcx: block, expr: @ast::expr, datum: Datum) -> DatumBlock {
         // This is not the most efficient thing possible; since slices
         // are two words it'd be better if this were compiled in
         // 'dest' mode, but I can't find a nice way to structure the
@@ -250,7 +250,10 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
 
         let tcx = bcx.tcx();
         let unit_ty = ty::sequence_element_type(tcx, datum.ty);
-        let (base, len) = datum.get_base_and_len(bcx);
+
+        // FIXME(#6272) need to distinguish "auto-slice" from explicit index?
+        let (bcx, base, len) =
+            datum.get_vec_base_and_len(bcx, expr.span, expr.id);
 
         // this type may have a different region/mutability than the
         // real one, but it will have the same runtime representation
@@ -273,7 +276,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
 
         let tcx = bcx.tcx();
         let closure_ty = expr_ty_adjusted(bcx, expr);
-        debug!("add_env(closure_ty=%s)", ty_to_str(tcx, closure_ty));
+        debug!("add_env(closure_ty=%s)", closure_ty.repr(tcx));
         let scratch = scratch_datum(bcx, closure_ty, false);
         let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]);
         assert!(datum.appropriate_mode() == ByValue);
@@ -283,8 +286,10 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         DatumBlock {bcx: bcx, datum: scratch}
     }
 
-    fn auto_slice_and_ref(bcx: block, datum: Datum) -> DatumBlock {
-        let DatumBlock { bcx, datum } = auto_slice(bcx, datum);
+    fn auto_slice_and_ref(bcx: block,
+                          expr: @ast::expr,
+                          datum: Datum) -> DatumBlock {
+        let DatumBlock { bcx, datum } = auto_slice(bcx, expr, datum);
         auto_ref(bcx, datum)
     }
 }
@@ -562,7 +567,6 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
 
 fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
                                dest: Dest) -> block {
-    let mut bcx = bcx;
     let _icx = bcx.insn_ctxt("trans_rvalue_dps_unadjusted");
     let tcx = bcx.tcx();
 
@@ -612,7 +616,7 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
             let sigil = ty::ty_closure_sigil(expr_ty);
             debug!("translating fn_block %s with type %s",
                    expr_to_str(expr, tcx.sess.intr()),
-                   ty_to_str(tcx, expr_ty));
+                   expr_ty.repr(tcx));
             return closure::trans_expr_fn(bcx, sigil, decl, body,
                                           expr.id, expr.id,
                                           None, dest);
@@ -820,67 +824,35 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
 
     trace_span!(bcx, expr.span, @shorten(bcx.expr_to_str(expr)));
 
-    let unrooted_datum = unpack_datum!(bcx, unrooted(bcx, expr));
-
-    // If the lvalue must remain rooted, create a scratch datum, copy
-    // the lvalue in there, and then arrange for it to be cleaned up
-    // at the end of the scope with id `scope_id`:
-    let root_key = root_map_key { id: expr.id, derefs: 0u };
-    for bcx.ccx().maps.root_map.find(&root_key).each |&root_info| {
-        bcx = unrooted_datum.root(bcx, *root_info);
-    }
-
-    return DatumBlock {bcx: bcx, datum: unrooted_datum};
-
-    fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock {
-        /*!
-         *
-         * Translates `expr`.  Note that this version generally
-         * yields an unrooted, unmoved version.  Rooting and possible
-         * moves are dealt with above in trans_lvalue_unadjusted().
-         *
-         * One exception is if `expr` refers to a local variable,
-         * in which case the source may already be FromMovedLvalue
-         * if appropriate.
-         */
-
-        let mut bcx = bcx;
-
-        match expr.node {
-            ast::expr_paren(e) => {
-                return unrooted(bcx, e);
-            }
-            ast::expr_path(_) => {
-                return trans_def_lvalue(bcx, expr, bcx.def(expr.id));
-            }
-            ast::expr_field(base, ident, _) => {
-                return trans_rec_field(bcx, base, ident);
-            }
-            ast::expr_index(base, idx) => {
-                return trans_index(bcx, expr, base, idx);
-            }
-            ast::expr_unary(ast::deref, base) => {
-                let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-                return basedatum.deref(bcx, base, 0);
-            }
-            _ => {
-                bcx.tcx().sess.span_bug(
-                    expr.span,
-                    fmt!("trans_lvalue reached fall-through case: %?",
-                         expr.node));
-            }
+    return match expr.node {
+        ast::expr_paren(e) => {
+            trans_lvalue_unadjusted(bcx, e)
         }
-    }
+        ast::expr_path(_) => {
+            trans_def_lvalue(bcx, expr, bcx.def(expr.id))
+        }
+        ast::expr_field(base, ident, _) => {
+            trans_rec_field(bcx, base, ident)
+        }
+        ast::expr_index(base, idx) => {
+            trans_index(bcx, expr, base, idx)
+        }
+        ast::expr_unary(ast::deref, base) => {
+            let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base));
+            basedatum.deref(bcx, expr, 0)
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(
+                expr.span,
+                fmt!("trans_lvalue reached fall-through case: %?",
+                     expr.node));
+        }
+    };
 
     fn trans_rec_field(bcx: block,
                        base: @ast::expr,
                        field: ast::ident) -> DatumBlock {
-        /*!
-         *
-         * Translates `base.field`.  Note that this version always
-         * yields an unrooted, unmoved version.  Rooting and possible
-         * moves are dealt with above in trans_lvalue_unadjusted().
-         */
+        //! Translates `base.field`.
 
         let mut bcx = bcx;
         let _icx = bcx.insn_ctxt("trans_rec_field");
@@ -904,12 +876,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
                    index_expr: @ast::expr,
                    base: @ast::expr,
                    idx: @ast::expr) -> DatumBlock {
-        /*!
-         *
-         * Translates `base[idx]`.  Note that this version always
-         * yields an unrooted, unmoved version.  Rooting and possible
-         * moves are dealt with above in trans_lvalue_unadjusted().
-         */
+        //! Translates `base[idx]`.
 
         let _icx = bcx.insn_ctxt("trans_index");
         let ccx = bcx.ccx();
@@ -940,7 +907,8 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
         let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
         base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix");
 
-        let mut (base, len) = base_datum.get_base_and_len(bcx);
+        let mut (bcx, base, len) =
+            base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id);
 
         if ty::type_is_str(base_ty) {
             // acccount for null terminator in the case of string
@@ -972,14 +940,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
                         def: ast::def)
         -> DatumBlock
     {
-        /*!
-         *
-         * Translates a reference to a path.  Note that this version
-         * generally yields an unrooted, unmoved version.  Rooting and
-         * possible moves are dealt with above in
-         * trans_lvalue_unadjusted(), with the caveat that local variables
-         * may already be in move mode.
-         */
+        //! Translates a reference to a path.
 
         let _icx = bcx.insn_ctxt("trans_def_lvalue");
         let ccx = bcx.ccx();
@@ -1087,6 +1048,9 @@ pub fn trans_local_var(bcx: block, def: ast::def) -> Datum {
                 }
             };
 
+            debug!("def_self() reference, self_info.t=%s",
+                   self_info.t.repr(bcx.tcx()));
+
             // This cast should not be necessary. We should cast self *once*,
             // but right now this conflicts with default methods.
             let real_self_ty = monomorphize_type(bcx, self_info.t);
@@ -1150,10 +1114,10 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
                     tcx.sess.bug(fmt!(
                         "cannot get field types from the enum type %s \
                          without a node ID",
-                        ty_to_str(tcx, ty)));
+                        ty.repr(tcx)));
                 }
                 Some(node_id) => {
-                    match *tcx.def_map.get(&node_id) {
+                    match tcx.def_map.get_copy(&node_id) {
                         ast::def_variant(enum_id, variant_id) => {
                             let variant_info = ty::enum_variant_with_id(
                                 tcx, enum_id, variant_id);
@@ -1172,7 +1136,7 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
         _ => {
             tcx.sess.bug(fmt!(
                 "cannot get field types from the type %s",
-                ty_to_str(tcx, ty)));
+                ty.repr(tcx)));
         }
     }
 }
@@ -1403,7 +1367,6 @@ fn trans_eager_binop(bcx: block,
                      lhs_datum: &Datum,
                      rhs_datum: &Datum)
                   -> DatumBlock {
-    let mut bcx = bcx;
     let _icx = bcx.insn_ctxt("trans_eager_binop");
 
     let lhs = lhs_datum.to_appropriate_llval(bcx);
@@ -1573,7 +1536,7 @@ fn trans_overloaded_op(bcx: block,
                        ret_ty: ty::t,
                        dest: Dest)
                        -> block {
-    let origin = *bcx.ccx().maps.method_map.get(&expr.id);
+    let origin = bcx.ccx().maps.method_map.get_copy(&expr.id);
     let fty = node_id_type(bcx, expr.callee_id);
     callee::trans_call_inner(bcx,
                              expr.info(),
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 0e2bf80cc5e84..21e29b9ad8259 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -724,7 +724,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
             let in_type_size = machine::llbitsize_of_real(ccx, llintype);
             let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
             if in_type_size != out_type_size {
-                let sp = match *ccx.tcx.items.get(&ref_id.get()) {
+                let sp = match ccx.tcx.items.get_copy(&ref_id.get()) {
                     ast_map::node_expr(e) => e.span,
                     _ => fail!(~"transmute has non-expr arg"),
                 };
diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs
index 35136410268ff..e5c6244879d32 100644
--- a/src/librustc/middle/trans/inline.rs
+++ b/src/librustc/middle/trans/inline.rs
@@ -29,96 +29,101 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::def_id,
     -> ast::def_id {
     let _icx = ccx.insn_ctxt("maybe_instantiate_inline");
     match ccx.external.find(&fn_id) {
-      Some(&Some(node_id)) => {
-        // Already inline
-        debug!("maybe_instantiate_inline(%s): already inline as node id %d",
-               ty::item_path_str(ccx.tcx, fn_id), node_id);
-        local_def(node_id)
-      }
-      Some(&None) => fn_id, // Not inlinable
-      None => { // Not seen yet
-        match csearch::maybe_get_item_ast(
+        Some(&Some(node_id)) => {
+            // Already inline
+            debug!("maybe_instantiate_inline(%s): already inline as node id %d",
+                   ty::item_path_str(ccx.tcx, fn_id), node_id);
+            return local_def(node_id);
+        }
+        Some(&None) => {
+            return fn_id; // Not inlinable
+        }
+        None => {
+            // Not seen yet
+        }
+    }
+
+    let csearch_result =
+        csearch::maybe_get_item_ast(
             ccx.tcx, fn_id,
             |a,b,c,d| {
                 astencode::decode_inlined_item(a, b, ccx.maps,
                                                /*bad*/ copy c, d)
-            }) {
-
-          csearch::not_found => {
+            });
+    return match csearch_result {
+        csearch::not_found => {
             ccx.external.insert(fn_id, None);
             fn_id
-          }
-          csearch::found(ast::ii_item(item)) => {
+        }
+        csearch::found(ast::ii_item(item)) => {
             ccx.external.insert(fn_id, Some(item.id));
             ccx.stats.n_inlines += 1;
             if translate { trans_item(ccx, item); }
             local_def(item.id)
-          }
-          csearch::found(ast::ii_foreign(item)) => {
-            ccx.external.insert(fn_id, Some(item.id));
-            local_def(item.id)
-          }
-          csearch::found_parent(parent_id, ast::ii_item(item)) => {
-            ccx.external.insert(parent_id, Some(item.id));
-            let mut my_id = 0;
-            match item.node {
-              ast::item_enum(_, _) => {
-                let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
-                let vs_there = ty::enum_variants(ccx.tcx, parent_id);
-                for vec::each2(*vs_here, *vs_there) |here, there| {
-                    if there.id == fn_id { my_id = here.id.node; }
-                    ccx.external.insert(there.id, Some(here.id.node));
-                }
+        }
+        csearch::found(ast::ii_foreign(item)) => {
+          ccx.external.insert(fn_id, Some(item.id));
+          local_def(item.id)
+        }
+        csearch::found_parent(parent_id, ast::ii_item(item)) => {
+          ccx.external.insert(parent_id, Some(item.id));
+          let mut my_id = 0;
+          match item.node {
+            ast::item_enum(_, _) => {
+              let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
+              let vs_there = ty::enum_variants(ccx.tcx, parent_id);
+              for vec::each2(*vs_here, *vs_there) |here, there| {
+                  if there.id == fn_id { my_id = here.id.node; }
+                  ccx.external.insert(there.id, Some(here.id.node));
               }
-              _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \
-                    non-enum parent")
             }
-            if translate { trans_item(ccx, item); }
-            local_def(my_id)
-          }
-          csearch::found_parent(_, _) => {
-              ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \
-               with a non-item parent");
+            _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \
+                  non-enum parent")
           }
-          csearch::found(ast::ii_method(impl_did, mth)) => {
-            ccx.stats.n_inlines += 1;
-            ccx.external.insert(fn_id, Some(mth.id));
-            let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did);
-            let num_type_params =
-                impl_tpt.generics.type_param_defs.len() +
-                mth.generics.ty_params.len();
-            if translate && num_type_params == 0 {
-                let llfn = get_item_val(ccx, mth.id);
-                let path = vec::append(
-                    ty::item_path(ccx.tcx, impl_did),
-                    ~[path_name(mth.ident)]);
-                let self_kind = match mth.self_ty.node {
-                    ast::sty_static => no_self,
-                    _ => {
-                        let self_ty = ty::node_id_to_type(ccx.tcx,
-                                                          mth.self_id);
-                        debug!("calling inline trans_fn with self_ty %s",
-                               ty_to_str(ccx.tcx, self_ty));
-                        match mth.self_ty.node {
-                            ast::sty_value => impl_owned_self(self_ty),
-                            _ => impl_self(self_ty),
-                        }
-                    }
-                };
-                trans_fn(ccx,
-                         path,
-                         &mth.decl,
-                         &mth.body,
-                         llfn,
-                         self_kind,
-                         None,
-                         mth.id,
-                         Some(impl_did),
-                         []);
-            }
-            local_def(mth.id)
+          if translate { trans_item(ccx, item); }
+          local_def(my_id)
+        }
+        csearch::found_parent(_, _) => {
+            ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \
+             with a non-item parent");
+        }
+        csearch::found(ast::ii_method(impl_did, mth)) => {
+          ccx.stats.n_inlines += 1;
+          ccx.external.insert(fn_id, Some(mth.id));
+          let impl_tpt = ty::lookup_item_type(ccx.tcx, impl_did);
+          let num_type_params =
+              impl_tpt.generics.type_param_defs.len() +
+              mth.generics.ty_params.len();
+          if translate && num_type_params == 0 {
+              let llfn = get_item_val(ccx, mth.id);
+              let path = vec::append(
+                  ty::item_path(ccx.tcx, impl_did),
+                  ~[path_name(mth.ident)]);
+              let self_kind = match mth.self_ty.node {
+                  ast::sty_static => no_self,
+                  _ => {
+                      let self_ty = ty::node_id_to_type(ccx.tcx,
+                                                        mth.self_id);
+                      debug!("calling inline trans_fn with self_ty %s",
+                             ty_to_str(ccx.tcx, self_ty));
+                      match mth.self_ty.node {
+                          ast::sty_value => impl_owned_self(self_ty),
+                          _ => impl_self(self_ty),
+                      }
+                  }
+              };
+              trans_fn(ccx,
+                       path,
+                       &mth.decl,
+                       &mth.body,
+                       llfn,
+                       self_kind,
+                       None,
+                       mth.id,
+                       Some(impl_did),
+                       []);
           }
+          local_def(mth.id)
         }
-      }
-    }
+    };
 }
diff --git a/src/librustc/middle/trans/machine.rs b/src/librustc/middle/trans/machine.rs
index b1349104e5490..ff9faa24376d9 100644
--- a/src/librustc/middle/trans/machine.rs
+++ b/src/librustc/middle/trans/machine.rs
@@ -118,7 +118,7 @@ pub fn llalign_of(cx: @CrateContext, t: TypeRef) -> ValueRef {
 // Computes the size of the data part of an enum.
 pub fn static_size_of_enum(cx: @CrateContext, t: ty::t) -> uint {
     if cx.enum_sizes.contains_key(&t) {
-        return *cx.enum_sizes.get(&t);
+        return cx.enum_sizes.get_copy(&t);
     }
 
     debug!("static_size_of_enum %s", ty_to_str(cx.tcx, t));
diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index a90475d9ed085..934a995b58841 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -44,6 +44,11 @@ pub fn trans_impl(ccx: @CrateContext, path: path, name: ast::ident,
                   methods: &[@ast::method], generics: &ast::Generics,
                   self_ty: Option<ty::t>, id: ast::node_id) {
     let _icx = ccx.insn_ctxt("impl::trans_impl");
+    let tcx = ccx.tcx;
+
+    debug!("trans_impl(path=%s, name=%s, self_ty=%s, id=%?)",
+           path.repr(tcx), name.repr(tcx), self_ty.repr(tcx), id);
+
     if !generics.ty_params.is_empty() { return; }
     let sub_path = vec::append_one(path, path_name(name));
     for vec::each(methods) |method| {
@@ -307,7 +312,7 @@ pub fn trans_static_method_callee(bcx: block,
     };
 
     let mname = if method_id.crate == ast::local_crate {
-        match *bcx.tcx().items.get(&method_id.node) {
+        match bcx.tcx().items.get_copy(&method_id.node) {
             ast_map::node_trait_method(trait_method, _, _) => {
                 ast_util::trait_method_to_ty_method(trait_method).ident
             }
@@ -324,7 +329,7 @@ pub fn trans_static_method_callee(bcx: block,
             name=%s", method_id, callee_id, *ccx.sess.str_of(mname));
 
     let vtbls = resolve_vtables_in_fn_ctxt(
-        bcx.fcx, *ccx.maps.vtable_map.get(&callee_id));
+        bcx.fcx, ccx.maps.vtable_map.get_copy(&callee_id));
 
     match vtbls[bound_index] {
         typeck::vtable_static(impl_did, ref rcvr_substs, rcvr_origins) => {
@@ -362,7 +367,7 @@ pub fn method_from_methods(ms: &[@ast::method], name: ast::ident)
 pub fn method_with_name(ccx: @CrateContext, impl_id: ast::def_id,
                         name: ast::ident) -> ast::def_id {
     if impl_id.crate == ast::local_crate {
-        match *ccx.tcx.items.get(&impl_id.node) {
+        match ccx.tcx.items.get_copy(&impl_id.node) {
           ast_map::node_item(@ast::item {
                 node: ast::item_impl(_, _, _, ref ms),
                 _
@@ -380,7 +385,7 @@ pub fn method_with_name_or_default(ccx: @CrateContext,
                                    impl_id: ast::def_id,
                                    name: ast::ident) -> ast::def_id {
     if impl_id.crate == ast::local_crate {
-        match *ccx.tcx.items.get(&impl_id.node) {
+        match ccx.tcx.items.get_copy(&impl_id.node) {
           ast_map::node_item(@ast::item {
                 node: ast::item_impl(_, _, _, ref ms), _
           }, _) => {
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 2640dab1c04fb..6e25064186941 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -100,12 +100,14 @@ pub fn monomorphic_fn(ccx: @CrateContext,
     let tpt = ty::lookup_item_type(ccx.tcx, fn_id);
     let llitem_ty = tpt.ty;
 
-    let map_node = session::expect(ccx.sess, ccx.tcx.items.find(&fn_id.node),
-     || fmt!("While monomorphizing %?, couldn't find it in the item map \
-        (may have attempted to monomorphize an item defined in a different \
-        crate?)", fn_id));
+    let map_node = session::expect(
+        ccx.sess,
+        ccx.tcx.items.find_copy(&fn_id.node),
+        || fmt!("While monomorphizing %?, couldn't find it in the item map \
+                 (may have attempted to monomorphize an item \
+                 defined in a different crate?)", fn_id));
     // Get the path so that we can create a symbol
-    let (pt, name, span) = match *map_node {
+    let (pt, name, span) = match map_node {
       ast_map::node_item(i, pt) => (pt, i.ident, i.span),
       ast_map::node_variant(ref v, enm, pt) => (pt, (*v).node.name, enm.span),
       ast_map::node_method(m, _, pt) => (pt, m.ident, m.span),
@@ -134,6 +136,9 @@ pub fn monomorphic_fn(ccx: @CrateContext,
       ast_map::node_local(*) => {
           ccx.tcx.sess.bug(~"Can't monomorphize a local")
       }
+      ast_map::node_callee_scope(*) => {
+          ccx.tcx.sess.bug(~"Can't monomorphize a callee-scope")
+      }
       ast_map::node_struct_ctor(_, i, pt) => (pt, i.ident, i.span)
     };
 
@@ -182,7 +187,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
         self_ty: impl_ty_opt
     });
 
-    let lldecl = match *map_node {
+    let lldecl = match map_node {
       ast_map::node_item(i@@ast::item {
                 node: ast::item_fn(ref decl, _, _, _, ref body),
                 _
@@ -266,6 +271,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
       ast_map::node_trait_method(*) |
       ast_map::node_arg(*) |
       ast_map::node_block(*) |
+      ast_map::node_callee_scope(*) |
       ast_map::node_local(*) => {
         ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node))
       }
diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs
index 217eda870be9f..9bbf50397c35a 100644
--- a/src/librustc/middle/trans/reachable.rs
+++ b/src/librustc/middle/trans/reachable.rs
@@ -42,19 +42,19 @@ pub fn find_reachable(crate_mod: &_mod, exp_map2: resolve::ExportMap2,
                       tcx: ty::ctxt, method_map: typeck::method_map) -> map {
     let mut rmap = HashSet::new();
     {
-        let cx = ctx {
+        let cx = @mut ctx {
             exp_map2: exp_map2,
             tcx: tcx,
             method_map: method_map,
             rmap: &mut rmap
         };
-        traverse_public_mod(&cx, ast::crate_node_id, crate_mod);
-        traverse_all_resources_and_impls(&cx, crate_mod);
+        traverse_public_mod(cx, ast::crate_node_id, crate_mod);
+        traverse_all_resources_and_impls(cx, crate_mod);
     }
     return @rmap;
 }
 
-fn traverse_exports(cx: &ctx, mod_id: node_id) -> bool {
+fn traverse_exports(cx: @mut ctx, mod_id: node_id) -> bool {
     let mut found_export = false;
     match cx.exp_map2.find(&mod_id) {
       Some(ref exp2s) => {
@@ -68,23 +68,25 @@ fn traverse_exports(cx: &ctx, mod_id: node_id) -> bool {
     return found_export;
 }
 
-fn traverse_def_id(cx: &ctx, did: def_id) {
+fn traverse_def_id(cx: @mut ctx, did: def_id) {
     if did.crate != local_crate { return; }
     match cx.tcx.items.find(&did.node) {
         None => (), // This can happen for self, for example
         Some(&ast_map::node_item(item, _)) => traverse_public_item(cx, item),
         Some(&ast_map::node_method(_, impl_id, _)) => traverse_def_id(cx, impl_id),
         Some(&ast_map::node_foreign_item(item, _, _, _)) => {
+            let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
             cx.rmap.insert(item.id);
         }
         Some(&ast_map::node_variant(ref v, _, _)) => {
+            let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
             cx.rmap.insert(v.node.id);
         }
         _ => ()
     }
 }
 
-fn traverse_public_mod(cx: &ctx, mod_id: node_id, m: &_mod) {
+fn traverse_public_mod(cx: @mut ctx, mod_id: node_id, m: &_mod) {
     if !traverse_exports(cx, mod_id) {
         // No exports, so every local item is exported
         for m.items.each |item| {
@@ -93,16 +95,21 @@ fn traverse_public_mod(cx: &ctx, mod_id: node_id, m: &_mod) {
     }
 }
 
-fn traverse_public_item(cx: &ctx, item: @item) {
-    // FIXME #6021: naming rmap shouldn't be necessary
-    let rmap: &mut HashSet<node_id> = cx.rmap;
-    if rmap.contains(&item.id) { return; }
-    rmap.insert(item.id);
+fn traverse_public_item(cx: @mut ctx, item: @item) {
+    {
+        // FIXME #6021: naming rmap shouldn't be necessary
+        let cx = &mut *cx;
+        let rmap: &mut HashSet<node_id> = cx.rmap;
+        if rmap.contains(&item.id) { return; }
+        rmap.insert(item.id);
+    }
+
     match item.node {
       item_mod(ref m) => traverse_public_mod(cx, item.id, m),
       item_foreign_mod(ref nm) => {
           if !traverse_exports(cx, item.id) {
               for nm.items.each |item| {
+                  let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
                   cx.rmap.insert(item.id);
               }
           }
@@ -119,13 +126,17 @@ fn traverse_public_item(cx: &ctx, item: @item) {
                 m.generics.ty_params.len() > 0u ||
                 attr::find_inline_attr(m.attrs) != attr::ia_none
             {
-                cx.rmap.insert(m.id);
+                {
+                    let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
+                    cx.rmap.insert(m.id);
+                }
                 traverse_inline_body(cx, &m.body);
             }
         }
       }
       item_struct(ref struct_def, _) => {
         for struct_def.ctor_id.each |&ctor_id| {
+            let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
             cx.rmap.insert(ctor_id);
         }
       }
@@ -140,11 +151,12 @@ fn traverse_public_item(cx: &ctx, item: @item) {
     }
 }
 
-fn traverse_ty<'a, 'b>(ty: @Ty, cx: &'b ctx<'a>, v: visit::vt<&'b ctx<'a>>) {
-    // FIXME #6021: naming rmap shouldn't be necessary
-    let rmap: &mut HashSet<node_id> = cx.rmap;
-    if rmap.contains(&ty.id) { return; }
-    rmap.insert(ty.id);
+fn traverse_ty<'a>(ty: @Ty, cx: @mut ctx<'a>, v: visit::vt<@mut ctx<'a>>) {
+    {
+        let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
+        if cx.rmap.contains(&ty.id) { return; }
+        cx.rmap.insert(ty.id);
+    }
 
     match ty.node {
       ty_path(p, p_id) => {
@@ -163,9 +175,9 @@ fn traverse_ty<'a, 'b>(ty: @Ty, cx: &'b ctx<'a>, v: visit::vt<&'b ctx<'a>>) {
     }
 }
 
-fn traverse_inline_body(cx: &ctx, body: &blk) {
-    fn traverse_expr<'a, 'b>(e: @expr, cx: &'b ctx<'a>,
-                             v: visit::vt<&'b ctx<'a>>) {
+fn traverse_inline_body(cx: @mut ctx, body: &blk) {
+    fn traverse_expr<'a>(e: @expr, cx: @mut ctx<'a>,
+                         v: visit::vt<@mut ctx<'a>>) {
         match e.node {
           expr_path(_) => {
             match cx.tcx.def_map.find(&e.id) {
@@ -212,7 +224,7 @@ fn traverse_inline_body(cx: &ctx, body: &blk) {
     // Don't ignore nested items: for example if a generic fn contains a
     // generic impl (as in deque::create), we need to monomorphize the
     // impl as well
-    fn traverse_item(i: @item, cx: &ctx, _v: visit::vt<&ctx>) {
+    fn traverse_item(i: @item, cx: @mut ctx, _v: visit::vt<@mut ctx>) {
       traverse_public_item(cx, i);
     }
     visit::visit_block(body, cx, visit::mk_vt(@visit::Visitor {
@@ -222,7 +234,7 @@ fn traverse_inline_body(cx: &ctx, body: &blk) {
     }));
 }
 
-fn traverse_all_resources_and_impls(cx: &ctx, crate_mod: &_mod) {
+fn traverse_all_resources_and_impls(cx: @mut ctx, crate_mod: &_mod) {
     visit::visit_mod(
         crate_mod,
         codemap::dummy_sp(),
diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs
index 9bae056091852..9e1f10467e346 100644
--- a/src/librustc/middle/trans/reflect.rs
+++ b/src/librustc/middle/trans/reflect.rs
@@ -274,8 +274,9 @@ pub impl Reflector {
             let repr = adt::represent_type(bcx.ccx(), t);
             let variants = ty::substd_enum_variants(ccx.tcx, did, substs);
             let llptrty = T_ptr(type_of(ccx, t));
-            let (_, opaquety) = *(ccx.tcx.intrinsic_defs.find(&ccx.sess.ident_of(~"Opaque"))
-                                      .expect("Failed to resolve intrinsic::Opaque"));
+            let (_, opaquety) =
+                ccx.tcx.intrinsic_defs.find_copy(&ccx.sess.ident_of(~"Opaque"))
+                .expect("Failed to resolve intrinsic::Opaque");
             let opaqueptrty = ty::mk_ptr(ccx.tcx, ty::mt { ty: opaquety, mutbl: ast::m_imm });
 
             let make_get_disr = || {
@@ -374,7 +375,7 @@ pub fn emit_calls_to_trait_visit_ty(bcx: block,
     use syntax::parse::token::special_idents::tydesc;
     let final = sub_block(bcx, ~"final");
     assert!(bcx.ccx().tcx.intrinsic_defs.contains_key(&tydesc));
-    let (_, tydesc_ty) = *bcx.ccx().tcx.intrinsic_defs.get(&tydesc);
+    let (_, tydesc_ty) = bcx.ccx().tcx.intrinsic_defs.get_copy(&tydesc);
     let tydesc_ty = type_of(bcx.ccx(), tydesc_ty);
     let mut r = Reflector {
         visitor_val: visitor_val,
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index a842f91f0ed6e..fc27c11c06f24 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -110,8 +110,7 @@ pub fn type_of_non_gc_box(cx: @CrateContext, t: ty::t) -> TypeRef {
 
 pub fn sizing_type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
     match cx.llsizingtypes.find(&t) {
-        // FIXME(#5562): removing this copy causes a segfault in stage1 core
-        Some(t) => return /*bad*/ copy *t,
+        Some(t) => return *t,
         None => ()
     }
 
@@ -178,8 +177,7 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
 
     // Check the cache.
     match cx.lltypes.find(&t) {
-        // FIXME(#5562): removing this copy causes a segfault in stage1 core
-        Some(t) => return /*bad*/ copy *t,
+        Some(&t) => return t,
         None => ()
     }
 
diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs
index 794912d8307bc..94ef33e45bbe9 100644
--- a/src/librustc/middle/trans/type_use.rs
+++ b/src/librustc/middle/trans/type_use.rs
@@ -236,18 +236,11 @@ pub fn node_type_needs(cx: Context, use_: uint, id: node_id) {
 }
 
 pub fn mark_for_method_call(cx: Context, e_id: node_id, callee_id: node_id) {
+    let mut opt_static_did = None;
     for cx.ccx.maps.method_map.find(&e_id).each |mth| {
         match mth.origin {
           typeck::method_static(did) => {
-            for cx.ccx.tcx.node_type_substs.find(&callee_id).each |ts| {
-                // FIXME(#5562): removing this copy causes a segfault
-                //               before stage2
-                let ts = /*bad*/ copy **ts;
-                let type_uses = type_uses_for(cx.ccx, did, ts.len());
-                for vec::each2(*type_uses, ts) |uses, subst| {
-                    type_needs(cx, *uses, *subst)
-                }
-            }
+              opt_static_did = Some(did);
           }
           typeck::method_param(typeck::method_param {
               param_num: param,
@@ -259,6 +252,19 @@ pub fn mark_for_method_call(cx: Context, e_id: node_id, callee_id: node_id) {
               | typeck::method_super(*) => (),
         }
     }
+
+    // Note: we do not execute this code from within the each() call
+    // above because the recursive call to `type_needs` can trigger
+    // inlining and hence can cause `method_map` and
+    // `node_type_substs` to be modified.
+    for opt_static_did.each |&did| {
+        for cx.ccx.tcx.node_type_substs.find_copy(&callee_id).each |ts| {
+            let type_uses = type_uses_for(cx.ccx, did, ts.len());
+            for vec::each2(*type_uses, *ts) |uses, subst| {
+                type_needs(cx, *uses, *subst)
+            }
+        }
+    }
 }
 
 pub fn mark_for_expr(cx: Context, e: @expr) {
@@ -288,12 +294,11 @@ pub fn mark_for_expr(cx: Context, e: @expr) {
         }
       }
       expr_path(_) => {
-        for cx.ccx.tcx.node_type_substs.find(&e.id).each |ts| {
-            // FIXME(#5562): removing this copy causes a segfault before stage2
-            let ts = copy **ts;
-            let id = ast_util::def_id_of_def(*cx.ccx.tcx.def_map.get(&e.id));
+        let opt_ts = cx.ccx.tcx.node_type_substs.find_copy(&e.id);
+        for opt_ts.each |ts| {
+            let id = ast_util::def_id_of_def(cx.ccx.tcx.def_map.get_copy(&e.id));
             let uses_for_ts = type_uses_for(cx.ccx, id, ts.len());
-            for vec::each2(*uses_for_ts, ts) |uses, subst| {
+            for vec::each2(*uses_for_ts, *ts) |uses, subst| {
                 type_needs(cx, *uses, *subst)
             }
         }
diff --git a/src/librustc/middle/trans/write_guard.rs b/src/librustc/middle/trans/write_guard.rs
new file mode 100644
index 0000000000000..18f21b489b0b8
--- /dev/null
+++ b/src/librustc/middle/trans/write_guard.rs
@@ -0,0 +1,201 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Logic relating to rooting and write guards for managed values
+//! (`@` and `@mut`). This code is primarily for use by datum;
+//! it exists in its own module both to keep datum.rs bite-sized
+//! and for each in debugging (e.g., so you can use
+//! `RUST_LOG=rustc::middle::trans::write_guard`).
+
+use lib::llvm::ValueRef;
+use middle::borrowck::{RootInfo, root_map_key, DynaImm, DynaMut};
+use middle::trans::base::*;
+use middle::trans::build::*;
+use middle::trans::callee;
+use middle::trans::common::*;
+use middle::trans::datum::*;
+use middle::trans::expr;
+use middle::ty;
+use driver::session;
+use syntax::codemap::span;
+use syntax::ast;
+
+pub fn root_and_write_guard(datum: &Datum,
+                            mut bcx: block,
+                            span: span,
+                            expr_id: ast::node_id,
+                            derefs: uint) -> block {
+    let key = root_map_key { id: expr_id, derefs: derefs };
+    debug!("write_guard::root_and_write_guard(key=%?)", key);
+
+    // root the autoderef'd value, if necessary:
+    //
+    // (Note: root'd values are always boxes)
+    let ccx = bcx.ccx();
+    bcx = match ccx.maps.root_map.find(&key) {
+        None => bcx,
+        Some(&root_info) => root(datum, bcx, span, key, root_info)
+    };
+
+    // Perform the write guard, if necessary.
+    //
+    // (Note: write-guarded values are always boxes)
+    if ccx.maps.write_guard_map.contains(&key) {
+        perform_write_guard(datum, bcx, span)
+    } else {
+        bcx
+    }
+}
+
+pub fn return_to_mut(mut bcx: block,
+                     root_key: root_map_key,
+                     frozen_val_ref: ValueRef,
+                     bits_val_ref: ValueRef,
+                     filename_val: ValueRef,
+                     line_val: ValueRef) -> block {
+    debug!("write_guard::return_to_mut(root_key=%?, %s, %s, %s)",
+           root_key,
+           bcx.to_str(),
+           val_str(bcx.ccx().tn, frozen_val_ref),
+           val_str(bcx.ccx().tn, bits_val_ref));
+
+    let box_ptr =
+        Load(bcx, PointerCast(bcx,
+                              frozen_val_ref,
+                              T_ptr(T_ptr(T_i8()))));
+
+    let bits_val =
+        Load(bcx, bits_val_ref);
+
+    if bcx.tcx().sess.opts.optimize == session::No {
+        bcx = callee::trans_lang_call(
+            bcx,
+            bcx.tcx().lang_items.unrecord_borrow_fn(),
+            ~[
+                box_ptr,
+                bits_val,
+                filename_val,
+                line_val
+            ],
+            expr::Ignore);
+    }
+
+    callee::trans_lang_call(
+        bcx,
+        bcx.tcx().lang_items.return_to_mut_fn(),
+        ~[
+            box_ptr,
+            bits_val,
+            filename_val,
+            line_val
+        ],
+        expr::Ignore
+    )
+}
+
+fn root(datum: &Datum,
+        mut bcx: block,
+        span: span,
+        root_key: root_map_key,
+        root_info: RootInfo) -> block {
+    //! In some cases, borrowck will decide that an @T/@[]/@str
+    //! value must be rooted for the program to be safe.  In that
+    //! case, we will call this function, which will stash a copy
+    //! away until we exit the scope `scope_id`.
+
+    debug!("write_guard::root(root_key=%?, root_info=%?, datum=%?)",
+           root_key, root_info, datum.to_str(bcx.ccx()));
+
+    if bcx.sess().trace() {
+        trans_trace(
+            bcx, None,
+            @fmt!("preserving until end of scope %d",
+                  root_info.scope));
+    }
+
+    // First, root the datum. Note that we must zero this value,
+    // because sometimes we root on one path but not another.
+    // See e.g. #4904.
+    let scratch = scratch_datum(bcx, datum.ty, true);
+    datum.copy_to_datum(bcx, INIT, scratch);
+    let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope);
+    add_clean_temp_mem(cleanup_bcx, scratch.val, scratch.ty);
+
+    // Now, consider also freezing it.
+    match root_info.freeze {
+        None => {}
+        Some(freeze_kind) => {
+            let (filename, line) = filename_and_line_num_from_span(bcx, span);
+
+            // in this case, we don't have to zero, because
+            // scratch.val will be NULL should the cleanup get
+            // called without the freezing actually occurring, and
+            // return_to_mut checks for this condition.
+            let scratch_bits = scratch_datum(bcx, ty::mk_uint(), false);
+
+            let freeze_did = match freeze_kind {
+                DynaImm => bcx.tcx().lang_items.borrow_as_imm_fn(),
+                DynaMut => bcx.tcx().lang_items.borrow_as_mut_fn(),
+            };
+
+            let box_ptr = Load(bcx,
+                               PointerCast(bcx,
+                                           scratch.val,
+                                           T_ptr(T_ptr(T_i8()))));
+
+            bcx = callee::trans_lang_call(
+                bcx,
+                freeze_did,
+                ~[
+                    box_ptr,
+                    filename,
+                    line
+                ],
+                expr::SaveIn(scratch_bits.val));
+
+            if bcx.tcx().sess.opts.optimize == session::No {
+                bcx = callee::trans_lang_call(
+                    bcx,
+                    bcx.tcx().lang_items.record_borrow_fn(),
+                    ~[
+                        box_ptr,
+                        Load(bcx, scratch_bits.val),
+                        filename,
+                        line
+                    ],
+                    expr::Ignore);
+            }
+
+            add_clean_return_to_mut(
+                cleanup_bcx, root_key, scratch.val, scratch_bits.val,
+                filename, line);
+        }
+    }
+
+    bcx
+}
+
+fn perform_write_guard(datum: &Datum,
+                       bcx: block,
+                       span: span) -> block {
+    debug!("perform_write_guard");
+
+    let llval = datum.to_value_llval(bcx);
+    let (filename, line) = filename_and_line_num_from_span(bcx, span);
+
+    callee::trans_lang_call(
+        bcx,
+        bcx.tcx().lang_items.check_not_borrowed_fn(),
+        ~[PointerCast(bcx, llval, T_ptr(T_i8())),
+          filename,
+          line],
+        expr::Ignore)
+}
+
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index abf82c511e551..d3875bad13a7b 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -183,26 +183,21 @@ pub struct AutoDerefRef {
 
 #[auto_encode]
 #[auto_decode]
-pub struct AutoRef {
-    kind: AutoRefKind,
-    region: Region,
-    mutbl: ast::mutability
-}
-
-#[auto_encode]
-#[auto_decode]
-pub enum AutoRefKind {
+pub enum AutoRef {
     /// Convert from T to &T
-    AutoPtr,
+    AutoPtr(Region, ast::mutability),
 
     /// Convert from @[]/~[]/&[] to &[] (or str)
-    AutoBorrowVec,
+    AutoBorrowVec(Region, ast::mutability),
 
     /// Convert from @[]/~[]/&[] to &&[] (or str)
-    AutoBorrowVecRef,
+    AutoBorrowVecRef(Region, ast::mutability),
 
     /// Convert from @fn()/~fn()/&fn() to &fn()
-    AutoBorrowFn
+    AutoBorrowFn(Region),
+
+    /// Convert from T to *T
+    AutoUnsafe(ast::mutability)
 }
 
 // Stores information about provided methods (a.k.a. default methods) in
@@ -432,11 +427,20 @@ pub enum Region {
     /// A concrete region naming some expression within the current function.
     re_scope(node_id),
 
-    /// Static data that has an "infinite" lifetime.
+    /// Static data that has an "infinite" lifetime. Top in the region lattice.
     re_static,
 
     /// A region variable.  Should not exist after typeck.
-    re_infer(InferRegion)
+    re_infer(InferRegion),
+
+    /// Empty lifetime is for data that is never accessed.
+    /// Bottom in the region lattice. We treat re_empty somewhat
+    /// specially; at least right now, we do not generate instances of
+    /// it during the GLB computations, but rather
+    /// generate an error instead. This is to improve error messages.
+    /// The only way to get an instance of re_empty is to have a region
+    /// variable with no constraints.
+    re_empty,
 }
 
 pub impl Region {
@@ -1539,6 +1543,13 @@ pub fn type_is_ty_var(ty: t) -> bool {
 
 pub fn type_is_bool(ty: t) -> bool { get(ty).sty == ty_bool }
 
+pub fn type_is_self(ty: t) -> bool {
+    match get(ty).sty {
+        ty_self(*) => true,
+        _ => false
+    }
+}
+
 pub fn type_is_structural(ty: t) -> bool {
     match get(ty).sty {
       ty_struct(*) | ty_tup(_) | ty_enum(*) | ty_closure(_) | ty_trait(*) |
@@ -1939,7 +1950,7 @@ pub fn type_contents(cx: ctxt, ty: t) -> TypeContents {
 
         let _i = indenter();
 
-        let mut result = match get(ty).sty {
+        let result = match get(ty).sty {
             // Scalar and unique types are sendable, constant, and owned
             ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
             ty_bare_fn(_) | ty_ptr(_) => {
@@ -2789,6 +2800,17 @@ pub fn ty_region(tcx: ctxt,
     }
 }
 
+pub fn replace_fn_sig(cx: ctxt, fsty: &sty, new_sig: FnSig) -> t {
+    match *fsty {
+        ty_bare_fn(ref f) => mk_bare_fn(cx, BareFnTy {sig: new_sig, ..*f}),
+        ty_closure(ref f) => mk_closure(cx, ClosureTy {sig: new_sig, ..*f}),
+        ref s => {
+            cx.sess.bug(
+                fmt!("ty_fn_sig() called on non-fn type: %?", s));
+        }
+    }
+}
+
 pub fn replace_closure_return_type(tcx: ctxt, fn_type: t, ret_type: t) -> t {
     /*!
      *
@@ -2908,26 +2930,26 @@ pub fn adjust_ty(cx: ctxt,
             match adj.autoref {
                 None => adjusted_ty,
                 Some(ref autoref) => {
-                    match autoref.kind {
-                        AutoPtr => {
-                            mk_rptr(cx, autoref.region,
-                                    mt {ty: adjusted_ty,
-                                        mutbl: autoref.mutbl})
+                    match *autoref {
+                        AutoPtr(r, m) => {
+                            mk_rptr(cx, r, mt {ty: adjusted_ty, mutbl: m})
+                        }
+
+                        AutoBorrowVec(r, m) => {
+                            borrow_vec(cx, span, r, m, adjusted_ty)
                         }
 
-                        AutoBorrowVec => {
-                            borrow_vec(cx, span, autoref, adjusted_ty)
+                        AutoBorrowVecRef(r, m) => {
+                            adjusted_ty = borrow_vec(cx, span, r, m, adjusted_ty);
+                            mk_rptr(cx, r, mt {ty: adjusted_ty, mutbl: ast::m_imm})
                         }
 
-                        AutoBorrowVecRef => {
-                            adjusted_ty = borrow_vec(cx, span, autoref,
-                                                     adjusted_ty);
-                            mk_rptr(cx, autoref.region,
-                                    mt {ty: adjusted_ty, mutbl: ast::m_imm})
+                        AutoBorrowFn(r) => {
+                            borrow_fn(cx, span, r, adjusted_ty)
                         }
 
-                        AutoBorrowFn => {
-                            borrow_fn(cx, span, autoref, adjusted_ty)
+                        AutoUnsafe(m) => {
+                            mk_ptr(cx, mt {ty: adjusted_ty, mutbl: m})
                         }
                     }
                 }
@@ -2936,15 +2958,15 @@ pub fn adjust_ty(cx: ctxt,
     };
 
     fn borrow_vec(cx: ctxt, span: span,
-                  autoref: &AutoRef, ty: ty::t) -> ty::t {
+                  r: Region, m: ast::mutability,
+                  ty: ty::t) -> ty::t {
         match get(ty).sty {
             ty_evec(mt, _) => {
-                ty::mk_evec(cx, mt {ty: mt.ty, mutbl: autoref.mutbl},
-                            vstore_slice(autoref.region))
+                ty::mk_evec(cx, mt {ty: mt.ty, mutbl: m}, vstore_slice(r))
             }
 
             ty_estr(_) => {
-                ty::mk_estr(cx, vstore_slice(autoref.region))
+                ty::mk_estr(cx, vstore_slice(r))
             }
 
             ref s => {
@@ -2956,13 +2978,12 @@ pub fn adjust_ty(cx: ctxt,
         }
     }
 
-    fn borrow_fn(cx: ctxt, span: span,
-                 autoref: &AutoRef, ty: ty::t) -> ty::t {
+    fn borrow_fn(cx: ctxt, span: span, r: Region, ty: ty::t) -> ty::t {
         match get(ty).sty {
             ty_closure(ref fty) => {
                 ty::mk_closure(cx, ClosureTy {
                     sigil: BorrowedSigil,
-                    region: autoref.region,
+                    region: r,
                     ..copy *fty
                 })
             }
@@ -2977,6 +2998,18 @@ pub fn adjust_ty(cx: ctxt,
     }
 }
 
+pub impl AutoRef {
+    fn map_region(&self, f: &fn(Region) -> Region) -> AutoRef {
+        match *self {
+            ty::AutoPtr(r, m) => ty::AutoPtr(f(r), m),
+            ty::AutoBorrowVec(r, m) => ty::AutoBorrowVec(f(r), m),
+            ty::AutoBorrowVecRef(r, m) => ty::AutoBorrowVecRef(f(r), m),
+            ty::AutoBorrowFn(r) => ty::AutoBorrowFn(f(r)),
+            ty::AutoUnsafe(m) => ty::AutoUnsafe(m),
+        }
+    }
+}
+
 pub struct ParamsTy {
     params: ~[t],
     ty: t
@@ -3749,7 +3782,7 @@ pub fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[VariantInfo] {
           call eval_const_expr, it should never get called twice for the same
           expr, since check_enum_variants also updates the enum_var_cache
          */
-        match *cx.items.get(&id.node) {
+        match cx.items.get_copy(&id.node) {
           ast_map::node_item(@ast::item {
                     node: ast::item_enum(ref enum_definition, _),
                     _
@@ -3875,7 +3908,7 @@ pub fn lookup_field_type(tcx: ctxt,
     }
     else {
         match tcx.tcache.find(&id) {
-           Some(tpt) => tpt.ty,
+           Some(&ty_param_bounds_and_ty {ty, _}) => ty,
            None => {
                let tpt = csearch::get_field_type(tcx, struct_id, id);
                tcx.tcache.insert(id, tpt);
@@ -4280,7 +4313,7 @@ pub fn get_impl_id(tcx: ctxt, trait_id: def_id, self_ty: t) -> def_id {
 pub fn visitor_object_ty(tcx: ctxt) -> (@TraitRef, t) {
     let ty_visitor_name = special_idents::ty_visitor;
     assert!(tcx.intrinsic_traits.contains_key(&ty_visitor_name));
-    let trait_ref = *tcx.intrinsic_traits.get(&ty_visitor_name);
+    let trait_ref = tcx.intrinsic_traits.get_copy(&ty_visitor_name);
     (trait_ref,
      mk_trait(tcx, trait_ref.def_id, copy trait_ref.substs, BoxTraitStore, ast::m_imm))
 }
diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs
index 3a0ad0141b00b..40c5df7b76832 100644
--- a/src/librustc/middle/typeck/check/_match.rs
+++ b/src/librustc/middle/typeck/check/_match.rs
@@ -118,8 +118,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path,
                 Some((enm, var)) => {
                     // Assign the pattern the type of the *enum*, not the variant.
                     let enum_tpt = ty::lookup_item_type(tcx, enm);
-                    instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id,
-                                     pcx.block_region);
+                    instantiate_path(pcx.fcx, path, enum_tpt, pat.span, pat.id);
 
                     // check that the type of the value being matched is a subtype
                     // of the type of the pattern:
@@ -175,8 +174,7 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: @ast::Path,
             } else {
                 ctor_tpt
             };
-            instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id,
-                             pcx.block_region);
+            instantiate_path(pcx.fcx, path, struct_tpt, pat.span, pat.id);
 
             // Check that the type of the value being matched is a subtype of
             // the type of the pattern.
@@ -425,7 +423,7 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
       }
       ast::pat_enum(*) |
       ast::pat_ident(*) if pat_is_const(tcx.def_map, pat) => {
-        let const_did = ast_util::def_id_of_def(*tcx.def_map.get(&pat.id));
+        let const_did = ast_util::def_id_of_def(tcx.def_map.get_copy(&pat.id));
         let const_tpt = ty::lookup_item_type(tcx, const_did);
         demand::suptype(fcx, pat.span, expected, const_tpt.ty);
         fcx.write_ty(pat.id, const_tpt.ty);
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index abaadceb053fc..08398f9880a40 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -119,7 +119,8 @@ pub fn lookup(
         // In a call `a.b::<X, Y, ...>(...)`:
         expr: @ast::expr,                   // The expression `a.b(...)`.
         self_expr: @ast::expr,              // The expression `a`.
-        callee_id: node_id,                 // Where to store `a.b`'s type
+        callee_id: node_id,                 /* Where to store `a.b`'s type,
+                                             * also the scope of the call */
         m_name: ast::ident,                 // The ident `b`.
         self_ty: ty::t,                     // The type of `a`.
         supplied_tps: &[ty::t],             // The list of types X, Y, ... .
@@ -127,7 +128,7 @@ pub fn lookup(
         check_traits: CheckTraitsFlag,      // Whether we check traits only.
         autoderef_receiver: AutoderefReceiverFlag)
      -> Option<method_map_entry> {
-    let mut impl_dups = HashSet::new();
+    let impl_dups = @mut HashSet::new();
     let lcx = LookupContext {
         fcx: fcx,
         expr: expr,
@@ -135,7 +136,7 @@ pub fn lookup(
         callee_id: callee_id,
         m_name: m_name,
         supplied_tps: supplied_tps,
-        impl_dups: &mut impl_dups,
+        impl_dups: impl_dups,
         inherent_candidates: @mut ~[],
         extension_candidates: @mut ~[],
         deref_args: deref_args,
@@ -154,7 +155,7 @@ pub struct LookupContext<'self> {
     callee_id: node_id,
     m_name: ast::ident,
     supplied_tps: &'self [ty::t],
-    impl_dups: &'self mut HashSet<def_id>,
+    impl_dups: @mut HashSet<def_id>,
     inherent_candidates: @mut ~[Candidate],
     extension_candidates: @mut ~[Candidate],
     deref_args: check::DerefArgs,
@@ -640,7 +641,7 @@ pub impl<'self> LookupContext<'self> {
         /*!
          *
          * In the event that we are invoking a method with a receiver
-         * of a linear borrowed type like `&mut T` or `&mut [T]`,
+         * of a borrowed type like `&T`, `&mut T`, or `&mut [T]`,
          * we will "reborrow" the receiver implicitly.  For example, if
          * you have a call `r.inc()` and where `r` has type `&mut T`,
          * then we treat that like `(&mut *r).inc()`.  This avoids
@@ -657,26 +658,25 @@ pub impl<'self> LookupContext<'self> {
 
         let tcx = self.tcx();
         return match ty::get(self_ty).sty {
-            ty::ty_rptr(_, self_mt) if self_mt.mutbl == m_mutbl => {
-                let region = self.infcx().next_region_var(self.expr.span,
-                                                          self.expr.id);
+            ty::ty_rptr(_, self_mt) if default_method_hack(self_mt) => {
+                (self_ty,
+                 ty::AutoDerefRef(ty::AutoDerefRef {
+                     autoderefs: autoderefs,
+                     autoref: None}))
+            }
+            ty::ty_rptr(_, self_mt) => {
+                let region = self.infcx().next_region_var_nb(self.expr.span);
                 (ty::mk_rptr(tcx, region, self_mt),
                  ty::AutoDerefRef(ty::AutoDerefRef {
                      autoderefs: autoderefs+1,
-                     autoref: Some(ty::AutoRef {kind: AutoPtr,
-                                                region: region,
-                                                mutbl: self_mt.mutbl})}))
+                     autoref: Some(ty::AutoPtr(region, self_mt.mutbl))}))
             }
-            ty::ty_evec(self_mt, vstore_slice(_))
-            if self_mt.mutbl == m_mutbl => {
-                let region = self.infcx().next_region_var(self.expr.span,
-                                                          self.expr.id);
+            ty::ty_evec(self_mt, vstore_slice(_)) => {
+                let region = self.infcx().next_region_var_nb(self.expr.span);
                 (ty::mk_evec(tcx, self_mt, vstore_slice(region)),
                  ty::AutoDerefRef(ty::AutoDerefRef {
-                    autoderefs: autoderefs,
-                    autoref: Some(ty::AutoRef {kind: AutoBorrowVec,
-                                               region: region,
-                                               mutbl: self_mt.mutbl})}))
+                     autoderefs: autoderefs,
+                     autoref: Some(ty::AutoBorrowVec(region, self_mt.mutbl))}))
             }
             _ => {
                 (self_ty,
@@ -685,6 +685,16 @@ pub impl<'self> LookupContext<'self> {
                      autoref: None}))
             }
         };
+
+        fn default_method_hack(self_mt: ty::mt) -> bool {
+            // FIXME(#6129). Default methods can't deal with autoref.
+            //
+            // I am a horrible monster and I pray for death. Currently
+            // the default method code fails when you try to reborrow
+            // because it is not handling types correctly. In lieu of
+            // fixing that, I am introducing this horrible hack. - ndm
+            self_mt.mutbl == m_imm && ty::type_is_self(self_mt.ty)
+        }
     }
 
     fn search_for_autosliced_method(
@@ -793,7 +803,7 @@ pub impl<'self> LookupContext<'self> {
 
     fn search_for_some_kind_of_autorefd_method(
         &self,
-        kind: AutoRefKind,
+        kind: &fn(Region, ast::mutability) -> ty::AutoRef,
         autoderefs: uint,
         mutbls: &[ast::mutability],
         mk_autoref_ty: &fn(ast::mutability, ty::Region) -> ty::t)
@@ -801,8 +811,7 @@ pub impl<'self> LookupContext<'self> {
     {
         // This is hokey. We should have mutability inference as a
         // variable.  But for now, try &const, then &, then &mut:
-        let region = self.infcx().next_region_var(self.expr.span,
-                                                  self.expr.id);
+        let region = self.infcx().next_region_var_nb(self.expr.span);
         for mutbls.each |mutbl| {
             let autoref_ty = mk_autoref_ty(*mutbl, region);
             match self.search_for_method(autoref_ty) {
@@ -812,12 +821,7 @@ pub impl<'self> LookupContext<'self> {
                         self.self_expr.id,
                         @ty::AutoDerefRef(ty::AutoDerefRef {
                             autoderefs: autoderefs,
-                            autoref: Some(ty::AutoRef {
-                                kind: kind,
-                                region: region,
-                                mutbl: *mutbl,
-                            }),
-                        }));
+                            autoref: Some(kind(region, *mutbl))}));
                     return Some(mme);
                 }
             }
@@ -1024,8 +1028,7 @@ pub impl<'self> LookupContext<'self> {
         let (_, opt_transformed_self_ty, fn_sig) =
             replace_bound_regions_in_fn_sig(
                 tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig,
-                |_br| self.fcx.infcx().next_region_var(
-                    self.expr.span, self.expr.id));
+                |_br| self.fcx.infcx().next_region_var_nb(self.expr.span));
         let transformed_self_ty = opt_transformed_self_ty.get();
         let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {sig: fn_sig, ..bare_fn_ty});
         debug!("after replacing bound regions, fty=%s", self.ty_to_str(fty));
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 69775771d9697..e171765ef6c4e 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -207,9 +207,11 @@ pub impl PurityState {
 }
 
 pub struct FnCtxt {
-    // var_bindings, locals and next_var_id are shared
-    // with any nested functions that capture the environment
-    // (and with any functions whose environment is being captured).
+    // Number of errors that had been reported when we started
+    // checking this function. On exit, if we find that *more* errors
+    // have been reported, we will skip regionck and other work that
+    // expects the types within the function to be consistent.
+    err_count_on_creation: uint,
 
     ret_ty: ty::t,
     // Used by loop bodies that return from the outer function
@@ -263,6 +265,7 @@ pub fn blank_fn_ctxt(ccx: @mut CrateCtxt,
 // It's kind of a kludge to manufacture a fake function context
 // and statement context, but we might as well do write the code only once
     @mut FnCtxt {
+        err_count_on_creation: ccx.tcx.sess.err_count(),
         ret_ty: rty,
         indirect_ret_ty: None,
         ps: PurityState::function(ast::pure_fn, 0),
@@ -328,6 +331,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
      */
 
     let tcx = ccx.tcx;
+    let err_count_on_creation = tcx.sess.err_count();
 
     // ______________________________________________________________________
     // First, we have to replace any bound regions in the fn and self
@@ -368,6 +372,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
         };
 
         @mut FnCtxt {
+            err_count_on_creation: err_count_on_creation,
             ret_ty: ret_ty,
             indirect_ret_ty: indirect_ret_ty,
             ps: PurityState::function(purity, id),
@@ -433,7 +438,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
             assign(self_info.self_id, Some(self_info.self_ty));
             debug!("self is assigned to %s",
                    fcx.infcx().ty_to_str(
-                       *fcx.inh.locals.get(&self_info.self_id)));
+                       fcx.inh.locals.get_copy(&self_info.self_id)));
         }
 
         // Add formal parameters.
@@ -466,7 +471,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
             debug!("Local variable %s is assigned type %s",
                    fcx.pat_to_str(local.node.pat),
                    fcx.infcx().ty_to_str(
-                       *fcx.inh.locals.get(&local.node.id)));
+                       fcx.inh.locals.get_copy(&local.node.id)));
             visit::visit_local(local, e, v);
         };
 
@@ -479,7 +484,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
                 debug!("Pattern binding %s is assigned to %s",
                        *tcx.sess.str_of(path.idents[0]),
                        fcx.infcx().ty_to_str(
-                           *fcx.inh.locals.get(&p.id)));
+                           fcx.inh.locals.get_copy(&p.id)));
               }
               _ => {}
             }
@@ -642,7 +647,12 @@ impl AstConv for FnCtxt {
 }
 
 pub impl FnCtxt {
-    fn infcx(&self) -> @mut infer::InferCtxt { self.inh.infcx }
+    fn infcx(&self) -> @mut infer::InferCtxt {
+        self.inh.infcx
+    }
+    fn err_count_since_creation(&self) -> uint {
+        self.ccx.tcx.sess.err_count() - self.err_count_on_creation
+    }
     fn search_in_scope_regions(
         &self,
         span: span,
@@ -898,11 +908,9 @@ pub impl FnCtxt {
 
     fn region_var_if_parameterized(&self,
                                    rp: Option<ty::region_variance>,
-                                   span: span,
-                                   lower_bound: ty::Region)
+                                   span: span)
                                 -> Option<ty::Region> {
-        rp.map(
-            |_rp| self.infcx().next_region_var_with_lb(span, lower_bound))
+        rp.map(|_rp| self.infcx().next_region_var_nb(span))
     }
 
     fn type_error_message(&self,
@@ -1083,8 +1091,7 @@ pub fn impl_self_ty(vcx: &VtableContext,
     };
 
     let self_r = if region_param.is_some() {
-        Some(vcx.infcx.next_region_var(location_info.span,
-                                         location_info.id))
+        Some(vcx.infcx.next_region_var_nb(location_info.span))
     } else {
         None
     };
@@ -1291,9 +1298,17 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         // that they appear in call position.
         check_expr(fcx, f);
 
+        // Store the type of `f` as the type of the callee
+        let fn_ty = fcx.expr_ty(f);
+
+        // FIXME(#6273) should write callee type AFTER regions have
+        // been subst'd.  However, it is awkward to deal with this
+        // now. Best thing would I think be to just have a separate
+        // "callee table" that contains the FnSig and not a general
+        // purpose ty::t
+        fcx.write_ty(call_expr.callee_id, fn_ty);
 
         // Extract the function signature from `in_fty`.
-        let fn_ty = fcx.expr_ty(f);
         let fn_sty = structure_of(fcx, f.span, fn_ty);
 
         // FIXME(#3678) For now, do not permit calls to C abi functions.
@@ -1330,7 +1345,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         let (_, _, fn_sig) =
             replace_bound_regions_in_fn_sig(
                 fcx.tcx(), @Nil, None, &fn_sig,
-                |_br| fcx.infcx().next_region_var(call_expr.span, call_expr.id));
+                |_br| fcx.infcx().next_region_var_nb(call_expr.span));
 
         // Call the generic checker.
         check_argument_types(fcx, call_expr.span, fn_sig.inputs, f,
@@ -1651,7 +1666,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         };
 
         // construct the function type
-        let mut fn_ty = astconv::ty_of_closure(fcx,
+        let fn_ty = astconv::ty_of_closure(fcx,
                                                fcx,
                                                sigil,
                                                purity,
@@ -1662,7 +1677,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
                                                &opt_vec::Empty,
                                                expr.span);
 
-        let mut fty_sig;
+        let fty_sig;
         let fty = if error_happened {
             fty_sig = FnSig {
                 bound_lifetime_names: opt_vec::Empty,
@@ -1909,9 +1924,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         // Generate the struct type.
         let self_region =
-            fcx.region_var_if_parameterized(region_parameterized,
-                                            span,
-                                            ty::re_scope(id));
+            fcx.region_var_if_parameterized(region_parameterized, span);
         let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
         let substitutions = substs {
             self_r: self_region,
@@ -1997,9 +2010,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         // Generate the enum type.
         let self_region =
-            fcx.region_var_if_parameterized(region_parameterized,
-                                            span,
-                                            ty::re_scope(id));
+            fcx.region_var_if_parameterized(region_parameterized, span);
         let type_parameters = fcx.infcx().next_ty_vars(type_parameter_count);
         let substitutions = substs {
             self_r: self_region,
@@ -2336,13 +2347,12 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         // (and how long it is valid), which we don't know yet until type
         // inference is complete.
         //
-        // Therefore, here we simply generate a region variable with
-        // the current expression as a lower bound.  The region
-        // inferencer will then select the ultimate value.  Finally,
-        // borrowck is charged with guaranteeing that the value whose
-        // address was taken can actually be made to live as long as
-        // it needs to live.
-        let region = fcx.infcx().next_region_var(expr.span, expr.id);
+        // Therefore, here we simply generate a region variable.  The
+        // region inferencer will then select the ultimate value.
+        // Finally, borrowck is charged with guaranteeing that the
+        // value whose address was taken can actually be made to live
+        // as long as it needs to live.
+        let region = fcx.infcx().next_region_var_nb(expr.span);
 
         let tm = ty::mt { ty: fcx.expr_ty(oprnd), mutbl: mutbl };
         let oprnd_t = if ty::type_is_error(tm.ty) {
@@ -2359,8 +2369,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         let defn = lookup_def(fcx, pth.span, id);
 
         let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn);
-        let region_lb = ty::re_scope(expr.id);
-        instantiate_path(fcx, pth, tpt, expr.span, expr.id, region_lb);
+        instantiate_path(fcx, pth, tpt, expr.span, expr.id);
       }
       ast::expr_inline_asm(ref ia) => {
           fcx.require_unsafe(expr.span, ~"use of inline assembly");
@@ -2936,7 +2945,8 @@ pub fn check_block(fcx0: @mut FnCtxt, blk: &ast::blk)  {
 pub fn check_block_with_expected(fcx: @mut FnCtxt,
                                  blk: &ast::blk,
                                  expected: Option<ty::t>) {
-    let prev = replace(&mut fcx.ps, fcx.ps.recurse(blk));
+    let purity_state = fcx.ps.recurse(blk);
+    let prev = replace(&mut fcx.ps, purity_state);
 
     do fcx.with_region_lb(blk.node.id) {
         let mut warned = false;
@@ -3227,8 +3237,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
                         pth: @ast::Path,
                         tpt: ty_param_bounds_and_ty,
                         span: span,
-                        node_id: ast::node_id,
-                        region_lb: ty::Region) {
+                        node_id: ast::node_id) {
     debug!(">>> instantiate_path");
 
     let ty_param_count = tpt.generics.type_param_defs.len();
@@ -3254,8 +3263,7 @@ pub fn instantiate_path(fcx: @mut FnCtxt,
         }
       }
       None => { // no lifetime parameter supplied, insert default
-        fcx.region_var_if_parameterized(
-            tpt.generics.region_param, span, region_lb)
+        fcx.region_var_if_parameterized(tpt.generics.region_param, span)
       }
     };
 
@@ -3339,7 +3347,7 @@ pub fn ast_expr_vstore_to_vstore(fcx: @mut FnCtxt,
         ast::expr_vstore_uniq => ty::vstore_uniq,
         ast::expr_vstore_box | ast::expr_vstore_mut_box => ty::vstore_box,
         ast::expr_vstore_slice | ast::expr_vstore_mut_slice => {
-            let r = fcx.infcx().next_region_var(e.span, e.id);
+            let r = fcx.infcx().next_region_var_nb(e.span);
             ty::vstore_slice(r)
         }
     }
@@ -3462,7 +3470,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) {
       ~"visit_tydesc" => {
         let tydesc_name = special_idents::tydesc;
         assert!(tcx.intrinsic_defs.contains_key(&tydesc_name));
-        let (_, tydesc_ty) = *tcx.intrinsic_defs.get(&tydesc_name);
+        let (_, tydesc_ty) = tcx.intrinsic_defs.get_copy(&tydesc_name);
         let (_, visitor_object_ty) = ty::visitor_object_ty(tcx);
         let td_ptr = ty::mk_ptr(ccx.tcx, ty::mt {
             ty: tydesc_ty,
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index 3a38d0b1d7ba3..6db6b7556aee4 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -28,16 +28,15 @@ this point a bit better.
 */
 
 use middle::freevars::get_freevars;
-use middle::pat_util::pat_bindings;
 use middle::ty::{re_scope};
 use middle::ty;
 use middle::typeck::check::FnCtxt;
-use middle::typeck::check::lookup_def;
 use middle::typeck::check::regionmanip::relate_nested_regions;
 use middle::typeck::infer::resolve_and_force_all_but_regions;
 use middle::typeck::infer::resolve_type;
 use util::ppaux::{note_and_explain_region, ty_to_str,
                   region_to_str};
+use middle::pat_util;
 
 use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil};
 use syntax::ast::{def_arg, def_binding, def_local, def_self, def_upvar};
@@ -73,7 +72,11 @@ fn encl_region_of_def(fcx: @mut FnCtxt, def: ast::def) -> ty::Region {
 }
 
 pub impl Rcx {
-    fn resolve_type(@mut self, unresolved_ty: ty::t) -> ty::t {
+    fn tcx(&self) -> ty::ctxt {
+        self.fcx.ccx.tcx
+    }
+
+    fn resolve_type(&mut self, unresolved_ty: ty::t) -> ty::t {
         /*!
          * Try to resolve the type for the given node, returning
          * t_err if an error results.  Note that we never care
@@ -135,24 +138,40 @@ pub impl Rcx {
 
 pub fn regionck_expr(fcx: @mut FnCtxt, e: @ast::expr) {
     let rcx = @mut Rcx { fcx: fcx, errors_reported: 0 };
-    let v = regionck_visitor();
-    (v.visit_expr)(e, rcx, v);
+    if fcx.err_count_since_creation() == 0 {
+        // regionck assumes typeck succeeded
+        let v = regionck_visitor();
+        (v.visit_expr)(e, rcx, v);
+    }
     fcx.infcx().resolve_regions();
 }
 
 pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) {
     let rcx = @mut Rcx { fcx: fcx, errors_reported: 0 };
-    let v = regionck_visitor();
-    (v.visit_block)(blk, rcx, v);
+    if fcx.err_count_since_creation() == 0 {
+        // regionck assumes typeck succeeded
+        let v = regionck_visitor();
+        (v.visit_block)(blk, rcx, v);
+    }
     fcx.infcx().resolve_regions();
 }
 
 fn regionck_visitor() -> rvt {
+    // (*) FIXME(#3238) should use visit_pat, not visit_arm/visit_local,
+    // However, right now we run into an issue whereby some free
+    // regions are not properly related if they appear within the
+    // types of arguments that must be inferred. This could be
+    // addressed by deferring the construction of the region
+    // hierarchy, and in particular the relationships between free
+    // regions, until regionck, as described in #3238.
     visit::mk_vt(@visit::Visitor {visit_item: visit_item,
-                                  visit_stmt: visit_stmt,
                                   visit_expr: visit_expr,
-                                  visit_block: visit_block,
+
+                                  //visit_pat: visit_pat, // (*) see above
+                                  visit_arm: visit_arm,
                                   visit_local: visit_local,
+
+                                  visit_block: visit_block,
                                   .. *visit::default_visitor()})
 }
 
@@ -160,44 +179,110 @@ fn visit_item(_item: @ast::item, _rcx: @mut Rcx, _v: rvt) {
     // Ignore items
 }
 
-fn visit_local(l: @ast::local, rcx: @mut Rcx, v: rvt) {
-    // Check to make sure that the regions in all local variables are
-    // within scope.
-    //
-    // Note: we do this here rather than in visit_pat because we do
-    // not wish to constrain the regions in *patterns* in quite the
-    // same way.  `visit_node()` guarantees that the region encloses
-    // the node in question, which ultimately constrains the regions
-    // in patterns to enclose the match expression as a whole.  But we
-    // want them to enclose the *arm*.  However, regions in patterns
-    // must either derive from the discriminant or a ref pattern: in
-    // the case of the discriminant, the regions will be constrained
-    // when the type of the discriminant is checked.  In the case of a
-    // ref pattern, the variable is created with a suitable lower
-    // bound.
-    let e = rcx.errors_reported;
-    (v.visit_pat)(l.node.pat, rcx, v);
-    let def_map = rcx.fcx.ccx.tcx.def_map;
-    do pat_bindings(def_map, l.node.pat) |_bm, id, sp, _path| {
-        visit_node(id, sp, rcx);
-    }
-    if e != rcx.errors_reported {
-        return; // if decl has errors, skip initializer expr
-    }
+fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) {
+    rcx.fcx.tcx().region_maps.record_cleanup_scope(b.node.id);
+    visit::visit_block(b, rcx, v);
+}
 
-    (v.visit_ty)(l.node.ty, rcx, v);
-    for l.node.init.each |i| {
-        (v.visit_expr)(*i, rcx, v);
+fn visit_arm(arm: &ast::arm, rcx: @mut Rcx, v: rvt) {
+    // see above
+    for arm.pats.each |&p| {
+        constrain_bindings_in_pat(p, rcx);
     }
+
+    visit::visit_arm(arm, rcx, v);
 }
 
-fn visit_block(b: &ast::blk, rcx: @mut Rcx, v: rvt) {
-    visit::visit_block(b, rcx, v);
+fn visit_local(l: @ast::local, rcx: @mut Rcx, v: rvt) {
+    // see above
+    constrain_bindings_in_pat(l.node.pat, rcx);
+    visit::visit_local(l, rcx, v);
+}
+
+fn constrain_bindings_in_pat(pat: @ast::pat, rcx: @mut Rcx) {
+    let tcx = rcx.fcx.tcx();
+    debug!("regionck::visit_pat(pat=%s)", pat.repr(tcx));
+    do pat_util::pat_bindings(tcx.def_map, pat) |_, id, span, _| {
+        // If we have a variable that contains region'd data, that
+        // data will be accessible from anywhere that the variable is
+        // accessed. We must be wary of loops like this:
+        //
+        //     // from src/test/compile-fail/borrowck-lend-flow.rs
+        //     let mut v = ~3, w = ~4;
+        //     let mut x = &mut w;
+        //     loop {
+        //         **x += 1;   // (2)
+        //         borrow(v);  //~ ERROR cannot borrow
+        //         x = &mut v; // (1)
+        //     }
+        //
+        // Typically, we try to determine the region of a borrow from
+        // those points where it is dereferenced. In this case, one
+        // might imagine that the lifetime of `x` need only be the
+        // body of the loop. But of course this is incorrect because
+        // the pointer that is created at point (1) is consumed at
+        // point (2), meaning that it must be live across the loop
+        // iteration. The easiest way to guarantee this is to require
+        // that the lifetime of any regions that appear in a
+        // variable's type enclose at least the variable's scope.
+
+        let encl_region = tcx.region_maps.encl_region(id);
+        constrain_regions_in_type_of_node(rcx, id, encl_region, span);
+    }
 }
 
 fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
     debug!("regionck::visit_expr(e=%s)", rcx.fcx.expr_to_str(expr));
 
+    let has_method_map = rcx.fcx.inh.method_map.contains_key(&expr.id);
+
+    // Record cleanup scopes, which are used by borrowck to decide the
+    // maximum lifetime of a temporary rvalue.  These were derived by
+    // examining where trans creates block scopes, not because this
+    // reflects some principled decision around temporary lifetimes.
+    // Ordinarily this would seem like something that should be setup
+    // in region, but we need to know which uses of operators are
+    // overloaded.  See #3511.
+    let tcx = rcx.fcx.tcx();
+    match expr.node {
+        // You'd think that x += y where `+=` is overloaded would be a
+        // cleanup scope. You'd be... kind of right. In fact the
+        // handling of `+=` and friends in trans for overloaded
+        // operators is a hopeless mess and I can't figure out how to
+        // represent it. - ndm
+        //
+        // ast::expr_assign_op(*) |
+
+        ast::expr_index(*) |
+        ast::expr_binary(*) |
+        ast::expr_unary(*) if has_method_map => {
+            tcx.region_maps.record_cleanup_scope(expr.id);
+        }
+        ast::expr_binary(ast::and, lhs, rhs) |
+        ast::expr_binary(ast::or, lhs, rhs) => {
+            tcx.region_maps.record_cleanup_scope(lhs.id);
+            tcx.region_maps.record_cleanup_scope(rhs.id);
+        }
+        ast::expr_call(*) |
+        ast::expr_method_call(*) => {
+            tcx.region_maps.record_cleanup_scope(expr.id);
+        }
+        ast::expr_match(_, ref arms) => {
+            tcx.region_maps.record_cleanup_scope(expr.id);
+            for arms.each |arm| {
+                for arm.guard.each |guard| {
+                    tcx.region_maps.record_cleanup_scope(guard.id);
+                }
+            }
+        }
+        ast::expr_while(cond, ref body) => {
+            tcx.region_maps.record_cleanup_scope(cond.id);
+            tcx.region_maps.record_cleanup_scope(body.node.id);
+        }
+        _ => {}
+    }
+
+    // Check any autoderefs or autorefs that appear.
     for rcx.fcx.inh.adjustments.find(&expr.id).each |&adjustment| {
         debug!("adjustment=%?", adjustment);
         match *adjustment {
@@ -208,6 +293,13 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
                 constrain_derefs(rcx, expr, autoderefs, expr_ty);
                 for opt_autoref.each |autoref| {
                     guarantor::for_autoref(rcx, expr, autoderefs, autoref);
+
+                    // Require that the resulting region encompasses
+                    // the current node.
+                    //
+                    // FIXME(#6268) remove to support nested method calls
+                    constrain_regions_in_type_of_node(
+                        rcx, expr.id, ty::re_scope(expr.id), expr.span);
                 }
             }
             _ => {}
@@ -215,58 +307,40 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
     }
 
     match expr.node {
-        ast::expr_path(*) => {
-            // Avoid checking the use of local variables, as we
-            // already check their definitions.  The def'n always
-            // encloses the use.  So if the def'n is enclosed by the
-            // region, then the uses will also be enclosed (and
-            // otherwise, an error will have been reported at the
-            // def'n site).
-            match lookup_def(rcx.fcx, expr.span, expr.id) {
-                ast::def_local(*) | ast::def_arg(*) |
-                ast::def_upvar(*) => return,
-                _ => ()
-            }
+        ast::expr_call(callee, ref args, _) => {
+            constrain_callee(rcx, expr, callee);
+            constrain_call(rcx, expr, None, *args, false);
         }
 
-        ast::expr_call(callee, ref args, _) => {
-            // Check for a.b() where b is a method.  Ensure that
-            // any types in the callee are valid for the entire
-            // method call.
-
-            // FIXME(#3387)--we should really invoke
-            // `constrain_auto_ref()` on all exprs.  But that causes a
-            // lot of spurious errors because of how the region
-            // hierarchy is setup.
-            if rcx.fcx.inh.method_map.contains_key(&callee.id) {
-                match callee.node {
-                    ast::expr_field(base, _, _) => {
-                        constrain_auto_ref(rcx, base);
-                    }
-                    _ => {
-                        // This can happen if you have code like
-                        // (x[0])() where `x[0]` is overloaded.  Just
-                        // ignore it.
-                    }
-                }
-            } else {
-                constrain_auto_ref(rcx, callee);
-            }
+        ast::expr_method_call(arg0, _, _, ref args, _) => {
+            constrain_call(rcx, expr, Some(arg0), *args, false);
+        }
 
-            for args.each |arg| {
-                constrain_auto_ref(rcx, *arg);
-            }
+        ast::expr_index(lhs, rhs) |
+        ast::expr_assign_op(_, lhs, rhs) |
+        ast::expr_binary(_, lhs, rhs) if has_method_map => {
+            // As `expr_method_call`, but the call is via an
+            // overloaded op.  Note that we (sadly) currently use an
+            // implicit "by ref" sort of passing style here.  This
+            // should be converted to an adjustment!
+            constrain_call(rcx, expr, Some(lhs), [rhs], true);
         }
 
-        ast::expr_method_call(rcvr, _, _, ref args, _) => {
-            // Check for a.b() where b is a method.  Ensure that
-            // any types in the callee are valid for the entire
-            // method call.
+        ast::expr_unary(_, lhs) if has_method_map => {
+            // As above.
+            constrain_call(rcx, expr, Some(lhs), [], true);
+        }
 
-            constrain_auto_ref(rcx, rcvr);
-            for args.each |arg| {
-                constrain_auto_ref(rcx, *arg);
-            }
+        ast::expr_unary(ast::deref, base) => {
+            // For *a, the lifetime of a must enclose the deref
+            let base_ty = rcx.resolve_node_type(base.id);
+            constrain_derefs(rcx, expr, 1, base_ty);
+        }
+
+        ast::expr_index(vec_expr, _) => {
+            // For a[b], the lifetime of a must enclose the deref
+            let vec_type = rcx.resolve_expr_type_adjusted(vec_expr);
+            constrain_index(rcx, expr, vec_type);
         }
 
         ast::expr_cast(source, _) => {
@@ -294,18 +368,18 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
             }
         }
 
-        ast::expr_index(vec_expr, _) => {
-            let vec_type = rcx.resolve_expr_type_adjusted(vec_expr);
-            constrain_index(rcx, expr, vec_type);
-        }
-
-        ast::expr_unary(ast::deref, base) => {
-            let base_ty = rcx.resolve_node_type(base.id);
-            constrain_derefs(rcx, expr, 1, base_ty);
-        }
-
         ast::expr_addr_of(_, base) => {
             guarantor::for_addr_of(rcx, expr, base);
+
+            // Require that when you write a `&expr` expression, the
+            // resulting pointer has a lifetime that encompasses the
+            // `&expr` expression itself. Note that we constraining
+            // the type of the node expr.id here *before applying
+            // adjustments*.
+            //
+            // FIXME(#6268) nested method calls requires that this rule change
+            let ty0 = rcx.resolve_node_type(expr.id);
+            constrain_regions_in_type(rcx, ty::re_scope(expr.id), expr.span, ty0);
         }
 
         ast::expr_match(discr, ref arms) => {
@@ -313,6 +387,8 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
         }
 
         ast::expr_fn_block(*) => {
+            // The lifetime of a block fn must not outlive the variables
+            // it closes over
             let function_type = rcx.resolve_node_type(expr.id);
             match ty::get(function_type).sty {
                 ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil,
@@ -326,46 +402,101 @@ fn visit_expr(expr: @ast::expr, rcx: @mut Rcx, v: rvt) {
         _ => ()
     }
 
-    if !visit_node(expr.id, expr.span, rcx) { return; }
     visit::visit_expr(expr, rcx, v);
 }
 
-fn visit_stmt(s: @ast::stmt, rcx: @mut Rcx, v: rvt) {
-    visit::visit_stmt(s, rcx, v);
-}
+fn constrain_callee(rcx: @mut Rcx,
+                    call_expr: @ast::expr,
+                    callee_expr: @ast::expr)
+{
+    let tcx = rcx.fcx.tcx();
 
-fn visit_node(id: ast::node_id, span: span, rcx: @mut Rcx) -> bool {
-    /*!
-     *
-     * checks the type of the node `id` and reports an error if it
-     * references a region that is not in scope for that node.
-     * Returns false if an error is reported; this is used to cause us
-     * to cut off region checking for that subtree to avoid reporting
-     * tons of errors. */
-
-    let fcx = rcx.fcx;
-
-    // find the region where this expr evaluation is taking place
-    let tcx = fcx.ccx.tcx;
-    let encl_region = match tcx.region_maps.opt_encl_scope(id) {
-        None => ty::re_static,
-        Some(r) => ty::re_scope(r)
-    };
-
-    // Otherwise, look at the type and see if it is a region pointer.
-    constrain_regions_in_type_of_node(rcx, id, encl_region, span)
+    let call_region = ty::re_scope(call_expr.id);
+
+    let callee_ty = rcx.resolve_node_type(call_expr.callee_id);
+    match ty::get(callee_ty).sty {
+        ty::ty_bare_fn(*) => { }
+        ty::ty_closure(ref closure_ty) => {
+            match rcx.fcx.mk_subr(true, callee_expr.span,
+                                  call_region, closure_ty.region) {
+                result::Err(_) => {
+                    tcx.sess.span_err(
+                        callee_expr.span,
+                        fmt!("cannot invoke closure outside of its lifetime"));
+                    note_and_explain_region(
+                        tcx,
+                        "the closure is only valid for ",
+                        closure_ty.region,
+                        "");
+                }
+                result::Ok(_) => {}
+            }
+        }
+        _ => {
+            // this should not happen, but it does if the program is
+            // erroneous
+            //
+            // tcx.sess.span_bug(
+            //     callee_expr.span,
+            //     fmt!("Calling non-function: %s", callee_ty.repr(tcx)));
+        }
+    }
 }
 
-fn encl_region_or_static(rcx: @mut Rcx, expr: @ast::expr) -> ty::Region {
-    // FIXME(#3850) --- interactions with modes compel overly large granularity
-    // that is, we would probably prefer to just return re_scope(expr.id)
-    // here but we cannot just yet.
+fn constrain_call(rcx: @mut Rcx,
+                  // might be expr_call, expr_method_call, or an overloaded
+                  // operator
+                  call_expr: @ast::expr,
+                  receiver: Option<@ast::expr>,
+                  arg_exprs: &[@ast::expr],
+                  implicitly_ref_args: bool)
+{
+    //! Invoked on every call site (i.e., normal calls, method calls,
+    //! and overloaded operators). Constrains the regions which appear
+    //! in the type of the function. Also constrains the regions that
+    //! appear in the arguments appropriately.
 
     let tcx = rcx.fcx.tcx();
-    match tcx.region_maps.opt_encl_scope(expr.id) {
-        Some(s) => ty::re_scope(s),
-        None => ty::re_static // occurs in constants
+    debug!("constrain_call(call_expr=%s, implicitly_ref_args=%?)",
+           call_expr.repr(tcx), implicitly_ref_args);
+    let callee_ty = rcx.resolve_node_type(call_expr.callee_id);
+    let fn_sig = ty::ty_fn_sig(callee_ty);
+
+    // `callee_region` is the scope representing the time in which the
+    // call occurs.
+    //
+    // FIXME(#6268) to support nested method calls, should be callee_id
+    let callee_scope = call_expr.id;
+    let callee_region = ty::re_scope(callee_scope);
+
+    for arg_exprs.each |&arg_expr| {
+        // ensure that any regions appearing in the argument type are
+        // valid for at least the lifetime of the function:
+        constrain_regions_in_type_of_node(
+            rcx, arg_expr.id, callee_region, arg_expr.span);
+
+        // unfortunately, there are two means of taking implicit
+        // references, and we need to propagate constraints as a
+        // result. modes are going away and the "DerefArgs" code
+        // should be ported to use adjustments
+        if implicitly_ref_args {
+            guarantor::for_by_ref(rcx, arg_expr, callee_scope);
+        }
+    }
+
+    // as loop above, but for receiver
+    for receiver.each |&r| {
+        constrain_regions_in_type_of_node(
+            rcx, r.id, callee_region, r.span);
+        if implicitly_ref_args {
+            guarantor::for_by_ref(rcx, r, callee_scope);
+        }
     }
+
+    // constrain regions that may appear in the return type to be
+    // valid for the function call:
+    constrain_regions_in_type(
+        rcx, callee_region, call_expr.span, fn_sig.output);
 }
 
 fn constrain_derefs(rcx: @mut Rcx,
@@ -379,9 +510,8 @@ fn constrain_derefs(rcx: @mut Rcx,
      * pointer being derefenced, the lifetime of the pointer includes
      * the deref expr.
      */
-
     let tcx = rcx.fcx.tcx();
-    let r_deref_expr = encl_region_or_static(rcx, deref_expr);
+    let r_deref_expr = ty::re_scope(deref_expr.id);
     for uint::range(0, derefs) |i| {
         debug!("constrain_derefs(deref_expr=%s, derefd_ty=%s, derefs=%?/%?",
                rcx.fcx.expr_to_str(deref_expr),
@@ -390,19 +520,8 @@ fn constrain_derefs(rcx: @mut Rcx,
 
         match ty::get(derefd_ty).sty {
             ty::ty_rptr(r_ptr, _) => {
-                match rcx.fcx.mk_subr(true, deref_expr.span, r_deref_expr, r_ptr) {
-                    result::Ok(*) => {}
-                    result::Err(*) => {
-                        tcx.sess.span_err(
-                            deref_expr.span,
-                            fmt!("dereference of reference outside its lifetime"));
-                        note_and_explain_region(
-                            tcx,
-                            "the reference is only valid for ",
-                            r_ptr,
-                            "");
-                    }
-                }
+                mk_subregion_due_to_derefence(rcx, deref_expr.span,
+                                              r_deref_expr, r_ptr);
             }
 
             _ => {}
@@ -417,6 +536,27 @@ fn constrain_derefs(rcx: @mut Rcx,
     }
 }
 
+pub fn mk_subregion_due_to_derefence(rcx: @mut Rcx,
+                                     deref_span: span,
+                                     minimum_lifetime: ty::Region,
+                                     maximum_lifetime: ty::Region) {
+    match rcx.fcx.mk_subr(true, deref_span,
+                          minimum_lifetime, maximum_lifetime) {
+        result::Ok(*) => {}
+        result::Err(*) => {
+            rcx.tcx().sess.span_err(
+                deref_span,
+                fmt!("dereference of reference outside its lifetime"));
+            note_and_explain_region(
+                rcx.tcx(),
+                "the reference is only valid for ",
+                maximum_lifetime,
+                "");
+        }
+    }
+}
+
+
 fn constrain_index(rcx: @mut Rcx,
                    index_expr: @ast::expr,
                    indexed_ty: ty::t)
@@ -433,7 +573,7 @@ fn constrain_index(rcx: @mut Rcx,
            rcx.fcx.expr_to_str(index_expr),
            rcx.fcx.infcx().ty_to_str(indexed_ty));
 
-    let r_index_expr = encl_region_or_static(rcx, index_expr);
+    let r_index_expr = ty::re_scope(index_expr.id);
     match ty::get(indexed_ty).sty {
         ty::ty_estr(ty::vstore_slice(r_ptr)) |
         ty::ty_evec(_, ty::vstore_slice(r_ptr)) => {
@@ -456,66 +596,22 @@ fn constrain_index(rcx: @mut Rcx,
     }
 }
 
-fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) {
+fn constrain_free_variables(rcx: @mut Rcx,
+                            region: ty::Region,
+                            expr: @ast::expr) {
     /*!
-     *
-     * If `expr` is auto-ref'd (e.g., as part of a borrow), then this
-     * function ensures that the lifetime of the resulting borrowed
-     * ptr includes at least the expression `expr`. */
-
-    debug!("constrain_auto_ref(expr=%s)", rcx.fcx.expr_to_str(expr));
-
-    let adjustment = rcx.fcx.inh.adjustments.find(&expr.id);
-    let region = match adjustment {
-        Some(&@ty::AutoDerefRef(
-            ty::AutoDerefRef {
-                autoref: Some(ref auto_ref), _})) => {
-            auto_ref.region
-        }
-        _ => { return; }
-    };
-
-    let tcx = rcx.fcx.tcx();
-    let encl_region = tcx.region_maps.encl_region(expr.id);
-    match rcx.fcx.mk_subr(true, expr.span, encl_region, region) {
-        result::Ok(()) => {}
-        result::Err(_) => {
-            // In practice, this cannot happen: `region` is always a
-            // region variable, and constraints on region variables
-            // are collected and then resolved later.  However, I
-            // included the span_err() here (rather than, say,
-            // span_bug()) because it seemed more future-proof: if,
-            // for some reason, the code were to change so that in
-            // some cases `region` is not a region variable, then
-            // reporting an error would be the correct path.
-            tcx.sess.span_err(
-                expr.span,
-                "lifetime of borrowed pointer does not include \
-                 the expression being borrowed");
-            note_and_explain_region(
-                tcx,
-                "lifetime of the borrowed pointer is",
-                region,
-                "");
-            rcx.errors_reported += 1;
-        }
-    }
-}
-
-fn constrain_free_variables(
-    rcx: @mut Rcx,
-    region: ty::Region,
-    expr: @ast::expr) {
-    /*!
-     *
      * Make sure that all free variables referenced inside the closure
-     * outlive the closure itself. */
+     * outlive the closure itself.
+     */
 
     let tcx = rcx.fcx.ccx.tcx;
+    debug!("constrain_free_variables(%s, %s)",
+           region.repr(tcx), expr.repr(tcx));
     for get_freevars(tcx, expr.id).each |freevar| {
         debug!("freevar def is %?", freevar.def);
         let def = freevar.def;
         let en_region = encl_region_of_def(rcx.fcx, def);
+        debug!("en_region = %s", en_region.repr(tcx));
         match rcx.fcx.mk_subr(true, freevar.span,
                               region, en_region) {
           result::Ok(()) => {}
@@ -541,9 +637,13 @@ fn constrain_free_variables(
 fn constrain_regions_in_type_of_node(
     rcx: @mut Rcx,
     id: ast::node_id,
-    encl_region: ty::Region,
+    minimum_lifetime: ty::Region,
     span: span) -> bool
 {
+    //! Guarantees that any lifetimes which appear in the type of
+    //! the node `id` (after applying adjustments) are valid for at
+    //! least `minimum_lifetime`
+
     let tcx = rcx.fcx.tcx();
 
     // Try to resolve the type.  If we encounter an error, then typeck
@@ -553,22 +653,21 @@ fn constrain_regions_in_type_of_node(
     let adjustment = rcx.fcx.inh.adjustments.find(&id);
     let ty = ty::adjust_ty(tcx, span, ty0, adjustment);
     debug!("constrain_regions_in_type_of_node(\
-            ty=%s, ty0=%s, id=%d, encl_region=%?, adjustment=%?)",
+            ty=%s, ty0=%s, id=%d, minimum_lifetime=%?, adjustment=%?)",
            ty_to_str(tcx, ty), ty_to_str(tcx, ty0),
-           id, encl_region, adjustment);
-    constrain_regions_in_type(rcx, encl_region, span, ty)
+           id, minimum_lifetime, adjustment);
+    constrain_regions_in_type(rcx, minimum_lifetime, span, ty)
 }
 
 fn constrain_regions_in_type(
     rcx: @mut Rcx,
-    encl_region: ty::Region,
+    minimum_lifetime: ty::Region,
     span: span,
     ty: ty::t) -> bool
 {
     /*!
-     *
      * Requires that any regions which appear in `ty` must be
-     * superregions of `encl_region`.  Also enforces the constraint
+     * superregions of `minimum_lifetime`.  Also enforces the constraint
      * that given a pointer type `&'r T`, T must not contain regions
      * that outlive 'r, as well as analogous constraints for other
      * lifetime'd types.
@@ -583,11 +682,11 @@ fn constrain_regions_in_type(
     let e = rcx.errors_reported;
     let tcx = rcx.fcx.ccx.tcx;
 
-    debug!("constrain_regions_in_type(encl_region=%s, ty=%s)",
-           region_to_str(tcx, encl_region),
+    debug!("constrain_regions_in_type(minimum_lifetime=%s, ty=%s)",
+           region_to_str(tcx, minimum_lifetime),
            ty_to_str(tcx, ty));
 
-    do relate_nested_regions(tcx, Some(encl_region), ty) |r_sub, r_sup| {
+    do relate_nested_regions(tcx, Some(minimum_lifetime), ty) |r_sub, r_sup| {
         debug!("relate(r_sub=%s, r_sup=%s)",
                region_to_str(tcx, r_sub),
                region_to_str(tcx, r_sup));
@@ -595,12 +694,12 @@ fn constrain_regions_in_type(
         if r_sup.is_bound() || r_sub.is_bound() {
             // a bound region is one which appears inside an fn type.
             // (e.g., the `&` in `fn(&T)`).  Such regions need not be
-            // constrained by `encl_region` as they are placeholders
+            // constrained by `minimum_lifetime` as they are placeholders
             // for regions that are as-yet-unknown.
         } else {
             match rcx.fcx.mk_subr(true, span, r_sub, r_sup) {
                 result::Err(_) => {
-                    if r_sub == encl_region {
+                    if r_sub == minimum_lifetime {
                         tcx.sess.span_err(
                             span,
                             fmt!("reference is not valid outside of its lifetime"));
@@ -639,7 +738,6 @@ fn constrain_regions_in_type(
 
 pub mod guarantor {
     /*!
-     *
      * The routines in this module are aiming to deal with the case
      * where a the contents of a borrowed pointer are re-borrowed.
      * Imagine you have a borrowed pointer `b` with lifetime L1 and
@@ -686,6 +784,7 @@ pub mod guarantor {
      */
 
     use middle::typeck::check::regionck::{Rcx, infallibly_mk_subr};
+    use middle::typeck::check::regionck::mk_subregion_due_to_derefence;
     use middle::ty;
     use syntax::ast;
     use syntax::codemap::span;
@@ -693,14 +792,12 @@ pub mod guarantor {
 
     pub fn for_addr_of(rcx: @mut Rcx, expr: @ast::expr, base: @ast::expr) {
         /*!
-         *
          * Computes the guarantor for an expression `&base` and then
          * ensures that the lifetime of the resulting pointer is linked
          * to the lifetime of its guarantor (if any).
          */
 
         debug!("guarantor::for_addr_of(base=%s)", rcx.fcx.expr_to_str(base));
-        let _i = ::util::common::indenter();
 
         let guarantor = guarantor(rcx, base);
         link(rcx, expr.span, expr.id, guarantor);
@@ -708,13 +805,14 @@ pub mod guarantor {
 
     pub fn for_match(rcx: @mut Rcx, discr: @ast::expr, arms: &[ast::arm]) {
         /*!
-         *
          * Computes the guarantors for any ref bindings in a match and
          * then ensures that the lifetime of the resulting pointer is
          * linked to the lifetime of its guarantor (if any).
          */
 
+        debug!("regionck::for_match()");
         let discr_guarantor = guarantor(rcx, discr);
+        debug!("discr_guarantor=%s", discr_guarantor.repr(rcx.tcx()));
         for arms.each |arm| {
             for arm.pats.each |pat| {
                 link_ref_bindings_in_pat(rcx, *pat, discr_guarantor);
@@ -727,7 +825,6 @@ pub mod guarantor {
                        autoderefs: uint,
                        autoref: &ty::AutoRef) {
         /*!
-         *
          * Computes the guarantor for an expression that has an
          * autoref adjustment and links it to the lifetime of the
          * autoref.  This is only important when auto re-borrowing
@@ -736,30 +833,30 @@ pub mod guarantor {
 
         debug!("guarantor::for_autoref(expr=%s, autoref=%?)",
                rcx.fcx.expr_to_str(expr), autoref);
-        let _i = ::util::common::indenter();
 
         let mut expr_ct = categorize_unadjusted(rcx, expr);
         debug!("    unadjusted cat=%?", expr_ct.cat);
         expr_ct = apply_autoderefs(
             rcx, expr, autoderefs, expr_ct);
 
-        match autoref.kind {
-            ty::AutoPtr => {
+        match *autoref {
+            ty::AutoPtr(r, _) => {
                 // In this case, we are implicitly adding an `&`.
-                maybe_make_subregion(rcx, expr, autoref.region,
-                                     expr_ct.cat.guarantor);
+                maybe_make_subregion(rcx, expr, r, expr_ct.cat.guarantor);
             }
 
-            ty::AutoBorrowVec |
-            ty::AutoBorrowVecRef |
-            ty::AutoBorrowFn => {
+            ty::AutoBorrowVec(r, _) |
+            ty::AutoBorrowVecRef(r, _) |
+            ty::AutoBorrowFn(r) => {
                 // In each of these cases, what is being borrowed is
                 // not the (autoderef'd) expr itself but rather the
                 // contents of the autoderef'd expression (i.e., what
                 // the pointer points at).
-                maybe_make_subregion(rcx, expr, autoref.region,
+                maybe_make_subregion(rcx, expr, r,
                                      guarantor_of_deref(&expr_ct.cat));
             }
+
+            ty::AutoUnsafe(_) => {}
         }
 
         fn maybe_make_subregion(
@@ -774,6 +871,28 @@ pub mod guarantor {
         }
     }
 
+    pub fn for_by_ref(rcx: @mut Rcx,
+                      expr: @ast::expr,
+                      callee_scope: ast::node_id) {
+        /*!
+         * Computes the guarantor for cases where the `expr` is
+         * being passed by implicit reference and must outlive
+         * `callee_scope`.
+         */
+
+        let tcx = rcx.tcx();
+        debug!("guarantor::for_by_ref(expr=%s, callee_scope=%?)",
+               expr.repr(tcx), callee_scope);
+        let expr_cat = categorize(rcx, expr);
+        debug!("guarantor::for_by_ref(expr=%?, callee_scope=%?) category=%?",
+               expr.id, callee_scope, expr_cat);
+        let minimum_lifetime = ty::re_scope(callee_scope);
+        for expr_cat.guarantor.each |guarantor| {
+            mk_subregion_due_to_derefence(rcx, expr.span,
+                                          minimum_lifetime, *guarantor);
+        }
+    }
+
     fn link(
         rcx: @mut Rcx,
         span: span,
@@ -801,7 +920,7 @@ pub mod guarantor {
         // expressions, both of which always yield a region variable, so
         // mk_subr should never fail.
         let rptr_ty = rcx.resolve_node_type(id);
-        if !ty::type_is_error(rptr_ty) && !ty::type_is_bot(rptr_ty) {
+        if !ty::type_is_bot(rptr_ty) {
             let tcx = rcx.fcx.ccx.tcx;
             debug!("rptr_ty=%s", ty_to_str(tcx, rptr_ty));
             let r = ty::ty_region(tcx, span, rptr_ty);
@@ -907,7 +1026,6 @@ pub mod guarantor {
 
     fn categorize(rcx: @mut Rcx, expr: @ast::expr) -> ExprCategorization {
         debug!("categorize(expr=%s)", rcx.fcx.expr_to_str(expr));
-        let _i = ::util::common::indenter();
 
         let mut expr_ct = categorize_unadjusted(rcx, expr);
         debug!("before adjustments, cat=%?", expr_ct.cat);
@@ -928,12 +1046,24 @@ pub mod guarantor {
                 expr_ct = apply_autoderefs(
                     rcx, expr, adjustment.autoderefs, expr_ct);
 
-                for adjustment.autoref.each |autoref| {
-                    // If there is an autoref, then the result of this
-                    // expression will be some sort of borrowed pointer.
-                    expr_ct.cat.guarantor = None;
-                    expr_ct.cat.pointer = BorrowedPointer(autoref.region);
-                    debug!("autoref, cat=%?", expr_ct.cat);
+                match adjustment.autoref {
+                    None => {
+                    }
+                    Some(ty::AutoUnsafe(_)) => {
+                        expr_ct.cat.guarantor = None;
+                        expr_ct.cat.pointer = OtherPointer;
+                        debug!("autoref, cat=%?", expr_ct.cat);
+                    }
+                    Some(ty::AutoPtr(r, _)) |
+                    Some(ty::AutoBorrowVec(r, _)) |
+                    Some(ty::AutoBorrowVecRef(r, _)) |
+                    Some(ty::AutoBorrowFn(r)) => {
+                        // If there is an autoref, then the result of this
+                        // expression will be some sort of borrowed pointer.
+                        expr_ct.cat.guarantor = None;
+                        expr_ct.cat.pointer = BorrowedPointer(r);
+                        debug!("autoref, cat=%?", expr_ct.cat);
+                    }
                 }
             }
 
@@ -948,7 +1078,6 @@ pub mod guarantor {
                              expr: @ast::expr)
                           -> ExprCategorizationType {
         debug!("categorize_unadjusted(expr=%s)", rcx.fcx.expr_to_str(expr));
-        let _i = ::util::common::indenter();
 
         let guarantor = {
             if rcx.fcx.inh.method_map.contains_key(&expr.id) {
@@ -1053,7 +1182,6 @@ pub mod guarantor {
 
         debug!("link_ref_bindings_in_pat(pat=%s, guarantor=%?)",
                rcx.fcx.pat_to_str(pat), guarantor);
-        let _i = ::util::common::indenter();
 
         match pat.node {
             ast::pat_wild => {}
@@ -1069,7 +1197,10 @@ pub mod guarantor {
                     link_ref_bindings_in_pat(rcx, *p, guarantor);
                 }
             }
-            ast::pat_enum(*) => {}
+            ast::pat_enum(_, None) => {}
+            ast::pat_enum(_, Some(ref pats)) => {
+                link_ref_bindings_in_pats(rcx, pats, guarantor);
+            }
             ast::pat_struct(_, ref fpats, _) => {
                 for fpats.each |fpat| {
                     link_ref_bindings_in_pat(rcx, fpat.pat, guarantor);
@@ -1086,29 +1217,25 @@ pub mod guarantor {
             }
             ast::pat_region(p) => {
                 let rptr_ty = rcx.resolve_node_type(pat.id);
-                if !ty::type_is_error(rptr_ty) {
-                    let r = ty::ty_region(rcx.fcx.tcx(), pat.span, rptr_ty);
-                    link_ref_bindings_in_pat(rcx, p, Some(r));
-                }
+                let r = ty::ty_region(rcx.fcx.tcx(), pat.span, rptr_ty);
+                link_ref_bindings_in_pat(rcx, p, Some(r));
             }
             ast::pat_lit(*) => {}
             ast::pat_range(*) => {}
             ast::pat_vec(ref before, ref slice, ref after) => {
                 let vec_ty = rcx.resolve_node_type(pat.id);
-                if !ty::type_is_error(vec_ty) {
-                    let vstore = ty::ty_vstore(vec_ty);
-                    let guarantor1 = match vstore {
-                        ty::vstore_fixed(_) | ty::vstore_uniq => guarantor,
-                        ty::vstore_slice(r) => Some(r),
-                        ty::vstore_box => None
-                    };
-
-                    link_ref_bindings_in_pats(rcx, before, guarantor1);
-                    for slice.each |&p| {
-                        link_ref_bindings_in_pat(rcx, p, guarantor);
-                    }
-                    link_ref_bindings_in_pats(rcx, after, guarantor1);
+                let vstore = ty::ty_vstore(vec_ty);
+                let guarantor1 = match vstore {
+                    ty::vstore_fixed(_) | ty::vstore_uniq => guarantor,
+                    ty::vstore_slice(r) => Some(r),
+                    ty::vstore_box => None
+                };
+
+                link_ref_bindings_in_pats(rcx, before, guarantor1);
+                for slice.each |&p| {
+                    link_ref_bindings_in_pat(rcx, p, guarantor);
                 }
+                link_ref_bindings_in_pats(rcx, after, guarantor1);
             }
         }
     }
diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs
index f293893bc131f..cfbd012b7b7cd 100644
--- a/src/librustc/middle/typeck/check/regionmanip.rs
+++ b/src/librustc/middle/typeck/check/regionmanip.rs
@@ -87,7 +87,7 @@ pub fn replace_bound_regions_in_fn_sig(
                       to_r: &fn(ty::bound_region) -> ty::Region,
                       r: ty::Region) -> isr_alist {
             match r {
-              ty::re_free(*) | ty::re_static | ty::re_scope(_) |
+              ty::re_empty | ty::re_free(*) | ty::re_static | ty::re_scope(_) |
               ty::re_infer(_) => {
                 isr
               }
@@ -153,6 +153,7 @@ pub fn replace_bound_regions_in_fn_sig(
               }
 
               // Free regions like these just stay the same:
+              ty::re_empty |
               ty::re_static |
               ty::re_scope(_) |
               ty::re_free(*) |
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 7f390de1f4943..b5bd5a48e9d78 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -244,11 +244,14 @@ fn lookup_vtable(vcx: &VtableContext,
                     // Nothing found. Continue.
                 }
                 Some(implementations) => {
-                    let implementations: &mut ~[@Impl] = *implementations;
+                    let len = { // FIXME(#5074): stage0 requires it
+                        let implementations: &mut ~[@Impl] = *implementations;
+                        implementations.len()
+                    };
 
                     // implementations is the list of all impls in scope for
                     // trait_ref. (Usually, there's just one.)
-                    for uint::range(0, implementations.len()) |i| {
+                    for uint::range(0, len) |i| {
                         let im = implementations[i];
 
                         // im is one specific impl of trait_ref.
@@ -487,7 +490,7 @@ pub fn early_resolve_expr(ex: @ast::expr,
         for fcx.opt_node_ty_substs(ex.id) |substs| {
             debug!("vtable resolution on parameter bounds for expr %s",
                    ex.repr(fcx.tcx()));
-            let def = *cx.tcx.def_map.get(&ex.id);
+            let def = cx.tcx.def_map.get_copy(&ex.id);
             let did = ast_util::def_id_of_def(def);
             let item_ty = ty::lookup_item_type(cx.tcx, did);
             debug!("early resolve expr: def %? %?, %?, %s", ex.id, did, def,
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index d6b09d1e7f453..b7713eaa2fd6e 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -134,23 +134,22 @@ fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: span, id: ast::node_id)
         }
 
         Some(&@ty::AutoDerefRef(adj)) => {
-            let resolved_autoref = match adj.autoref {
-                Some(ref autoref) => {
-                    match resolve_region(fcx.infcx(), autoref.region,
-                                         resolve_all | force_all) {
-                        Err(e) => {
-                            // This should not, I think, happen.
-                            fcx.ccx.tcx.sess.span_err(
-                                sp, fmt!("cannot resolve scope of borrow: %s",
-                                         infer::fixup_err_to_str(e)));
-                            Some(*autoref)
-                        }
-                        Ok(r) => {
-                            Some(ty::AutoRef {region: r, ..*autoref})
-                        }
+            let fixup_region = |r| {
+                match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
+                    Ok(r1) => r1,
+                    Err(e) => {
+                        // This should not, I think, happen.
+                        fcx.ccx.tcx.sess.span_err(
+                            sp, fmt!("cannot resolve scope of borrow: %s",
+                                     infer::fixup_err_to_str(e)));
+                        r
                     }
                 }
-                None => None
+            };
+
+            let resolved_autoref = match adj.autoref {
+                None => None,
+                Some(ref r) => Some(r.map_region(fixup_region))
             };
 
             let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef {
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index fe07dc9412db2..82ef09a83bee9 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -238,8 +238,8 @@ pub impl CoherenceChecker {
 
     fn check_implementation(&self,
                             item: @item, associated_traits: ~[@trait_ref]) {
-        let self_type = self.crate_context.tcx.tcache.get(
-            &local_def(item.id));
+        let tcx = self.crate_context.tcx;
+        let self_type = ty::lookup_item_type(tcx, local_def(item.id));
 
         // If there are no traits, then this implementation must have a
         // base type.
@@ -390,7 +390,7 @@ pub impl CoherenceChecker {
 
             let pmm = self.crate_context.tcx.provided_methods;
             match pmm.find(&local_def(impl_id)) {
-                Some(mis) => {
+                Some(&mis) => {
                     // If the trait already has an entry in the
                     // provided_methods_map, we just need to add this
                     // method to that entry.
@@ -423,8 +423,8 @@ pub impl CoherenceChecker {
                 self.crate_context.coherence_info.inherent_methods
                     .insert(base_def_id, implementation_list);
             }
-            Some(existing_implementation_list) => {
-                implementation_list = *existing_implementation_list;
+            Some(&existing_implementation_list) => {
+                implementation_list = existing_implementation_list;
             }
         }
 
@@ -440,8 +440,8 @@ pub impl CoherenceChecker {
                 self.crate_context.coherence_info.extension_methods
                     .insert(trait_id, implementation_list);
             }
-            Some(existing_implementation_list) => {
-                implementation_list = *existing_implementation_list;
+            Some(&existing_implementation_list) => {
+                implementation_list = existing_implementation_list;
             }
         }
 
@@ -449,10 +449,8 @@ pub impl CoherenceChecker {
     }
 
     fn check_implementation_coherence(&self) {
-        let coherence_info = &mut self.crate_context.coherence_info;
-        let extension_methods = &coherence_info.extension_methods;
-
-        for extension_methods.each_key |&trait_id| {
+        let coherence_info = self.crate_context.coherence_info;
+        for coherence_info.extension_methods.each_key |&trait_id| {
             self.check_implementation_coherence_of(trait_id);
         }
     }
@@ -502,20 +500,23 @@ pub impl CoherenceChecker {
                 m.insert(self_t, the_impl);
                 self.crate_context.tcx.trait_impls.insert(trait_t, m);
             }
-            Some(m) => {
+            Some(&m) => {
                 m.insert(self_t, the_impl);
             }
         }
     }
 
     fn iter_impls_of_trait(&self, trait_def_id: def_id, f: &fn(@Impl)) {
-        let coherence_info = &mut self.crate_context.coherence_info;
-        let extension_methods = &coherence_info.extension_methods;
+        let coherence_info = self.crate_context.coherence_info;
+        let extension_methods = &*coherence_info.extension_methods;
 
         match extension_methods.find(&trait_def_id) {
             Some(impls) => {
-                let impls: &mut ~[@Impl] = *impls;
-                for uint::range(0, impls.len()) |i| {
+                let len = { // FIXME(#5074) stage0 requires this
+                    let impls: &mut ~[@Impl] = *impls;
+                    impls.len()
+                };
+                for uint::range(0, len) |i| {
                     f(impls[i]);
                 }
             }
@@ -645,7 +646,7 @@ pub impl CoherenceChecker {
 
     fn get_self_type_for_implementation(&self, implementation: @Impl)
                                      -> ty_param_bounds_and_ty {
-        return *self.crate_context.tcx.tcache.get(&implementation.did);
+        return self.crate_context.tcx.tcache.get_copy(&implementation.did);
     }
 
     // Privileged scope checking
@@ -701,7 +702,7 @@ pub impl CoherenceChecker {
 
     fn trait_ref_to_trait_def_id(&self, trait_ref: @trait_ref) -> def_id {
         let def_map = self.crate_context.tcx.def_map;
-        let trait_def = *def_map.get(&trait_ref.ref_id);
+        let trait_def = def_map.get_copy(&trait_ref.ref_id);
         let trait_id = def_id_of_def(trait_def);
         return trait_id;
     }
@@ -741,7 +742,7 @@ pub impl CoherenceChecker {
                                               -> bool {
         match original_type.node {
             ty_path(_, path_id) => {
-                match *self.crate_context.tcx.def_map.get(&path_id) {
+                match self.crate_context.tcx.def_map.get_copy(&path_id) {
                     def_ty(def_id) | def_struct(def_id) => {
                         if def_id.crate != local_crate {
                             return false;
@@ -1003,7 +1004,7 @@ pub impl CoherenceChecker {
     //
 
     fn populate_destructor_table(&self) {
-        let coherence_info = &mut self.crate_context.coherence_info;
+        let coherence_info = self.crate_context.coherence_info;
         let tcx = self.crate_context.tcx;
         let drop_trait = tcx.lang_items.drop_trait();
         let impls_opt = coherence_info.extension_methods.find(&drop_trait);
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index 45d61bb735728..22850c16f9436 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -219,7 +219,7 @@ pub fn ensure_trait_methods(ccx: &CrateCtxt,
 {
     let tcx = ccx.tcx;
     let region_paramd = tcx.region_paramd_items.find(&trait_id).map(|&x| *x);
-    match *tcx.items.get(&trait_id) {
+    match tcx.items.get_copy(&trait_id) {
         ast_map::node_item(@ast::item {
             node: ast::item_trait(ref generics, _, ref ms),
             _
diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs
index dcd1c861540f4..3620b609edf3b 100644
--- a/src/librustc/middle/typeck/infer/coercion.rs
+++ b/src/librustc/middle/typeck/infer/coercion.rs
@@ -65,7 +65,7 @@ we may want to adjust precisely when coercions occur.
 */
 
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowFn};
-use middle::ty::{AutoDerefRef, AutoRef};
+use middle::ty::{AutoDerefRef};
 use middle::ty::{vstore_slice, vstore_box, vstore_uniq};
 use middle::ty::{mt};
 use middle::ty;
@@ -120,9 +120,9 @@ pub impl Coerce {
                 };
             }
 
-            ty::ty_ptr(_) => {
+            ty::ty_ptr(mt_b) => {
                 return do self.unpack_actual_value(a) |sty_a| {
-                    self.coerce_unsafe_ptr(a, sty_a, b)
+                    self.coerce_unsafe_ptr(a, sty_a, b, mt_b)
                 };
             }
 
@@ -205,11 +205,7 @@ pub impl Coerce {
         if_ok!(sub.tys(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 1,
-            autoref: Some(AutoRef {
-                kind: AutoPtr,
-                region: r_borrow,
-                mutbl: mt_b.mutbl
-            })
+            autoref: Some(AutoPtr(r_borrow, mt_b.mutbl))
         })))
     }
 
@@ -235,11 +231,7 @@ pub impl Coerce {
         if_ok!(self.subtype(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
-            autoref: Some(AutoRef {
-                kind: AutoBorrowVec,
-                region: r_a,
-                mutbl: m_imm
-            })
+            autoref: Some(AutoBorrowVec(r_a, m_imm))
         })))
     }
 
@@ -268,11 +260,7 @@ pub impl Coerce {
         if_ok!(sub.tys(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
-            autoref: Some(AutoRef {
-                kind: AutoBorrowVec,
-                region: r_borrow,
-                mutbl: mt_b.mutbl
-            })
+            autoref: Some(AutoBorrowVec(r_borrow, mt_b.mutbl))
         })))
     }
 
@@ -308,11 +296,7 @@ pub impl Coerce {
         if_ok!(self.subtype(a_borrowed, b));
         Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
-            autoref: Some(AutoRef {
-                kind: AutoBorrowFn,
-                region: r_borrow,
-                mutbl: m_imm
-            })
+            autoref: Some(AutoBorrowFn(r_borrow))
         })))
     }
 
@@ -363,7 +347,8 @@ pub impl Coerce {
     fn coerce_unsafe_ptr(&self,
                          a: ty::t,
                          sty_a: &ty::sty,
-                         b: ty::t) -> CoerceResult
+                         b: ty::t,
+                         mt_b: ty::mt) -> CoerceResult
     {
         debug!("coerce_unsafe_ptr(a=%s, sty_a=%?, b=%s)",
                a.inf_str(self.infcx), sty_a,
@@ -376,10 +361,17 @@ pub impl Coerce {
             }
         };
 
-        // borrowed pointers and unsafe pointers have the same
-        // representation, so just check that the types which they
-        // point at are compatible:
+        // check that the types which they point at are compatible
         let a_unsafe = ty::mk_ptr(self.infcx.tcx, mt_a);
-        self.subtype(a_unsafe, b)
+        if_ok!(self.subtype(a_unsafe, b));
+
+        // although borrowed ptrs and unsafe ptrs have the same
+        // representation, we still register an AutoDerefRef so that
+        // regionck knows that that the region for `a` must be valid
+        // here
+        Ok(Some(@AutoDerefRef(AutoDerefRef {
+            autoderefs: 1,
+            autoref: Some(ty::AutoUnsafe(mt_b.mutbl))
+        })))
     }
 }
diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs
index dc1e3e845df4e..c195454b53276 100644
--- a/src/librustc/middle/typeck/infer/glb.rs
+++ b/src/librustc/middle/typeck/infer/glb.rs
@@ -16,6 +16,7 @@ use middle::typeck::infer::lub::Lub;
 use middle::typeck::infer::sub::Sub;
 use middle::typeck::infer::to_str::InferStr;
 use middle::typeck::infer::{cres, InferCtxt};
+use middle::typeck::infer::fold_regions_in_sig;
 use middle::typeck::isr_alist;
 use syntax::ast;
 use syntax::ast::{Many, Once, extern_fn, impure_fn, m_const, m_imm, m_mutbl};
@@ -188,7 +189,8 @@ impl Combine for Glb {
         let new_vars =
             self.infcx.region_vars.vars_created_since_snapshot(snapshot);
         let sig1 =
-            self.infcx.fold_regions_in_sig(
+            fold_regions_in_sig(
+                self.infcx.tcx,
                 &sig0,
                 |r, _in_fn| generalize_region(self, snapshot,
                                               new_vars, a_isr, a_vars, b_vars,
diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs
index 8591433801796..34e006c9615a7 100644
--- a/src/librustc/middle/typeck/infer/lub.rs
+++ b/src/librustc/middle/typeck/infer/lub.rs
@@ -16,6 +16,7 @@ use middle::typeck::infer::lattice::*;
 use middle::typeck::infer::sub::Sub;
 use middle::typeck::infer::to_str::InferStr;
 use middle::typeck::infer::{cres, InferCtxt};
+use middle::typeck::infer::fold_regions_in_sig;
 use middle::typeck::isr_alist;
 use util::common::indent;
 use util::ppaux::mt_to_str;
@@ -141,7 +142,8 @@ impl Combine for Lub {
         let new_vars =
             self.infcx.region_vars.vars_created_since_snapshot(snapshot);
         let sig1 =
-            self.infcx.fold_regions_in_sig(
+            fold_regions_in_sig(
+                self.infcx.tcx,
                 &sig0,
                 |r, _in_fn| generalize_region(self, snapshot, new_vars,
                                               a_isr, r));
diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs
index 8f709e7cd5af1..2e784b11c357b 100644
--- a/src/librustc/middle/typeck/infer/mod.rs
+++ b/src/librustc/middle/typeck/infer/mod.rs
@@ -339,7 +339,7 @@ pub fn fixup_err_to_str(f: fixup_err) -> ~str {
 
 fn new_ValsAndBindings<V:Copy,T:Copy>() -> ValsAndBindings<V, T> {
     ValsAndBindings {
-        vals: @mut SmallIntMap::new(),
+        vals: SmallIntMap::new(),
         bindings: ~[]
     }
 }
@@ -469,28 +469,6 @@ pub fn resolve_region(cx: @mut InferCtxt, r: ty::Region, modes: uint)
     resolver.resolve_region_chk(r)
 }
 
-/*
-fn resolve_borrowings(cx: @mut InferCtxt) {
-    for cx.borrowings.each |item| {
-        match resolve_region(cx, item.scope, resolve_all|force_all) {
-          Ok(region) => {
-            debug!("borrowing for expr %d resolved to region %?, mutbl %?",
-                   item.expr_id, region, item.mutbl);
-            cx.tcx.borrowings.insert(
-                item.expr_id, {region: region, mutbl: item.mutbl});
-          }
-
-          Err(e) => {
-            let str = fixup_err_to_str(e);
-            cx.tcx.sess.span_err(
-                item.span,
-                fmt!("could not resolve lifetime for borrow: %s", str));
-          }
-        }
-    }
-}
-*/
-
 trait then {
     fn then<T:Copy>(&self, f: &fn() -> Result<T,ty::type_err>)
         -> Result<T,ty::type_err>;
@@ -554,7 +532,8 @@ struct Snapshot {
 }
 
 pub impl InferCtxt {
-    fn combine_fields(@mut self, a_is_expected: bool,
+    fn combine_fields(@mut self,
+                      a_is_expected: bool,
                       span: span) -> CombineFields {
         CombineFields {infcx: self,
                        a_is_expected: a_is_expected,
@@ -565,25 +544,24 @@ pub impl InferCtxt {
         Sub(self.combine_fields(a_is_expected, span))
     }
 
-    fn in_snapshot(@mut self) -> bool {
+    fn in_snapshot(&self) -> bool {
         self.region_vars.in_snapshot()
     }
 
-    fn start_snapshot(@mut self) -> Snapshot {
-        let this = &mut *self;
+    fn start_snapshot(&mut self) -> Snapshot {
         Snapshot {
             ty_var_bindings_len:
-                this.ty_var_bindings.bindings.len(),
+                self.ty_var_bindings.bindings.len(),
             int_var_bindings_len:
-                this.int_var_bindings.bindings.len(),
+                self.int_var_bindings.bindings.len(),
             float_var_bindings_len:
-                this.float_var_bindings.bindings.len(),
+                self.float_var_bindings.bindings.len(),
             region_vars_snapshot:
-                this.region_vars.start_snapshot(),
+                self.region_vars.start_snapshot(),
         }
     }
 
-    fn rollback_to(@mut self, snapshot: &Snapshot) {
+    fn rollback_to(&mut self, snapshot: &Snapshot) {
         debug!("rollback!");
         rollback_to(&mut self.ty_var_bindings, snapshot.ty_var_bindings_len);
 
@@ -647,45 +625,47 @@ fn next_simple_var<V:Copy,T:Copy>(
 }
 
 pub impl InferCtxt {
-    fn next_ty_var_id(@mut self) -> TyVid {
+    fn next_ty_var_id(&mut self) -> TyVid {
         let id = self.ty_var_counter;
         self.ty_var_counter += 1;
-        let vals = self.ty_var_bindings.vals;
-        vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u));
+        {
+            let vals = &mut self.ty_var_bindings.vals;
+            vals.insert(id, Root(Bounds { lb: None, ub: None }, 0u));
+        }
         return TyVid(id);
     }
 
-    fn next_ty_var(@mut self) -> ty::t {
+    fn next_ty_var(&mut self) -> ty::t {
         ty::mk_var(self.tcx, self.next_ty_var_id())
     }
 
-    fn next_ty_vars(@mut self, n: uint) -> ~[ty::t] {
+    fn next_ty_vars(&mut self, n: uint) -> ~[ty::t] {
         vec::from_fn(n, |_i| self.next_ty_var())
     }
 
-    fn next_int_var_id(@mut self) -> IntVid {
+    fn next_int_var_id(&mut self) -> IntVid {
         IntVid(next_simple_var(&mut self.int_var_counter,
                                &mut self.int_var_bindings))
     }
 
-    fn next_int_var(@mut self) -> ty::t {
+    fn next_int_var(&mut self) -> ty::t {
         ty::mk_int_var(self.tcx, self.next_int_var_id())
     }
 
-    fn next_float_var_id(@mut self) -> FloatVid {
+    fn next_float_var_id(&mut self) -> FloatVid {
         FloatVid(next_simple_var(&mut self.float_var_counter,
                                  &mut self.float_var_bindings))
     }
 
-    fn next_float_var(@mut self) -> ty::t {
+    fn next_float_var(&mut self) -> ty::t {
         ty::mk_float_var(self.tcx, self.next_float_var_id())
     }
 
-    fn next_region_var_nb(@mut self, span: span) -> ty::Region {
+    fn next_region_var_nb(&mut self, span: span) -> ty::Region {
         ty::re_infer(ty::ReVar(self.region_vars.new_region_var(span)))
     }
 
-    fn next_region_var_with_lb(@mut self, span: span,
+    fn next_region_var_with_lb(&mut self, span: span,
                                lb_region: ty::Region) -> ty::Region {
         let region_var = self.next_region_var_nb(span);
 
@@ -697,12 +677,12 @@ pub impl InferCtxt {
         return region_var;
     }
 
-    fn next_region_var(@mut self, span: span, scope_id: ast::node_id)
+    fn next_region_var(&mut self, span: span, scope_id: ast::node_id)
                       -> ty::Region {
         self.next_region_var_with_lb(span, ty::re_scope(scope_id))
     }
 
-    fn resolve_regions(@mut self) {
+    fn resolve_regions(&mut self) {
         self.region_vars.resolve_regions();
     }
 
@@ -722,7 +702,6 @@ pub impl InferCtxt {
           result::Err(_) => typ
         }
     }
-
     fn resolve_type_vars_in_trait_ref_if_possible(@mut self,
                                                   trait_ref: &ty::TraitRef)
         -> ty::TraitRef
@@ -793,7 +772,7 @@ pub impl InferCtxt {
         self.type_error_message(sp, mk_msg, a, Some(err));
     }
 
-    fn replace_bound_regions_with_fresh_regions(@mut self,
+    fn replace_bound_regions_with_fresh_regions(&mut self,
             span: span,
             fsig: &ty::FnSig)
          -> (ty::FnSig, isr_alist) {
@@ -811,15 +790,14 @@ pub impl InferCtxt {
             });
         (fn_sig, isr)
     }
+}
 
-    fn fold_regions_in_sig(
-        @mut self,
-        fn_sig: &ty::FnSig,
-        fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig
-    {
-        do ty::fold_sig(fn_sig) |t| {
-            ty::fold_regions(self.tcx, t, fldr)
-        }
+pub fn fold_regions_in_sig(
+    tcx: ty::ctxt,
+    fn_sig: &ty::FnSig,
+    fldr: &fn(r: ty::Region, in_fn: bool) -> ty::Region) -> ty::FnSig
+{
+    do ty::fold_sig(fn_sig) |t| {
+        ty::fold_regions(tcx, t, fldr)
     }
-
 }
diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs
index 1ee59c3fe0649..a3b5369d22a34 100644
--- a/src/librustc/middle/typeck/infer/region_inference.rs
+++ b/src/librustc/middle/typeck/infer/region_inference.rs
@@ -24,7 +24,7 @@ it's worth spending more time on a more involved analysis.  Moreover,
 regions are a simpler case than types: they don't have aggregate
 structure, for example.
 
-Unlike normal type inference, which is similar in spirit H-M and thus
+Unlike normal type inference, which is similar in spirit to H-M and thus
 works progressively, the region type inference works by accumulating
 constraints over the course of a function.  Finally, at the end of
 processing a function, we process and solve the constraints all at
@@ -130,7 +130,7 @@ of these variables can effectively be unified into a single variable.
 Once SCCs are removed, we are left with a DAG.  At this point, we can
 walk the DAG in toplogical order once to compute the expanding nodes,
 and again in reverse topological order to compute the contracting
-nodes.The main reason I did not write it this way is that I did not
+nodes. The main reason I did not write it this way is that I did not
 feel like implementing the SCC and toplogical sort algorithms at the
 moment.
 
@@ -538,7 +538,7 @@ more convincing in the future.
 
 use middle::ty;
 use middle::ty::{FreeRegion, Region, RegionVid};
-use middle::ty::{re_static, re_infer, re_free, re_bound};
+use middle::ty::{re_empty, re_static, re_infer, re_free, re_bound};
 use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh};
 use middle::typeck::infer::cres;
 use util::common::indenter;
@@ -547,6 +547,9 @@ use util::ppaux::note_and_explain_region;
 use core::cell::{Cell, empty_cell};
 use core::hashmap::{HashMap, HashSet};
 use core::to_bytes;
+use core::uint;
+use core::vec;
+use core;
 use syntax::codemap::span;
 use syntax::ast;
 
@@ -572,18 +575,12 @@ impl to_bytes::IterBytes for Constraint {
     }
 }
 
-#[deriving(Eq)]
+#[deriving(Eq, IterBytes)]
 struct TwoRegions {
     a: Region,
     b: Region,
 }
 
-impl to_bytes::IterBytes for TwoRegions {
-    fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
-        to_bytes::iter_bytes_2(&self.a, &self.b, lsb0, f)
-    }
-}
-
 enum UndoLogEntry {
     Snapshot,
     AddVar(RegionVid),
@@ -637,7 +634,7 @@ pub fn RegionVarBindings(tcx: ty::ctxt) -> RegionVarBindings {
 }
 
 pub impl RegionVarBindings {
-    fn in_snapshot(&mut self) -> bool {
+    fn in_snapshot(&self) -> bool {
         self.undo_log.len() > 0
     }
 
@@ -832,7 +829,6 @@ pub impl RegionVarBindings {
     }
 
     fn resolve_var(&mut self, rid: RegionVid) -> ty::Region {
-        debug!("RegionVarBindings: resolve_var(%?=%u)", rid, rid.to_uint());
         if self.values.is_empty() {
             self.tcx.sess.span_bug(
                 self.var_spans[rid.to_uint()],
@@ -841,29 +837,14 @@ pub impl RegionVarBindings {
         }
 
         let v = self.values.with_ref(|values| values[rid.to_uint()]);
+        debug!("RegionVarBindings: resolve_var(%?=%u)=%?",
+               rid, rid.to_uint(), v);
         match v {
             Value(r) => r,
 
             NoValue => {
-                // No constraints, report an error.  It is plausible
-                // that we could select an arbitrary region here
-                // instead.  At the moment I am not doing this because
-                // this generally masks bugs in the inference
-                // algorithm, and given our syntax one cannot create
-                // generally create a lifetime variable that isn't
-                // used in some type, and hence all lifetime variables
-                // should ultimately have some bounds.
-
-                self.tcx.sess.span_err(
-                    self.var_spans[rid.to_uint()],
-                    fmt!("Unconstrained region variable #%u", rid.to_uint()));
-
-                // Touch of a hack: to suppress duplicate messages,
-                // replace the NoValue entry with ErrorValue.
-                let mut values = self.values.take();
-                values[rid.to_uint()] = ErrorValue;
-                self.values.put_back(values);
-                re_static
+                // No constraints, return ty::re_empty
+                re_empty
             }
 
             ErrorValue => {
@@ -1031,6 +1012,10 @@ priv impl RegionVarBindings {
             re_static // nothing lives longer than static
           }
 
+          (re_empty, r) | (r, re_empty) => {
+            r // everything lives longer than empty
+          }
+
           (re_infer(ReVar(v_id)), _) | (_, re_infer(ReVar(v_id))) => {
             self.tcx.sess.span_bug(
                 self.var_spans[v_id.to_uint()],
@@ -1127,6 +1112,11 @@ priv impl RegionVarBindings {
                 Ok(r)
             }
 
+            (re_empty, _) | (_, re_empty) => {
+                // nothing lives shorter than everything else
+                Ok(re_empty)
+            }
+
             (re_infer(ReVar(v_id)), _) |
             (_, re_infer(ReVar(v_id))) => {
                 self.tcx.sess.span_bug(
@@ -1266,8 +1256,6 @@ struct SpannedRegion {
     span: span,
 }
 
-type TwoRegionsMap = HashSet<TwoRegions>;
-
 pub impl RegionVarBindings {
     fn infer_variable_values(&mut self) -> ~[GraphNodeValue] {
         let mut graph = self.construct_graph();
@@ -1329,11 +1317,15 @@ pub impl RegionVarBindings {
                        node_id: RegionVid,
                        edge_dir: Direction,
                        edge_idx: uint) {
+            //! Insert edge `edge_idx` on the link list of edges in direction
+            //! `edge_dir` for the node `node_id`
             let edge_dir = edge_dir as uint;
-            graph.edges[edge_idx].next_edge[edge_dir] =
-                graph.nodes[node_id.to_uint()].head_edge[edge_dir];
-            graph.nodes[node_id.to_uint()].head_edge[edge_dir] =
-                edge_idx;
+            assert_eq!(graph.edges[edge_idx].next_edge[edge_dir],
+                       uint::max_value);
+            let n = node_id.to_uint();
+            let prev_head = graph.nodes[n].head_edge[edge_dir];
+            graph.edges[edge_idx].next_edge[edge_dir] = prev_head;
+            graph.nodes[n].head_edge[edge_dir] = edge_idx;
         }
     }
 
@@ -1484,6 +1476,8 @@ pub impl RegionVarBindings {
                     }
                 }
                 Err(_) => {
+                    debug!("Setting %? to ErrorValue: no glb of %?, %?",
+                           a_vid, a_region, b_region);
                     a_node.value = ErrorValue;
                     false
                 }
@@ -1495,7 +1489,21 @@ pub impl RegionVarBindings {
         &mut self,
         graph: &Graph) -> ~[GraphNodeValue]
     {
-        let mut dup_map = HashSet::new();
+        debug!("extract_values_and_report_conflicts()");
+
+        // This is the best way that I have found to suppress
+        // duplicate and related errors. Basically we keep a set of
+        // flags for every node. Whenever an error occurs, we will
+        // walk some portion of the graph looking to find pairs of
+        // conflicting regions to report to the user. As we walk, we
+        // trip the flags from false to true, and if we find that
+        // we've already reported an error involving any particular
+        // node we just stop and don't report the current error.  The
+        // idea is to report errors that derive from independent
+        // regions of the graph, but not those that derive from
+        // overlapping locations.
+        let mut dup_vec = graph.nodes.map(|_| uint::max_value);
+
         graph.nodes.mapi(|idx, node| {
             match node.value {
                 Value(_) => {
@@ -1530,15 +1538,16 @@ pub impl RegionVarBindings {
                        that is not used is not a problem, so if this rule
                        starts to create problems we'll have to revisit
                        this portion of the code and think hard about it. =) */
+
                     let node_vid = RegionVid { id: idx };
                     match node.classification {
                         Expanding => {
                             self.report_error_for_expanding_node(
-                                graph, &mut dup_map, node_vid);
+                                graph, dup_vec, node_vid);
                         }
                         Contracting => {
                             self.report_error_for_contracting_node(
-                                graph, &mut dup_map, node_vid);
+                                graph, dup_vec, node_vid);
                         }
                     }
                 }
@@ -1548,38 +1557,26 @@ pub impl RegionVarBindings {
         })
     }
 
-    // Used to suppress reporting the same basic error over and over
-    fn is_reported(&mut self,
-                   dup_map: &mut TwoRegionsMap,
-                   r_a: Region,
-                   r_b: Region)
-                -> bool {
-        let key = TwoRegions { a: r_a, b: r_b };
-        !dup_map.insert(key)
-    }
-
     fn report_error_for_expanding_node(&mut self,
                                        graph: &Graph,
-                                       dup_map: &mut TwoRegionsMap,
+                                       dup_vec: &mut [uint],
                                        node_idx: RegionVid) {
         // Errors in expanding nodes result from a lower-bound that is
         // not contained by an upper-bound.
-        let lower_bounds =
-            self.collect_concrete_regions(graph, node_idx, Incoming);
-        let upper_bounds =
-            self.collect_concrete_regions(graph, node_idx, Outgoing);
+        let (lower_bounds, lower_dup) =
+            self.collect_concrete_regions(graph, node_idx, Incoming, dup_vec);
+        let (upper_bounds, upper_dup) =
+            self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec);
+
+        if lower_dup || upper_dup {
+            return;
+        }
 
         for vec::each(lower_bounds) |lower_bound| {
             for vec::each(upper_bounds) |upper_bound| {
                 if !self.is_subregion_of(lower_bound.region,
                                          upper_bound.region) {
 
-                    if self.is_reported(dup_map,
-                                        lower_bound.region,
-                                        upper_bound.region) {
-                        return;
-                    }
-
                     self.tcx.sess.span_err(
                         self.var_spans[node_idx.to_uint()],
                         fmt!("cannot infer an appropriate lifetime \
@@ -1609,16 +1606,28 @@ pub impl RegionVarBindings {
                 }
             }
         }
+
+        self.tcx.sess.span_bug(
+            self.var_spans[node_idx.to_uint()],
+            fmt!("report_error_for_expanding_node() could not find error \
+                  for var %?, lower_bounds=%s, upper_bounds=%s",
+                 node_idx,
+                 lower_bounds.map(|x| x.region).repr(self.tcx),
+                 upper_bounds.map(|x| x.region).repr(self.tcx)));
     }
 
     fn report_error_for_contracting_node(&mut self,
                                          graph: &Graph,
-                                         dup_map: &mut TwoRegionsMap,
+                                         dup_vec: &mut [uint],
                                          node_idx: RegionVid) {
         // Errors in contracting nodes result from two upper-bounds
         // that have no intersection.
-        let upper_bounds = self.collect_concrete_regions(graph, node_idx,
-                                                         Outgoing);
+        let (upper_bounds, dup_found) =
+            self.collect_concrete_regions(graph, node_idx, Outgoing, dup_vec);
+
+        if dup_found {
+            return;
+        }
 
         for vec::each(upper_bounds) |upper_bound_1| {
             for vec::each(upper_bounds) |upper_bound_2| {
@@ -1627,12 +1636,6 @@ pub impl RegionVarBindings {
                   Ok(_) => {}
                   Err(_) => {
 
-                    if self.is_reported(dup_map,
-                                        upper_bound_1.region,
-                                        upper_bound_2.region) {
-                        return;
-                    }
-
                     self.tcx.sess.span_err(
                         self.var_spans[node_idx.to_uint()],
                         fmt!("cannot infer an appropriate lifetime \
@@ -1663,50 +1666,94 @@ pub impl RegionVarBindings {
                 }
             }
         }
+
+        self.tcx.sess.span_bug(
+            self.var_spans[node_idx.to_uint()],
+            fmt!("report_error_for_contracting_node() could not find error \
+                  for var %?, upper_bounds=%s",
+                 node_idx,
+                 upper_bounds.map(|x| x.region).repr(self.tcx)));
     }
 
     fn collect_concrete_regions(&mut self,
                                 graph: &Graph,
                                 orig_node_idx: RegionVid,
-                                dir: Direction)
-                             -> ~[SpannedRegion] {
-        let mut set = HashSet::new();
-        let mut stack = ~[orig_node_idx];
-        set.insert(orig_node_idx.to_uint());
-        let mut result = ~[];
-        while !vec::is_empty(stack) {
-            let node_idx = stack.pop();
-            for self.each_edge(graph, node_idx, dir) |edge| {
+                                dir: Direction,
+                                dup_vec: &mut [uint])
+                             -> (~[SpannedRegion], bool) {
+        struct WalkState {
+            set: HashSet<RegionVid>,
+            stack: ~[RegionVid],
+            result: ~[SpannedRegion],
+            dup_found: bool
+        }
+        let mut state = WalkState {
+            set: HashSet::new(),
+            stack: ~[orig_node_idx],
+            result: ~[],
+            dup_found: false
+        };
+        state.set.insert(orig_node_idx);
+
+        // to start off the process, walk the source node in the
+        // direction specified
+        process_edges(self, &mut state, graph, orig_node_idx, dir);
+
+        while !state.stack.is_empty() {
+            let node_idx = state.stack.pop();
+            let classification = graph.nodes[node_idx.to_uint()].classification;
+
+            // check whether we've visited this node on some previous walk
+            if dup_vec[node_idx.to_uint()] == uint::max_value {
+                dup_vec[node_idx.to_uint()] = orig_node_idx.to_uint();
+            } else if dup_vec[node_idx.to_uint()] != orig_node_idx.to_uint() {
+                state.dup_found = true;
+            }
+
+            debug!("collect_concrete_regions(orig_node_idx=%?, node_idx=%?, \
+                    classification=%?)",
+                   orig_node_idx, node_idx, classification);
+
+            // figure out the direction from which this node takes its
+            // values, and search for concrete regions etc in that direction
+            let dir = match classification {
+                Expanding => Incoming,
+                Contracting => Outgoing
+            };
+
+            process_edges(self, &mut state, graph, node_idx, dir);
+        }
+
+        let WalkState {result, dup_found, _} = state;
+        return (result, dup_found);
+
+        fn process_edges(self: &mut RegionVarBindings,
+                         state: &mut WalkState,
+                         graph: &Graph,
+                         source_vid: RegionVid,
+                         dir: Direction) {
+            debug!("process_edges(source_vid=%?, dir=%?)", source_vid, dir);
+
+            for self.each_edge(graph, source_vid, dir) |edge| {
                 match edge.constraint {
-                  ConstrainVarSubVar(from_vid, to_vid) => {
-                    let vid = match dir {
-                      Incoming => from_vid,
-                      Outgoing => to_vid
-                    };
-                    if set.insert(vid.to_uint()) {
-                        stack.push(vid);
+                    ConstrainVarSubVar(from_vid, to_vid) => {
+                        let opp_vid =
+                            if from_vid == source_vid {to_vid} else {from_vid};
+                        if state.set.insert(opp_vid) {
+                            state.stack.push(opp_vid);
+                        }
                     }
-                  }
-
-                  ConstrainRegSubVar(region, _) => {
-                    assert!(dir == Incoming);
-                    result.push(SpannedRegion {
-                        region: region,
-                        span: edge.span
-                    });
-                  }
 
-                  ConstrainVarSubReg(_, region) => {
-                    assert!(dir == Outgoing);
-                    result.push(SpannedRegion {
-                        region: region,
-                        span: edge.span
-                    });
-                  }
+                    ConstrainRegSubVar(region, _) |
+                    ConstrainVarSubReg(_, region) => {
+                        state.result.push(SpannedRegion {
+                            region: region,
+                            span: edge.span
+                        });
+                    }
                 }
             }
         }
-        return result;
     }
 
     fn each_edge(&mut self,
diff --git a/src/librustc/middle/typeck/infer/unify.rs b/src/librustc/middle/typeck/infer/unify.rs
index e50f2089e4fb5..3bcff92346566 100644
--- a/src/librustc/middle/typeck/infer/unify.rs
+++ b/src/librustc/middle/typeck/infer/unify.rs
@@ -23,7 +23,7 @@ pub enum VarValue<V, T> {
 }
 
 pub struct ValsAndBindings<V, T> {
-    vals: @mut SmallIntMap<VarValue<V, T>>,
+    vals: SmallIntMap<VarValue<V, T>>,
     bindings: ~[(V, VarValue<V, T>)],
 }
 
@@ -60,26 +60,25 @@ pub impl InferCtxt {
             vid: V) -> Node<V, T>
         {
             let vid_u = vid.to_uint();
-            match vb.vals.find(&vid_u) {
+            let var_val = match vb.vals.find(&vid_u) {
+                Some(&var_val) => var_val,
                 None => {
                     tcx.sess.bug(fmt!(
                         "failed lookup of vid `%u`", vid_u));
                 }
-                Some(var_val) => {
-                    match *var_val {
-                        Redirect(vid) => {
-                            let node: Node<V,T> = helper(tcx, vb, vid);
-                            if node.root != vid {
-                                // Path compression
-                                vb.vals.insert(vid.to_uint(),
-                                               Redirect(node.root));
-                            }
-                            node
-                        }
-                        Root(ref pt, rk) => {
-                            Node {root: vid, possible_types: *pt, rank: rk}
-                        }
+            };
+            match var_val {
+                Redirect(vid) => {
+                    let node: Node<V,T> = helper(tcx, vb, vid);
+                    if node.root != vid {
+                        // Path compression
+                        vb.vals.insert(vid.to_uint(),
+                                       Redirect(node.root));
                     }
+                    node
+                }
+                Root(pt, rk) => {
+                    Node {root: vid, possible_types: pt, rank: rk}
                 }
             }
         }
@@ -99,8 +98,8 @@ pub impl InferCtxt {
 
         { // FIXME(#4903)---borrow checker is not flow sensitive
             let vb = UnifyVid::appropriate_vals_and_bindings(self);
-            let old_v = vb.vals.get(&vid.to_uint());
-            vb.bindings.push((vid, *old_v));
+            let old_v = { *vb.vals.get(&vid.to_uint()) }; // FIXME(#4903)
+            vb.bindings.push((vid, old_v));
             vb.vals.insert(vid.to_uint(), new_v);
         }
     }
diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs
index 0012eb700302a..1a152f3c29119 100644
--- a/src/librustc/middle/typeck/mod.rs
+++ b/src/librustc/middle/typeck/mod.rs
@@ -414,7 +414,11 @@ pub fn check_crate(tcx: ty::ctxt,
     time(time_passes, ~"type collecting", ||
         collect::collect_item_types(ccx, crate));
 
-    time(time_passes, ~"method resolution", ||
+    // this ensures that later parts of type checking can assume that items
+    // have valid types and not error
+    tcx.sess.abort_if_errors();
+
+    time(time_passes, ~"coherence checking", ||
         coherence::check_coherence(ccx, crate));
 
     time(time_passes, ~"type checking", ||
diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc
index 7a7dcbb8bfd8d..f8a19eaf374cb 100644
--- a/src/librustc/rustc.rc
+++ b/src/librustc/rustc.rc
@@ -46,6 +46,7 @@ pub mod middle {
         pub mod controlflow;
         pub mod glue;
         pub mod datum;
+        pub mod write_guard;
         pub mod callee;
         pub mod expr;
         pub mod common;
@@ -75,6 +76,9 @@ pub mod middle {
     }
     pub mod ty;
     pub mod subst;
+    #[cfg(stage0)] #[path = "resolve_stage0.rs"]
+    pub mod resolve;
+    #[cfg(not(stage0))]
     pub mod resolve;
     #[path = "typeck/mod.rs"]
     pub mod typeck;
@@ -84,6 +88,7 @@ pub mod middle {
     pub mod lint;
     #[path = "borrowck/mod.rs"]
     pub mod borrowck;
+    pub mod dataflow;
     pub mod mem_categorization;
     pub mod liveness;
     pub mod kind;
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 69849a8d51b83..59a0a1ba3d611 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -13,7 +13,8 @@ use middle::ty::{ReSkolemized, ReVar};
 use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid};
 use middle::ty::{br_fresh, ctxt, field, method};
 use middle::ty::{mt, t, param_bound, param_ty};
-use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region};
+use middle::ty::{re_bound, re_free, re_scope, re_infer, re_static, Region,
+                 re_empty};
 use middle::ty::{ty_bool, ty_bot, ty_box, ty_struct, ty_enum};
 use middle::ty::{ty_err, ty_estr, ty_evec, ty_float, ty_bare_fn, ty_closure};
 use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param};
@@ -65,6 +66,9 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region)
           Some(&ast_map::node_block(ref blk)) => {
             explain_span(cx, "block", blk.span)
           }
+          Some(&ast_map::node_callee_scope(expr)) => {
+              explain_span(cx, "callee", expr.span)
+          }
           Some(&ast_map::node_expr(expr)) => {
             match expr.node {
               ast::expr_call(*) => explain_span(cx, "call", expr.span),
@@ -113,6 +117,8 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region)
 
       re_static => { (~"the static lifetime", None) }
 
+      re_empty => { (~"the empty lifetime", None) }
+
       // I believe these cases should not occur (except when debugging,
       // perhaps)
       re_infer(_) | re_bound(_) => {
@@ -212,7 +218,8 @@ pub fn region_to_str_space(cx: ctxt, prefix: &str, region: Region) -> ~str {
             bound_region_to_str_space(cx, prefix, br)
         }
         re_infer(ReVar(_)) => prefix.to_str(),
-        re_static => fmt!("%s'static ", prefix)
+        re_static => fmt!("%s'static ", prefix),
+        re_empty => fmt!("%s'<empty> ", prefix)
     }
 }
 
@@ -739,3 +746,12 @@ impl Repr for ty::vstore {
         vstore_to_str(tcx, *self)
     }
 }
+
+impl Repr for ast_map::path_elt {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        match *self {
+            ast_map::path_mod(id) => id.repr(tcx),
+            ast_map::path_name(id) => id.repr(tcx)
+        }
+    }
+}
diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs
index f45fb4e765833..98d7a01b928b0 100644
--- a/src/libstd/arc.rs
+++ b/src/libstd/arc.rs
@@ -419,26 +419,26 @@ pub struct RWReadMode<'self, T> {
 
 pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> {
     /// Access the pre-downgrade RWARC in write mode.
-    fn write<U>(&self, blk: &fn(x: &mut T) -> U) -> U {
+    fn write<U>(&mut self, blk: &fn(x: &mut T) -> U) -> U {
         match *self {
             RWWriteMode {
-                data: ref data,
+                data: &ref mut data,
                 token: ref token,
                 poison: _
             } => {
                 do token.write {
-                    blk(&mut **data)
+                    blk(data)
                 }
             }
         }
     }
     /// Access the pre-downgrade RWARC in write mode with a condvar.
-    fn write_cond<'x, 'c, U>(&self,
+    fn write_cond<'x, 'c, U>(&mut self,
                              blk: &fn(x: &'x mut T, c: &'c Condvar) -> U)
                           -> U {
         match *self {
             RWWriteMode {
-                data: ref data,
+                data: &ref mut data,
                 token: ref token,
                 poison: ref poison
             } => {
@@ -449,7 +449,7 @@ pub impl<'self, T:Const + Owned> RWWriteMode<'self, T> {
                             failed: &mut *poison.failed,
                             cond: cond
                         };
-                        blk(&mut **data, &cvar)
+                        blk(data, &cvar)
                     }
                 }
             }
@@ -598,8 +598,8 @@ mod tests {
         let arc = ~RWARC(1);
         let arc2 = (*arc).clone();
         do task::try || {
-            do arc2.write_downgrade |write_mode| {
-                do (&write_mode).write |one| {
+            do arc2.write_downgrade |mut write_mode| {
+                do write_mode.write |one| {
                     assert!(*one == 2);
                 }
             }
@@ -733,8 +733,8 @@ mod tests {
         }
 
         // Downgrader (us)
-        do arc.write_downgrade |write_mode| {
-            do (&write_mode).write_cond |state, cond| {
+        do arc.write_downgrade |mut write_mode| {
+            do write_mode.write_cond |state, cond| {
                 wc1.send(()); // send to another writer who will wake us up
                 while *state == 0 {
                     cond.wait();
diff --git a/src/libstd/arena.rs b/src/libstd/arena.rs
index b9a09323f81d0..da882d53fcffa 100644
--- a/src/libstd/arena.rs
+++ b/src/libstd/arena.rs
@@ -315,9 +315,6 @@ fn test_arena_destructors_fail() {
     }
     // Now, fail while allocating
     do arena.alloc::<@int> {
-        // First, recursively allocate something else; that needs to
-        // get freed too.
-        do arena.alloc { @20 };
         // Now fail.
         fail!();
     };
diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs
index 078ed6af5a02a..ceb67fcabfa5b 100644
--- a/src/libstd/bitv.rs
+++ b/src/libstd/bitv.rs
@@ -215,16 +215,16 @@ pub struct Bitv {
     nbits: uint
 }
 
-priv impl Bitv {
+fn die() -> ! {
+    fail!(~"Tried to do operation on bit vectors with different sizes");
+}
 
-    fn die(&self) -> ! {
-        fail!(~"Tried to do operation on bit vectors with different sizes");
-    }
+priv impl Bitv {
 
     #[inline(always)]
     fn do_op(&mut self, op: Op, other: &Bitv) -> bool {
         if self.nbits != other.nbits {
-            self.die();
+            die();
         }
         match self.rep {
           Small(ref mut s) => match other.rep {
@@ -234,10 +234,10 @@ priv impl Bitv {
               Assign     => s.become(*s1,     self.nbits),
               Difference => s.difference(*s1, self.nbits)
             },
-            Big(_) => self.die()
+            Big(_) => die()
           },
           Big(ref mut s) => match other.rep {
-            Small(_) => self.die(),
+            Small(_) => die(),
             Big(ref s1) => match op {
               Union      => s.union(*s1,      self.nbits),
               Intersect  => s.intersect(*s1,  self.nbits),
diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs
index 8a4bc823fd881..864a49a14294f 100644
--- a/src/libstd/ebml.rs
+++ b/src/libstd/ebml.rs
@@ -735,7 +735,7 @@ pub mod writer {
     priv impl Encoder {
         // used internally to emit things like the vector length and so on
         fn _emit_tagged_uint(&mut self, t: EbmlEncoderTag, v: uint) {
-            assert!(v <= 0xFFFF_FFFF_u);
+            assert!(v <= 0xFFFF_FFFF_u); // FIXME(#6130) assert warns on 32-bit
             self.wr_tagged_u32(t as uint, v as u32);
         }
 
diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs
index 6278db617c7da..53eb6c5561b68 100644
--- a/src/libstd/net_tcp.rs
+++ b/src/libstd/net_tcp.rs
@@ -883,8 +883,8 @@ impl io::Reader for TcpSocketBuf {
                     let ncopy = uint::min(nbuffered, needed);
                     let dst = ptr::mut_offset(
                         vec::raw::to_mut_ptr(buf), count);
-                    let src = ptr::const_offset(
-                        vec::raw::to_const_ptr(self.data.buf),
+                    let src = ptr::offset(
+                        vec::raw::to_ptr(self.data.buf),
                         self.data.buf_off);
                     ptr::copy_memory(dst, src, ncopy);
                     self.data.buf_off += ncopy;
@@ -967,7 +967,7 @@ impl io::Reader for TcpSocketBuf {
 
 /// Implementation of `io::Reader` trait for a buffered `net::tcp::TcpSocket`
 impl io::Writer for TcpSocketBuf {
-    pub fn write(&self, data: &const [u8]) {
+    pub fn write(&self, data: &[u8]) {
         unsafe {
             let socket_data_ptr: *TcpSocketData =
                 &(*((*(self.data)).sock).socket_data);
diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs
index 1292d2a858559..93364f8a319ee 100644
--- a/src/libstd/rope.rs
+++ b/src/libstd/rope.rs
@@ -1256,22 +1256,24 @@ mod tests {
         match (r) {
           node::Empty => return ~"",
           node::Content(x) => {
-            let str = @mut ~"";
-            fn aux(str: @mut ~str, node: @node::Node) {
+            let mut str = ~"";
+            fn aux(str: &mut ~str, node: @node::Node) {
                 match (*node) {
-                  node::Leaf(x) => {
-                    *str += str::slice(
-                        *x.content, x.byte_offset,
-                        x.byte_offset + x.byte_len).to_owned();
-                  }
-                  node::Concat(ref x) => {
-                    aux(str, x.left);
-                    aux(str, x.right);
-                  }
+                    node::Leaf(x) => {
+                        str::push_str(
+                            str,
+                            str::slice(
+                                *x.content, x.byte_offset,
+                                x.byte_offset + x.byte_len));
+                    }
+                    node::Concat(ref x) => {
+                        aux(str, x.left);
+                        aux(str, x.right);
+                    }
                 }
             }
-            aux(str, x);
-            return *str
+            aux(&mut str, x);
+            return str
           }
         }
     }
diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs
index 172532a2e5b6b..a18e2f47a7721 100644
--- a/src/libstd/sort.rs
+++ b/src/libstd/sort.rs
@@ -11,7 +11,6 @@
 //! Sorting methods
 
 use core::cmp::{Eq, Ord};
-use core::util;
 use core::vec::len;
 use core::vec;
 
@@ -23,12 +22,12 @@ type Le<'self, T> = &'self fn(v1: &T, v2: &T) -> bool;
  * Has worst case O(n log n) performance, best case O(n), but
  * is not space efficient. This is a stable sort.
  */
-pub fn merge_sort<T:Copy>(v: &const [T], le: Le<T>) -> ~[T] {
+pub fn merge_sort<T:Copy>(v: &[T], le: Le<T>) -> ~[T] {
     type Slice = (uint, uint);
 
     return merge_sort_(v, (0u, len(v)), le);
 
-    fn merge_sort_<T:Copy>(v: &const [T], slice: Slice, le: Le<T>)
+    fn merge_sort_<T:Copy>(v: &[T], slice: Slice, le: Le<T>)
         -> ~[T] {
         let begin = slice.first();
         let end = slice.second();
@@ -61,6 +60,7 @@ pub fn merge_sort<T:Copy>(v: &const [T], le: Le<T>) -> ~[T] {
     }
 }
 
+#[cfg(stage0)]
 fn part<T>(arr: &mut [T], left: uint,
            right: uint, pivot: uint, compare_func: Le<T>) -> uint {
     arr[pivot] <-> arr[right];
@@ -79,6 +79,23 @@ fn part<T>(arr: &mut [T], left: uint,
     return storage_index;
 }
 
+#[cfg(not(stage0))]
+fn part<T>(arr: &mut [T], left: uint,
+           right: uint, pivot: uint, compare_func: Le<T>) -> uint {
+    arr[pivot] <-> arr[right];
+    let mut storage_index: uint = left;
+    let mut i: uint = left;
+    while i < right {
+        if compare_func(&arr[i], &arr[right]) {
+            arr[i] <-> arr[storage_index];
+            storage_index += 1;
+        }
+        i += 1;
+    }
+    arr[storage_index] <-> arr[right];
+    return storage_index;
+}
+
 fn qsort<T>(arr: &mut [T], left: uint,
             right: uint, compare_func: Le<T>) {
     if right > left {
@@ -162,7 +179,8 @@ fn qsort3<T:Copy + Ord + Eq>(arr: &mut [T], left: int, right: int) {
  */
 pub fn quick_sort3<T:Copy + Ord + Eq>(arr: &mut [T]) {
     if arr.len() <= 1 { return; }
-    qsort3(arr, 0, (arr.len() - 1) as int);
+    let len = arr.len(); // FIXME(#5074) nested calls
+    qsort3(arr, 0, (len - 1) as int);
 }
 
 pub trait Sort {
@@ -195,15 +213,20 @@ pub fn tim_sort<T:Copy + Ord>(array: &mut [T]) {
     let mut idx = 0;
     let mut remaining = size;
     loop {
-        let arr = vec::mut_slice(array, idx, size);
-        let mut run_len: uint = count_run_ascending(arr);
-
-        if run_len < min_run {
-            let force = if remaining <= min_run {remaining} else {min_run};
-            let slice = vec::mut_slice(arr, 0, force);
-            binarysort(slice, run_len);
-            run_len = force;
-        }
+        let run_len: uint = {
+            // This scope contains the slice `arr` here:
+            let arr = vec::mut_slice(array, idx, size);
+            let mut run_len: uint = count_run_ascending(arr);
+
+            if run_len < min_run {
+                let force = if remaining <= min_run {remaining} else {min_run};
+                let slice = vec::mut_slice(arr, 0, force);
+                binarysort(slice, run_len);
+                run_len = force;
+            }
+
+            run_len
+        };
 
         ms.push_run(idx, run_len);
         ms.merge_collapse(array);
@@ -240,7 +263,7 @@ fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) {
         assert!(left == right);
         let n = start-left;
 
-        copy_vec(array, left+1, array, left, n);
+        shift_vec(array, left+1, left, n);
         array[left] = pivot;
         start += 1;
     }
@@ -250,7 +273,7 @@ fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) {
 fn reverse_slice<T>(v: &mut [T], start: uint, end:uint) {
     let mut i = start;
     while i < end / 2 {
-        util::swap(&mut v[i], &mut v[end - i - 1]);
+        v[i] <-> v[end - i - 1];
         i += 1;
     }
 }
@@ -286,8 +309,8 @@ fn count_run_ascending<T:Copy + Ord>(array: &mut [T]) -> uint {
     return run;
 }
 
-fn gallop_left<T:Copy + Ord>(key: &const T,
-                             array: &const [T],
+fn gallop_left<T:Copy + Ord>(key: &T,
+                             array: &[T],
                              hint: uint)
                           -> uint {
     let size = array.len();
@@ -337,8 +360,8 @@ fn gallop_left<T:Copy + Ord>(key: &const T,
     return ofs;
 }
 
-fn gallop_right<T:Copy + Ord>(key: &const T,
-                              array: &const [T],
+fn gallop_right<T:Copy + Ord>(key: &T,
+                              array: &[T],
                               hint: uint)
                            -> uint {
     let size = array.len();
@@ -433,14 +456,17 @@ impl<T:Copy + Ord> MergeState<T> {
             self.runs[n+1].len = self.runs[n+2].len;
         }
 
-        let slice = vec::mut_slice(array, b1, b1+l1);
-        let k = gallop_right(&const array[b2], slice, 0);
+        let k = { // constrain lifetime of slice below
+            let slice = vec::slice(array, b1, b1+l1);
+            gallop_right(&array[b2], slice, 0)
+        };
         b1 += k;
         l1 -= k;
         if l1 != 0 {
-            let slice = vec::mut_slice(array, b2, b2+l2);
-            let l2 = gallop_left(
-                &const array[b1+l1-1],slice,l2-1);
+            let l2 = { // constrain lifetime of slice below
+                let slice = vec::slice(array, b2, b2+l2);
+                gallop_left(&array[b1+l1-1],slice,l2-1)
+            };
             if l2 > 0 {
                 if l1 <= l2 {
                     self.merge_lo(array, b1, l1, b2, l2);
@@ -471,11 +497,11 @@ impl<T:Copy + Ord> MergeState<T> {
         dest += 1; c2 += 1; len2 -= 1;
 
         if len2 == 0 {
-            copy_vec(array, dest, tmp, 0, len1);
+            copy_vec(array, dest, tmp.slice(0, len1));
             return;
         }
         if len1 == 1 {
-            copy_vec(array, dest, array, c2, len2);
+            shift_vec(array, dest, c2, len2);
             array[dest+len2] <-> tmp[c1];
             return;
         }
@@ -513,10 +539,12 @@ impl<T:Copy + Ord> MergeState<T> {
             loop {
                 assert!(len1 > 1 && len2 != 0);
 
-                let tmp_view = vec::const_slice(tmp, c1, c1+len1);
-                count1 = gallop_right(&const array[c2], tmp_view, 0);
+                count1 = {
+                    let tmp_view = vec::slice(tmp, c1, c1+len1);
+                    gallop_right(&array[c2], tmp_view, 0)
+                };
                 if count1 != 0 {
-                    copy_vec(array, dest, tmp, c1, count1);
+                    copy_vec(array, dest, tmp.slice(c1, c1+count1));
                     dest += count1; c1 += count1; len1 -= count1;
                     if len1 <= 1 { break_outer = true; break; }
                 }
@@ -524,10 +552,12 @@ impl<T:Copy + Ord> MergeState<T> {
                 dest += 1; c2 += 1; len2 -= 1;
                 if len2 == 0 { break_outer = true; break; }
 
-                let tmp_view = vec::const_slice(array, c2, c2+len2);
-                count2 = gallop_left(&const tmp[c1], tmp_view, 0);
+                count2 = {
+                    let tmp_view = vec::slice(array, c2, c2+len2);
+                    gallop_left(&tmp[c1], tmp_view, 0)
+                };
                 if count2 != 0 {
-                    copy_vec(array, dest, array, c2, count2);
+                    shift_vec(array, dest, c2, count2);
                     dest += count2; c2 += count2; len2 -= count2;
                     if len2 == 0 { break_outer = true; break; }
                 }
@@ -547,14 +577,14 @@ impl<T:Copy + Ord> MergeState<T> {
 
         if len1 == 1 {
             assert!(len2 > 0);
-            copy_vec(array, dest, array, c2, len2);
+            shift_vec(array, dest, c2, len2);
             array[dest+len2] <-> tmp[c1];
         } else if len1 == 0 {
             fail!(~"Comparison violates its contract!");
         } else {
             assert!(len2 == 0);
             assert!(len1 > 1);
-            copy_vec(array, dest, tmp, c1, len1);
+            copy_vec(array, dest, tmp.slice(c1, c1+len1));
         }
     }
 
@@ -577,13 +607,13 @@ impl<T:Copy + Ord> MergeState<T> {
         dest -= 1; c1 -= 1; len1 -= 1;
 
         if len1 == 0 {
-            copy_vec(array, dest-(len2-1), tmp, 0, len2);
+            copy_vec(array, dest-(len2-1), tmp.slice(0, len2));
             return;
         }
         if len2 == 1 {
             dest -= len1;
             c1 -= len1;
-            copy_vec(array, dest+1, array, c1+1, len1);
+            shift_vec(array, dest+1, c1+1, len1);
             array[dest] <-> tmp[c2];
             return;
         }
@@ -621,13 +651,15 @@ impl<T:Copy + Ord> MergeState<T> {
             loop {
                 assert!(len2 > 1 && len1 != 0);
 
-                let tmp_view = vec::mut_slice(array, base1, base1+len1);
-                count1 = len1 - gallop_right(
-                    &const tmp[c2], tmp_view, len1-1);
+                { // constrain scope of tmp_view:
+                    let tmp_view = vec::mut_slice (array, base1, base1+len1);
+                    count1 = len1 - gallop_right(
+                        &tmp[c2], tmp_view, len1-1);
+                }
 
                 if count1 != 0 {
                     dest -= count1; c1 -= count1; len1 -= count1;
-                    copy_vec(array, dest+1, array, c1+1, count1);
+                    shift_vec(array, dest+1, c1+1, count1);
                     if len1 == 0 { break_outer = true; break; }
                 }
 
@@ -636,17 +668,16 @@ impl<T:Copy + Ord> MergeState<T> {
                 if len2 == 1 { break_outer = true; break; }
 
                 let count2;
-                {
+                { // constrain scope of tmp_view
                     let tmp_view = vec::mut_slice(tmp, 0, len2);
-                    count2 = len2 - gallop_left(&const array[c1],
+                    count2 = len2 - gallop_left(&array[c1],
                                                 tmp_view,
                                                 len2-1);
-                    // Make tmp_view go out of scope to appease borrowck.
                 }
 
                 if count2 != 0 {
                     dest -= count2; c2 -= count2; len2 -= count2;
-                    copy_vec(array, dest+1, tmp, c2+1, count2);
+                    copy_vec(array, dest+1, tmp.slice(c2+1, c2+1+count2));
                     if len2 <= 1 { break_outer = true; break; }
                 }
                 array[dest] <-> array[c1];
@@ -668,14 +699,14 @@ impl<T:Copy + Ord> MergeState<T> {
             assert!(len1 > 0);
             dest -= len1;
             c1 -= len1;
-            copy_vec(array, dest+1, array, c1+1, len1);
+            shift_vec(array, dest+1, c1+1, len1);
             array[dest] <-> tmp[c2];
         } else if len2 == 0 {
             fail!(~"Comparison violates its contract!");
         } else {
             assert!(len1 == 0);
             assert!(len2 != 0);
-            copy_vec(array, dest-(len2-1), tmp, 0, len2);
+            copy_vec(array, dest-(len2-1), tmp.slice(0, len2));
         }
     }
 
@@ -711,21 +742,25 @@ impl<T:Copy + Ord> MergeState<T> {
 #[inline(always)]
 fn copy_vec<T:Copy>(dest: &mut [T],
                     s1: uint,
-                    from: &const [T],
-                    s2: uint,
-                    len: uint) {
-    assert!(s1+len <= dest.len() && s2+len <= from.len());
-
-    let mut slice = ~[];
-    for uint::range(s2, s2+len) |i| {
-        slice.push(from[i]);
-    }
+                    from: &[T]) {
+    assert!(s1+from.len() <= dest.len());
 
-    for slice.eachi |i, v| {
+    for from.eachi |i, v| {
         dest[s1+i] = *v;
     }
 }
 
+#[inline(always)]
+fn shift_vec<T:Copy>(dest: &mut [T],
+                     s1: uint,
+                     s2: uint,
+                     len: uint) {
+    assert!(s1+len <= dest.len());
+
+    let tmp = dest.slice(s2, s2+len).to_vec();
+    copy_vec(dest, s1, tmp);
+}
+
 #[cfg(test)]
 mod test_qsort3 {
     use sort::*;
@@ -737,8 +772,7 @@ mod test_qsort3 {
         quick_sort3::<int>(v1);
         let mut i = 0;
         while i < len {
-            // debug!(v2[i]);
-            assert!((v2[i] == v1[i]));
+            assert_eq!(v2[i], v1[i]);
             i += 1;
         }
     }
@@ -1009,7 +1043,7 @@ mod big_tests {
         tabulate_managed(low, high);
     }
 
-    fn multiplyVec<T:Copy>(arr: &const [T], num: uint) -> ~[T] {
+    fn multiplyVec<T:Copy>(arr: &[T], num: uint) -> ~[T] {
         let size = arr.len();
         let res = do vec::from_fn(num) |i| {
             arr[i % size]
@@ -1025,7 +1059,7 @@ mod big_tests {
     }
 
     fn tabulate_unique(lo: uint, hi: uint) {
-        fn isSorted<T:Ord>(arr: &const [T]) {
+        fn isSorted<T:Ord>(arr: &[T]) {
             for uint::range(0, arr.len()-1) |i| {
                 if arr[i] > arr[i+1] {
                     fail!(~"Array not sorted");
@@ -1096,7 +1130,7 @@ mod big_tests {
     }
 
     fn tabulate_managed(lo: uint, hi: uint) {
-        fn isSorted<T:Ord>(arr: &const [@T]) {
+        fn isSorted<T:Ord>(arr: &[@T]) {
             for uint::range(0, arr.len()-1) |i| {
                 if arr[i] > arr[i+1] {
                     fail!(~"Array not sorted");
diff --git a/src/libstd/sort_stage0.rs b/src/libstd/sort_stage0.rs
new file mode 100644
index 0000000000000..f3d30ecd5cdf1
--- /dev/null
+++ b/src/libstd/sort_stage0.rs
@@ -0,0 +1,1239 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Sorting methods
+
+use core::cmp::{Eq, Ord};
+use core::vec::len;
+use core::vec;
+
+type Le<'self, T> = &'self fn(v1: &T, v2: &T) -> bool;
+
+/**
+ * Merge sort. Returns a new vector containing the sorted list.
+ *
+ * Has worst case O(n log n) performance, best case O(n), but
+ * is not space efficient. This is a stable sort.
+ */
+pub fn merge_sort<T:Copy>(v: &const [T], le: Le<T>) -> ~[T] {
+    type Slice = (uint, uint);
+
+    return merge_sort_(v, (0u, len(v)), le);
+
+    fn merge_sort_<T:Copy>(v: &const [T], slice: Slice, le: Le<T>)
+        -> ~[T] {
+        let begin = slice.first();
+        let end = slice.second();
+
+        let v_len = end - begin;
+        if v_len == 0 { return ~[]; }
+        if v_len == 1 { return ~[v[begin]]; }
+
+        let mid = v_len / 2 + begin;
+        let a = (begin, mid);
+        let b = (mid, end);
+        return merge(le, merge_sort_(v, a, le), merge_sort_(v, b, le));
+    }
+
+    fn merge<T:Copy>(le: Le<T>, a: &[T], b: &[T]) -> ~[T] {
+        let mut rs = vec::with_capacity(len(a) + len(b));
+        let a_len = len(a);
+        let mut a_ix = 0;
+        let b_len = len(b);
+        let mut b_ix = 0;
+        while a_ix < a_len && b_ix < b_len {
+            if le(&a[a_ix], &b[b_ix]) {
+                rs.push(a[a_ix]);
+                a_ix += 1;
+            } else { rs.push(b[b_ix]); b_ix += 1; }
+        }
+        rs.push_all(vec::slice(a, a_ix, a_len));
+        rs.push_all(vec::slice(b, b_ix, b_len));
+        rs
+    }
+}
+
+#[cfg(stage0)]
+fn part<T>(arr: &mut [T], left: uint,
+           right: uint, pivot: uint, compare_func: Le<T>) -> uint {
+    arr[pivot] <-> arr[right];
+    let mut storage_index: uint = left;
+    let mut i: uint = left;
+    while i < right {
+        let a: &mut T = &mut arr[i];
+        let b: &mut T = &mut arr[right];
+        if compare_func(a, b) {
+            arr[i] <-> arr[storage_index];
+            storage_index += 1;
+        }
+        i += 1;
+    }
+    arr[storage_index] <-> arr[right];
+    return storage_index;
+}
+
+#[cfg(not(stage0))]
+fn part<T>(arr: &mut [T], left: uint,
+           right: uint, pivot: uint, compare_func: Le<T>) -> uint {
+    arr[pivot] <-> arr[right];
+    let mut storage_index: uint = left;
+    let mut i: uint = left;
+    while i < right {
+        if compare_func(&arr[i], &arr[right]) {
+            arr[i] <-> arr[storage_index];
+            storage_index += 1;
+        }
+        i += 1;
+    }
+    arr[storage_index] <-> arr[right];
+    return storage_index;
+}
+
+fn qsort<T>(arr: &mut [T], left: uint,
+            right: uint, compare_func: Le<T>) {
+    if right > left {
+        let pivot = (left + right) / 2u;
+        let new_pivot = part::<T>(arr, left, right, pivot, compare_func);
+        if new_pivot != 0u {
+            // Need to do this check before recursing due to overflow
+            qsort::<T>(arr, left, new_pivot - 1u, compare_func);
+        }
+        qsort::<T>(arr, new_pivot + 1u, right, compare_func);
+    }
+}
+
+/**
+ * Quicksort. Sorts a mut vector in place.
+ *
+ * Has worst case O(n^2) performance, average case O(n log n).
+ * This is an unstable sort.
+ */
+pub fn quick_sort<T>(arr: &mut [T], compare_func: Le<T>) {
+    if len::<T>(arr) == 0u { return; }
+    qsort::<T>(arr, 0u, len::<T>(arr) - 1u, compare_func);
+}
+
+fn qsort3<T:Copy + Ord + Eq>(arr: &mut [T], left: int, right: int) {
+    if right <= left { return; }
+    let v: T = arr[right];
+    let mut i: int = left - 1;
+    let mut j: int = right;
+    let mut p: int = i;
+    let mut q: int = j;
+    loop {
+        i += 1;
+        while arr[i] < v { i += 1; }
+        j -= 1;
+        while v < arr[j] {
+            if j == left { break; }
+            j -= 1;
+        }
+        if i >= j { break; }
+        arr[i] <-> arr[j];
+        if arr[i] == v {
+            p += 1;
+            arr[p] <-> arr[i];
+        }
+        if v == arr[j] {
+            q -= 1;
+            arr[j] <-> arr[q];
+        }
+    }
+    arr[i] <-> arr[right];
+    j = i - 1;
+    i += 1;
+    let mut k: int = left;
+    while k < p {
+        arr[k] <-> arr[j];
+        k += 1;
+        j -= 1;
+        if k == len::<T>(arr) as int { break; }
+    }
+    k = right - 1;
+    while k > q {
+        arr[i] <-> arr[k];
+        k -= 1;
+        i += 1;
+        if k == 0 { break; }
+    }
+    qsort3::<T>(arr, left, j);
+    qsort3::<T>(arr, i, right);
+}
+
+/**
+ * Fancy quicksort. Sorts a mut vector in place.
+ *
+ * Based on algorithm presented by ~[Sedgewick and Bentley]
+ * (http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf).
+ * According to these slides this is the algorithm of choice for
+ * 'randomly ordered keys, abstract compare' & 'small number of key values'.
+ *
+ * This is an unstable sort.
+ */
+pub fn quick_sort3<T:Copy + Ord + Eq>(arr: &mut [T]) {
+    if arr.len() <= 1 { return; }
+    let len = arr.len() - 1; // FIXME(#5074) nested calls
+    qsort3(arr, 0, (len - 1) as int);
+}
+
+pub trait Sort {
+    fn qsort(self);
+}
+
+impl<'self, T:Copy + Ord + Eq> Sort for &'self mut [T] {
+    fn qsort(self) { quick_sort3(self); }
+}
+
+static MIN_MERGE: uint = 64;
+static MIN_GALLOP: uint = 7;
+static INITIAL_TMP_STORAGE: uint = 128;
+
+pub fn tim_sort<T:Copy + Ord>(array: &mut [T]) {
+    let size = array.len();
+    if size < 2 {
+        return;
+    }
+
+    if size < MIN_MERGE {
+        let init_run_len = count_run_ascending(array);
+        binarysort(array, init_run_len);
+        return;
+    }
+
+    let mut ms = MergeState();
+    let min_run = min_run_length(size);
+
+    let mut idx = 0;
+    let mut remaining = size;
+    loop {
+        let run_len: uint = {
+            // This scope contains the slice `arr` here:
+            let arr = vec::mut_slice(array, idx, size);
+            let mut run_len: uint = count_run_ascending(arr);
+
+            if run_len < min_run {
+                let force = if remaining <= min_run {remaining} else {min_run};
+                let slice = vec::mut_slice(arr, 0, force);
+                binarysort(slice, run_len);
+                run_len = force;
+            }
+
+            run_len
+        };
+
+        ms.push_run(idx, run_len);
+        ms.merge_collapse(array);
+
+        idx += run_len;
+        remaining -= run_len;
+        if remaining == 0 { break; }
+    }
+
+    ms.merge_force_collapse(array);
+}
+
+fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) {
+    let size = array.len();
+    let mut start = start;
+    assert!(start <= size);
+
+    if start == 0 { start += 1; }
+
+    while start < size {
+        let pivot = array[start];
+        let mut left = 0;
+        let mut right = start;
+        assert!(left <= right);
+
+        while left < right {
+            let mid = (left + right) >> 1;
+            if pivot < array[mid] {
+                right = mid;
+            } else {
+                left = mid+1;
+            }
+        }
+        assert!(left == right);
+        let n = start-left;
+
+        copy_vec(array, left+1, array, left, n);
+        array[left] = pivot;
+        start += 1;
+    }
+}
+
+// Reverse the order of elements in a slice, in place
+fn reverse_slice<T>(v: &mut [T], start: uint, end:uint) {
+    let mut i = start;
+    while i < end / 2 {
+        v[i] <-> v[end - i - 1];
+        i += 1;
+    }
+}
+
+fn min_run_length(n: uint) -> uint {
+    let mut n = n;
+    let mut r = 0;   // becomes 1 if any 1 bits are shifted off
+
+    while n >= MIN_MERGE {
+        r |= n & 1;
+        n >>= 1;
+    }
+    return n + r;
+}
+
+fn count_run_ascending<T:Copy + Ord>(array: &mut [T]) -> uint {
+    let size = array.len();
+    assert!(size > 0);
+    if size == 1 { return 1; }
+
+    let mut run = 2;
+    if array[1] < array[0] {
+        while run < size && array[run] < array[run-1] {
+            run += 1;
+        }
+        reverse_slice(array, 0, run);
+    } else {
+        while run < size && array[run] >= array[run-1] {
+            run += 1;
+        }
+    }
+
+    return run;
+}
+
+fn gallop_left<T:Copy + Ord>(key: &const T,
+                             array: &const [T],
+                             hint: uint)
+                          -> uint {
+    let size = array.len();
+    assert!(size != 0 && hint < size);
+
+    let mut last_ofs = 0;
+    let mut ofs = 1;
+
+    if *key > array[hint] {
+        // Gallop right until array[hint+last_ofs] < key <= array[hint+ofs]
+        let max_ofs = size - hint;
+        while ofs < max_ofs && *key > array[hint+ofs] {
+            last_ofs = ofs;
+            ofs = (ofs << 1) + 1;
+            if ofs < last_ofs { ofs = max_ofs; } // uint overflow guard
+        }
+        if ofs > max_ofs { ofs = max_ofs; }
+
+        last_ofs += hint;
+        ofs += hint;
+    } else {
+        let max_ofs = hint + 1;
+        while ofs < max_ofs && *key <= array[hint-ofs] {
+            last_ofs = ofs;
+            ofs = (ofs << 1) + 1;
+            if ofs < last_ofs { ofs = max_ofs; } // uint overflow guard
+        }
+
+        if ofs > max_ofs { ofs = max_ofs; }
+
+        let tmp = last_ofs;
+        last_ofs = hint - ofs;
+        ofs = hint - tmp;
+    }
+    assert!((last_ofs < ofs || last_ofs+1 < ofs+1) && ofs <= size);
+
+    last_ofs += 1;
+    while last_ofs < ofs {
+        let m = last_ofs + ((ofs - last_ofs) >> 1);
+        if *key > array[m] {
+            last_ofs = m+1;
+        } else {
+            ofs = m;
+        }
+    }
+    assert!(last_ofs == ofs);
+    return ofs;
+}
+
+fn gallop_right<T:Copy + Ord>(key: &const T,
+                              array: &const [T],
+                              hint: uint)
+                           -> uint {
+    let size = array.len();
+    assert!(size != 0 && hint < size);
+
+    let mut last_ofs = 0;
+    let mut ofs = 1;
+
+    if *key >= array[hint] {
+        // Gallop right until array[hint+last_ofs] <= key < array[hint+ofs]
+        let max_ofs = size - hint;
+        while ofs < max_ofs && *key >= array[hint+ofs] {
+            last_ofs = ofs;
+            ofs = (ofs << 1) + 1;
+            if ofs < last_ofs { ofs = max_ofs; }
+        }
+        if ofs > max_ofs { ofs = max_ofs; }
+
+        last_ofs += hint;
+        ofs += hint;
+    } else {
+        // Gallop left until array[hint-ofs] <= key < array[hint-last_ofs]
+        let max_ofs = hint + 1;
+        while ofs < max_ofs && *key < array[hint-ofs] {
+            last_ofs = ofs;
+            ofs = (ofs << 1) + 1;
+            if ofs < last_ofs { ofs = max_ofs; }
+        }
+        if ofs > max_ofs { ofs = max_ofs; }
+
+        let tmp = last_ofs;
+        last_ofs = hint - ofs;
+        ofs = hint - tmp;
+    }
+
+    assert!((last_ofs < ofs || last_ofs+1 < ofs+1) && ofs <= size);
+
+    last_ofs += 1;
+    while last_ofs < ofs {
+        let m = last_ofs + ((ofs - last_ofs) >> 1);
+
+        if *key >= array[m] {
+            last_ofs = m + 1;
+        } else {
+            ofs = m;
+        }
+    }
+    assert!(last_ofs == ofs);
+    return ofs;
+}
+
+struct RunState {
+    base: uint,
+    len: uint,
+}
+
+struct MergeState<T> {
+    min_gallop: uint,
+    runs: ~[RunState],
+}
+
+// Fixme (#3853) Move into MergeState
+fn MergeState<T>() -> MergeState<T> {
+    MergeState {
+        min_gallop: MIN_GALLOP,
+        runs: ~[],
+    }
+}
+
+impl<T:Copy + Ord> MergeState<T> {
+    fn push_run(&mut self, run_base: uint, run_len: uint) {
+        let tmp = RunState{base: run_base, len: run_len};
+        self.runs.push(tmp);
+    }
+
+    fn merge_at(&mut self, n: uint, array: &mut [T]) {
+        let size = self.runs.len();
+        assert!(size >= 2);
+        assert!(n == size-2 || n == size-3);
+
+        let mut b1 = self.runs[n].base;
+        let mut l1 = self.runs[n].len;
+        let b2 = self.runs[n+1].base;
+        let l2 = self.runs[n+1].len;
+
+        assert!(l1 > 0 && l2 > 0);
+        assert!(b1 + l1 == b2);
+
+        self.runs[n].len = l1 + l2;
+        if n == size-3 {
+            self.runs[n+1].base = self.runs[n+2].base;
+            self.runs[n+1].len = self.runs[n+2].len;
+        }
+
+        let k = { // constrain lifetime of slice below
+            let slice = vec::mut_slice(array, b1, b1+l1);
+            gallop_right(&const array[b2], slice, 0)
+        };
+        b1 += k;
+        l1 -= k;
+        if l1 != 0 {
+            let l2 = { // constrain lifetime of slice below
+                let slice = vec::mut_slice(array, b2, b2+l2);
+                gallop_left(&const array[b1+l1-1],slice,l2-1)
+            };
+            if l2 > 0 {
+                if l1 <= l2 {
+                    self.merge_lo(array, b1, l1, b2, l2);
+                } else {
+                    self.merge_hi(array, b1, l1, b2, l2);
+                }
+            }
+        }
+        self.runs.pop();
+    }
+
+    fn merge_lo(&mut self, array: &mut [T], base1: uint, len1: uint,
+                base2: uint, len2: uint) {
+        assert!(len1 != 0 && len2 != 0 && base1+len1 == base2);
+
+        let mut tmp = ~[];
+        for uint::range(base1, base1+len1) |i| {
+            tmp.push(array[i]);
+        }
+
+        let mut c1 = 0;
+        let mut c2 = base2;
+        let mut dest = base1;
+        let mut len1 = len1;
+        let mut len2 = len2;
+
+        array[dest] <-> array[c2];
+        dest += 1; c2 += 1; len2 -= 1;
+
+        if len2 == 0 {
+            copy_vec(array, dest, tmp, 0, len1);
+            return;
+        }
+        if len1 == 1 {
+            copy_vec(array, dest, array, c2, len2);
+            array[dest+len2] <-> tmp[c1];
+            return;
+        }
+
+        let mut min_gallop = self.min_gallop;
+        loop {
+            let mut count1 = 0;
+            let mut count2 = 0;
+            let mut break_outer = false;
+
+            loop {
+                assert!(len1 > 1 && len2 != 0);
+                if array[c2] < tmp[c1] {
+                    array[dest] <-> array[c2];
+                    dest += 1; c2 += 1; len2 -= 1;
+                    count2 += 1; count1 = 0;
+                    if len2 == 0 {
+                        break_outer = true;
+                    }
+                } else {
+                    array[dest] <-> tmp[c1];
+                    dest += 1; c1 += 1; len1 -= 1;
+                    count1 += 1; count2 = 0;
+                    if len1 == 1 {
+                        break_outer = true;
+                    }
+                }
+                if break_outer || ((count1 | count2) >= min_gallop) {
+                    break;
+                }
+            }
+            if break_outer { break; }
+
+            // Start to gallop
+            loop {
+                assert!(len1 > 1 && len2 != 0);
+
+                let tmp_view = vec::const_slice(tmp, c1, c1+len1);
+                count1 = gallop_right(&const array[c2], tmp_view, 0);
+                if count1 != 0 {
+                    copy_vec(array, dest, tmp, c1, count1);
+                    dest += count1; c1 += count1; len1 -= count1;
+                    if len1 <= 1 { break_outer = true; break; }
+                }
+                array[dest] <-> array[c2];
+                dest += 1; c2 += 1; len2 -= 1;
+                if len2 == 0 { break_outer = true; break; }
+
+                let tmp_view = vec::const_slice(array, c2, c2+len2);
+                count2 = gallop_left(&const tmp[c1], tmp_view, 0);
+                if count2 != 0 {
+                    copy_vec(array, dest, array, c2, count2);
+                    dest += count2; c2 += count2; len2 -= count2;
+                    if len2 == 0 { break_outer = true; break; }
+                }
+                array[dest] <-> tmp[c1];
+                dest += 1; c1 += 1; len1 -= 1;
+                if len1 == 1 { break_outer = true; break; }
+                min_gallop -= 1;
+                if !(count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) {
+                    break;
+                }
+            }
+            if break_outer { break; }
+            if min_gallop < 0 { min_gallop = 0; }
+            min_gallop += 2; // Penalize for leaving gallop
+        }
+        self.min_gallop = if min_gallop < 1 { 1 } else { min_gallop };
+
+        if len1 == 1 {
+            assert!(len2 > 0);
+            copy_vec(array, dest, array, c2, len2);
+            array[dest+len2] <-> tmp[c1];
+        } else if len1 == 0 {
+            fail!(~"Comparison violates its contract!");
+        } else {
+            assert!(len2 == 0);
+            assert!(len1 > 1);
+            copy_vec(array, dest, tmp, c1, len1);
+        }
+    }
+
+    fn merge_hi(&mut self, array: &mut [T], base1: uint, len1: uint,
+                base2: uint, len2: uint) {
+        assert!(len1 != 1 && len2 != 0 && base1 + len1 == base2);
+
+        let mut tmp = ~[];
+        for uint::range(base2, base2+len2) |i| {
+            tmp.push(array[i]);
+        }
+
+        let mut c1 = base1 + len1 - 1;
+        let mut c2 = len2 - 1;
+        let mut dest = base2 + len2 - 1;
+        let mut len1 = len1;
+        let mut len2 = len2;
+
+        array[dest] <-> array[c1];
+        dest -= 1; c1 -= 1; len1 -= 1;
+
+        if len1 == 0 {
+            copy_vec(array, dest-(len2-1), tmp, 0, len2);
+            return;
+        }
+        if len2 == 1 {
+            dest -= len1;
+            c1 -= len1;
+            copy_vec(array, dest+1, array, c1+1, len1);
+            array[dest] <-> tmp[c2];
+            return;
+        }
+
+        let mut min_gallop = self.min_gallop;
+        loop {
+            let mut count1 = 0;
+            let mut count2 = 0;
+            let mut break_outer = false;
+
+            loop {
+                assert!(len1 != 0 && len2 > 1);
+                if tmp[c2] < array[c1] {
+                    array[dest] <-> array[c1];
+                    dest -= 1; c1 -= 1; len1 -= 1;
+                    count1 += 1; count2 = 0;
+                    if len1 == 0 {
+                        break_outer = true;
+                    }
+                } else {
+                    array[dest] <-> tmp[c2];
+                    dest -= 1; c2 -= 1; len2 -= 1;
+                    count2 += 1; count1 = 0;
+                    if len2 == 1 {
+                        break_outer = true;
+                    }
+                }
+                if break_outer || ((count1 | count2) >= min_gallop) {
+                    break;
+                }
+            }
+            if break_outer { break; }
+
+            // Start to gallop
+            loop {
+                assert!(len2 > 1 && len1 != 0);
+
+                { // constrain scope of tmp_view:
+                    let tmp_view = vec::mut_slice (array, base1, base1+len1);
+                    count1 = len1 - gallop_right(
+                        &const tmp[c2], tmp_view, len1-1);
+                }
+
+                if count1 != 0 {
+                    dest -= count1; c1 -= count1; len1 -= count1;
+                    copy_vec(array, dest+1, array, c1+1, count1);
+                    if len1 == 0 { break_outer = true; break; }
+                }
+
+                array[dest] <-> tmp[c2];
+                dest -= 1; c2 -= 1; len2 -= 1;
+                if len2 == 1 { break_outer = true; break; }
+
+                let count2;
+                { // constrain scope of tmp_view
+                    let tmp_view = vec::mut_slice(tmp, 0, len2);
+                    count2 = len2 - gallop_left(&const array[c1],
+                                                tmp_view,
+                                                len2-1);
+                }
+
+                if count2 != 0 {
+                    dest -= count2; c2 -= count2; len2 -= count2;
+                    copy_vec(array, dest+1, tmp, c2+1, count2);
+                    if len2 <= 1 { break_outer = true; break; }
+                }
+                array[dest] <-> array[c1];
+                dest -= 1; c1 -= 1; len1 -= 1;
+                if len1 == 0 { break_outer = true; break; }
+                min_gallop -= 1;
+                if !(count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) {
+                    break;
+                }
+            }
+
+            if break_outer { break; }
+            if min_gallop < 0 { min_gallop = 0; }
+            min_gallop += 2; // Penalize for leaving gallop
+        }
+        self.min_gallop = if min_gallop < 1 { 1 } else { min_gallop };
+
+        if len2 == 1 {
+            assert!(len1 > 0);
+            dest -= len1;
+            c1 -= len1;
+            copy_vec(array, dest+1, array, c1+1, len1);
+            array[dest] <-> tmp[c2];
+        } else if len2 == 0 {
+            fail!(~"Comparison violates its contract!");
+        } else {
+            assert!(len1 == 0);
+            assert!(len2 != 0);
+            copy_vec(array, dest-(len2-1), tmp, 0, len2);
+        }
+    }
+
+    fn merge_collapse(&mut self, array: &mut [T]) {
+        while self.runs.len() > 1 {
+            let mut n = self.runs.len()-2;
+            if n > 0 &&
+                self.runs[n-1].len <= self.runs[n].len + self.runs[n+1].len
+            {
+                if self.runs[n-1].len < self.runs[n+1].len { n -= 1; }
+            } else if self.runs[n].len <= self.runs[n+1].len {
+                /* keep going */
+            } else {
+                break;
+            }
+            self.merge_at(n, array);
+        }
+    }
+
+    fn merge_force_collapse(&mut self, array: &mut [T]) {
+        while self.runs.len() > 1 {
+            let mut n = self.runs.len()-2;
+            if n > 0 {
+                if self.runs[n-1].len < self.runs[n+1].len {
+                    n -= 1;
+                }
+            }
+            self.merge_at(n, array);
+        }
+    }
+}
+
+#[inline(always)]
+fn copy_vec<T:Copy>(dest: &mut [T],
+                    s1: uint,
+                    from: &const [T],
+                    s2: uint,
+                    len: uint) {
+    assert!(s1+len <= dest.len() && s2+len <= from.len());
+
+    let mut slice = ~[];
+    for uint::range(s2, s2+len) |i| {
+        slice.push(from[i]);
+    }
+
+    for slice.eachi |i, v| {
+        dest[s1+i] = *v;
+    }
+}
+
+#[cfg(test)]
+mod test_qsort3 {
+    use sort::*;
+
+    use core::vec;
+
+    fn check_sort(v1: &mut [int], v2: &mut [int]) {
+        let len = vec::len::<int>(v1);
+        quick_sort3::<int>(v1);
+        let mut i = 0;
+        while i < len {
+            // debug!(v2[i]);
+            assert!((v2[i] == v1[i]));
+            i += 1;
+        }
+    }
+
+    #[test]
+    fn test() {
+        {
+            let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8];
+            let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9];
+            check_sort(v1, v2);
+        }
+        {
+            let mut v1 = ~[1, 1, 1];
+            let mut v2 = ~[1, 1, 1];
+            check_sort(v1, v2);
+        }
+        {
+            let mut v1: ~[int] = ~[];
+            let mut v2: ~[int] = ~[];
+            check_sort(v1, v2);
+        }
+        { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); }
+        {
+            let mut v1 = ~[9, 3, 3, 3, 9];
+            let mut v2 = ~[3, 3, 3, 9, 9];
+            check_sort(v1, v2);
+        }
+    }
+}
+
+#[cfg(test)]
+mod test_qsort {
+    use sort::*;
+
+    use core::int;
+    use core::vec;
+
+    fn check_sort(v1: &mut [int], v2: &mut [int]) {
+        let len = vec::len::<int>(v1);
+        fn leual(a: &int, b: &int) -> bool { *a <= *b }
+        quick_sort::<int>(v1, leual);
+        let mut i = 0u;
+        while i < len {
+            // debug!(v2[i]);
+            assert!((v2[i] == v1[i]));
+            i += 1;
+        }
+    }
+
+    #[test]
+    fn test() {
+        {
+            let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8];
+            let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9];
+            check_sort(v1, v2);
+        }
+        {
+            let mut v1 = ~[1, 1, 1];
+            let mut v2 = ~[1, 1, 1];
+            check_sort(v1, v2);
+        }
+        {
+            let mut v1: ~[int] = ~[];
+            let mut v2: ~[int] = ~[];
+            check_sort(v1, v2);
+        }
+        { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); }
+        {
+            let mut v1 = ~[9, 3, 3, 3, 9];
+            let mut v2 = ~[3, 3, 3, 9, 9];
+            check_sort(v1, v2);
+        }
+    }
+
+    // Regression test for #750
+    #[test]
+    fn test_simple() {
+        let mut names = ~[2, 1, 3];
+
+        let expected = ~[1, 2, 3];
+
+        do quick_sort(names) |x, y| { int::le(*x, *y) };
+
+        let immut_names = names;
+
+        let pairs = vec::zip_slice(expected, immut_names);
+        for vec::each(pairs) |p| {
+            let (a, b) = *p;
+            debug!("%d %d", a, b);
+            assert!((a == b));
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use sort::*;
+
+    use core::vec;
+
+    fn check_sort(v1: &[int], v2: &[int]) {
+        let len = vec::len::<int>(v1);
+        pub fn le(a: &int, b: &int) -> bool { *a <= *b }
+        let f = le;
+        let v3 = merge_sort::<int>(v1, f);
+        let mut i = 0u;
+        while i < len {
+            debug!(v3[i]);
+            assert!((v3[i] == v2[i]));
+            i += 1;
+        }
+    }
+
+    #[test]
+    fn test() {
+        {
+            let v1 = ~[3, 7, 4, 5, 2, 9, 5, 8];
+            let v2 = ~[2, 3, 4, 5, 5, 7, 8, 9];
+            check_sort(v1, v2);
+        }
+        { let v1 = ~[1, 1, 1]; let v2 = ~[1, 1, 1]; check_sort(v1, v2); }
+        { let v1:~[int] = ~[]; let v2:~[int] = ~[]; check_sort(v1, v2); }
+        { let v1 = ~[9]; let v2 = ~[9]; check_sort(v1, v2); }
+        {
+            let v1 = ~[9, 3, 3, 3, 9];
+            let v2 = ~[3, 3, 3, 9, 9];
+            check_sort(v1, v2);
+        }
+    }
+
+    #[test]
+    fn test_merge_sort_mutable() {
+        pub fn le(a: &int, b: &int) -> bool { *a <= *b }
+        let mut v1 = ~[3, 2, 1];
+        let v2 = merge_sort(v1, le);
+        assert!(v2 == ~[1, 2, 3]);
+    }
+
+    #[test]
+    fn test_merge_sort_stability() {
+        // tjc: funny that we have to use parens
+        fn ile(x: &(&'static str), y: &(&'static str)) -> bool
+        {
+            // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
+            // to_ascii_consume and to_str_consume to not do a unnecessary copy.
+            // (Actually, could just remove the to_str_* call, but needs an deriving(Ord) on
+            // Ascii)
+            let x = x.to_ascii().to_lower().to_str_ascii();
+            let y = y.to_ascii().to_lower().to_str_ascii();
+            x <= y
+        }
+
+        let names1 = ~["joe bob", "Joe Bob", "Jack Brown", "JOE Bob",
+                       "Sally Mae", "JOE BOB", "Alex Andy"];
+        let names2 = ~["Alex Andy", "Jack Brown", "joe bob", "Joe Bob",
+                       "JOE Bob", "JOE BOB", "Sally Mae"];
+        let names3 = merge_sort(names1, ile);
+        assert!(names3 == names2);
+    }
+}
+
+#[cfg(test)]
+mod test_tim_sort {
+    use sort::tim_sort;
+    use core::rand::RngUtil;
+
+    struct CVal {
+        val: float,
+    }
+
+    impl Ord for CVal {
+        fn lt(&self, other: &CVal) -> bool {
+            let rng = rand::rng();
+            if rng.gen::<float>() > 0.995 { fail!(~"It's happening!!!"); }
+            (*self).val < other.val
+        }
+        fn le(&self, other: &CVal) -> bool { (*self).val <= other.val }
+        fn gt(&self, other: &CVal) -> bool { (*self).val > other.val }
+        fn ge(&self, other: &CVal) -> bool { (*self).val >= other.val }
+    }
+
+    fn check_sort(v1: &mut [int], v2: &mut [int]) {
+        let len = vec::len::<int>(v1);
+        tim_sort::<int>(v1);
+        let mut i = 0u;
+        while i < len {
+            // debug!(v2[i]);
+            assert!((v2[i] == v1[i]));
+            i += 1u;
+        }
+    }
+
+    #[test]
+    fn test() {
+        {
+            let mut v1 = ~[3, 7, 4, 5, 2, 9, 5, 8];
+            let mut v2 = ~[2, 3, 4, 5, 5, 7, 8, 9];
+            check_sort(v1, v2);
+        }
+        {
+            let mut v1 = ~[1, 1, 1];
+            let mut v2 = ~[1, 1, 1];
+            check_sort(v1, v2);
+        }
+        {
+            let mut v1: ~[int] = ~[];
+            let mut v2: ~[int] = ~[];
+            check_sort(v1, v2);
+        }
+        { let mut v1 = ~[9]; let mut v2 = ~[9]; check_sort(v1, v2); }
+        {
+            let mut v1 = ~[9, 3, 3, 3, 9];
+            let mut v2 = ~[3, 3, 3, 9, 9];
+            check_sort(v1, v2);
+        }
+    }
+
+    #[test]
+    #[should_fail]
+    #[cfg(unix)]
+    fn crash_test() {
+        let rng = rand::rng();
+        let mut arr = do vec::from_fn(1000) |_i| {
+            CVal { val: rng.gen() }
+        };
+
+        tim_sort(arr);
+        fail!(~"Guarantee the fail");
+    }
+
+    struct DVal { val: uint }
+
+    impl Ord for DVal {
+        fn lt(&self, _x: &DVal) -> bool { true }
+        fn le(&self, _x: &DVal) -> bool { true }
+        fn gt(&self, _x: &DVal) -> bool { true }
+        fn ge(&self, _x: &DVal) -> bool { true }
+    }
+
+    #[test]
+    fn test_bad_Ord_impl() {
+        let rng = rand::rng();
+        let mut arr = do vec::from_fn(500) |_i| {
+            DVal { val: rng.gen() }
+        };
+
+        tim_sort(arr);
+    }
+}
+
+#[cfg(test)]
+mod big_tests {
+    use sort::*;
+    use core::rand::RngUtil;
+
+    #[test]
+    fn test_unique() {
+        let low = 5;
+        let high = 10;
+        tabulate_unique(low, high);
+    }
+
+    #[test]
+    fn test_managed() {
+        let low = 5;
+        let high = 10;
+        tabulate_managed(low, high);
+    }
+
+    fn multiplyVec<T:Copy>(arr: &const [T], num: uint) -> ~[T] {
+        let size = arr.len();
+        let res = do vec::from_fn(num) |i| {
+            arr[i % size]
+        };
+        res
+    }
+
+    fn makeRange(n: uint) -> ~[uint] {
+        let one = do vec::from_fn(n) |i| { i };
+        let mut two = copy one;
+        vec::reverse(two);
+        vec::append(two, one)
+    }
+
+    fn tabulate_unique(lo: uint, hi: uint) {
+        fn isSorted<T:Ord>(arr: &const [T]) {
+            for uint::range(0, arr.len()-1) |i| {
+                if arr[i] > arr[i+1] {
+                    fail!(~"Array not sorted");
+                }
+            }
+        }
+
+        let rng = rand::rng();
+
+        for uint::range(lo, hi) |i| {
+            let n = 1 << i;
+            let mut arr: ~[float] = do vec::from_fn(n) |_i| {
+                rng.gen()
+            };
+
+            tim_sort(arr); // *sort
+            isSorted(arr);
+
+            vec::reverse(arr);
+            tim_sort(arr); // \sort
+            isSorted(arr);
+
+            tim_sort(arr); // /sort
+            isSorted(arr);
+
+            for 3.times {
+                let i1 = rng.gen_uint_range(0, n);
+                let i2 = rng.gen_uint_range(0, n);
+                arr[i1] <-> arr[i2];
+            }
+            tim_sort(arr); // 3sort
+            isSorted(arr);
+
+            if n >= 10 {
+                let size = arr.len();
+                let mut idx = 1;
+                while idx <= 10 {
+                    arr[size-idx] = rng.gen();
+                    idx += 1;
+                }
+            }
+            tim_sort(arr); // +sort
+            isSorted(arr);
+
+            for (n/100).times {
+                let idx = rng.gen_uint_range(0, n);
+                arr[idx] = rng.gen();
+            }
+            tim_sort(arr);
+            isSorted(arr);
+
+            let mut arr = if n > 4 {
+                let part = vec::slice(arr, 0, 4);
+                multiplyVec(part, n)
+            } else { arr };
+            tim_sort(arr); // ~sort
+            isSorted(arr);
+
+            let mut arr = vec::from_elem(n, -0.5);
+            tim_sort(arr); // =sort
+            isSorted(arr);
+
+            let half = n / 2;
+            let mut arr = makeRange(half).map(|i| *i as float);
+            tim_sort(arr); // !sort
+            isSorted(arr);
+        }
+    }
+
+    fn tabulate_managed(lo: uint, hi: uint) {
+        fn isSorted<T:Ord>(arr: &const [@T]) {
+            for uint::range(0, arr.len()-1) |i| {
+                if arr[i] > arr[i+1] {
+                    fail!(~"Array not sorted");
+                }
+            }
+        }
+
+        let rng = rand::rng();
+
+        for uint::range(lo, hi) |i| {
+            let n = 1 << i;
+            let arr: ~[@float] = do vec::from_fn(n) |_i| {
+                @rng.gen()
+            };
+            let mut arr = arr;
+
+            tim_sort(arr); // *sort
+            isSorted(arr);
+
+            vec::reverse(arr);
+            tim_sort(arr); // \sort
+            isSorted(arr);
+
+            tim_sort(arr); // /sort
+            isSorted(arr);
+
+            for 3.times {
+                let i1 = rng.gen_uint_range(0, n);
+                let i2 = rng.gen_uint_range(0, n);
+                arr[i1] <-> arr[i2];
+            }
+            tim_sort(arr); // 3sort
+            isSorted(arr);
+
+            if n >= 10 {
+                let size = arr.len();
+                let mut idx = 1;
+                while idx <= 10 {
+                    arr[size-idx] = @rng.gen();
+                    idx += 1;
+                }
+            }
+            tim_sort(arr); // +sort
+            isSorted(arr);
+
+            for (n/100).times {
+                let idx = rng.gen_uint_range(0, n);
+                arr[idx] = @rng.gen();
+            }
+            tim_sort(arr);
+            isSorted(arr);
+
+            let mut arr = if n > 4 {
+                let part = vec::slice(arr, 0, 4);
+                multiplyVec(part, n)
+            } else { arr };
+            tim_sort(arr); // ~sort
+            isSorted(arr);
+
+            let mut arr = vec::from_elem(n, @(-0.5));
+            tim_sort(arr); // =sort
+            isSorted(arr);
+
+            let half = n / 2;
+            let mut arr = makeRange(half).map(|i| @(*i as float));
+            tim_sort(arr); // !sort
+            isSorted(arr);
+        }
+    }
+
+    struct LVal<'self> {
+        val: uint,
+        key: &'self fn(@uint),
+    }
+
+    #[unsafe_destructor]
+    impl<'self> Drop for LVal<'self> {
+        fn finalize(&self) {
+            let x = unsafe { task::local_data::local_data_get(self.key) };
+            match x {
+                Some(@y) => {
+                    unsafe {
+                        task::local_data::local_data_set(self.key, @(y+1));
+                    }
+                }
+                _ => fail!(~"Expected key to work"),
+            }
+        }
+    }
+
+    impl<'self> Ord for LVal<'self> {
+        fn lt<'a>(&self, other: &'a LVal<'self>) -> bool {
+            (*self).val < other.val
+        }
+        fn le<'a>(&self, other: &'a LVal<'self>) -> bool {
+            (*self).val <= other.val
+        }
+        fn gt<'a>(&self, other: &'a LVal<'self>) -> bool {
+            (*self).val > other.val
+        }
+        fn ge<'a>(&self, other: &'a LVal<'self>) -> bool {
+            (*self).val >= other.val
+        }
+    }
+}
+
+// Local Variables:
+// mode: rust;
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// End:
diff --git a/src/libstd/std.rc b/src/libstd/std.rc
index 32c7c82a3127e..d96dae76c4290 100644
--- a/src/libstd/std.rc
+++ b/src/libstd/std.rc
@@ -69,7 +69,14 @@ pub mod list;
 pub mod priority_queue;
 pub mod rope;
 pub mod smallintmap;
+
+#[cfg(stage0)]
+#[path="sort_stage0.rs"]
+pub mod sort;
+
+#[cfg(not(stage0))]
 pub mod sort;
+
 pub mod dlist;
 pub mod treemap;
 
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
index 69397276c4f54..77a02adbafba7 100644
--- a/src/libsyntax/ast_map.rs
+++ b/src/libsyntax/ast_map.rs
@@ -18,6 +18,7 @@ use diagnostic::span_handler;
 use parse::token::ident_interner;
 use print::pprust;
 use visit;
+use syntax::parse::token::special_idents;
 
 use core::hashmap::HashMap;
 
@@ -88,12 +89,11 @@ pub enum ast_node {
     node_variant(variant, @item, @path),
     node_expr(@expr),
     node_stmt(@stmt),
-    // Locals are numbered, because the alias analysis needs to know in which
-    // order they are introduced.
-    node_arg(arg, uint),
-    node_local(uint),
+    node_arg,
+    node_local(ident),
     node_block(blk),
     node_struct_ctor(@struct_def, @item, @path),
+    node_callee_scope(@expr)
 }
 
 pub type map = @mut HashMap<node_id, ast_node>;
@@ -101,7 +101,6 @@ pub type map = @mut HashMap<node_id, ast_node>;
 pub struct Ctx {
     map: map,
     path: path,
-    local_id: uint,
     diag: @span_handler,
 }
 
@@ -117,9 +116,8 @@ pub fn mk_ast_map_visitor() -> vt {
         visit_expr: map_expr,
         visit_stmt: map_stmt,
         visit_fn: map_fn,
-        visit_local: map_local,
-        visit_arm: map_arm,
         visit_block: map_block,
+        visit_pat: map_pat,
         .. *visit::default_visitor()
     });
 }
@@ -128,7 +126,6 @@ pub fn map_crate(diag: @span_handler, c: @crate) -> map {
     let cx = @mut Ctx {
         map: @mut HashMap::new(),
         path: ~[],
-        local_id: 0u,
         diag: diag,
     };
     visit::visit_crate(c, cx, mk_ast_map_visitor());
@@ -151,7 +148,6 @@ pub fn map_decoded_item(diag: @span_handler,
     let cx = @mut Ctx {
         map: map,
         path: copy path,
-        local_id: 0,
         diag: diag,
     };
     let v = mk_ast_map_visitor();
@@ -186,9 +182,7 @@ pub fn map_fn(
     v: visit::vt<@mut Ctx>
 ) {
     for decl.inputs.each |a| {
-        cx.map.insert(a.id,
-                      node_arg(/* FIXME (#2543) */ copy *a, cx.local_id));
-        cx.local_id += 1u;
+        cx.map.insert(a.id, node_arg);
     }
     visit::visit_fn(fk, decl, body, sp, id, cx, v);
 }
@@ -198,33 +192,22 @@ pub fn map_block(b: &blk, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
     visit::visit_block(b, cx, v);
 }
 
-pub fn number_pat(cx: @mut Ctx, pat: @pat) {
-    do ast_util::walk_pat(pat) |p| {
-        match p.node {
-          pat_ident(*) => {
-            cx.map.insert(p.id, node_local(cx.local_id));
-            cx.local_id += 1u;
-          }
-          _ => ()
+pub fn map_pat(pat: @pat, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
+    match pat.node {
+        pat_ident(_, path, _) => {
+            // Note: this is at least *potentially* a pattern...
+            cx.map.insert(pat.id, node_local(ast_util::path_to_ident(path)));
         }
-    };
-}
-
-pub fn map_local(loc: @local, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
-    number_pat(cx, loc.node.pat);
-    visit::visit_local(loc, cx, v);
-}
+        _ => ()
+    }
 
-pub fn map_arm(arm: &arm, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
-    number_pat(cx, arm.pats[0]);
-    visit::visit_arm(arm, cx, v);
+    visit::visit_pat(pat, cx, v);
 }
 
 pub fn map_method(impl_did: def_id, impl_path: @path,
                   m: @method, cx: @mut Ctx) {
     cx.map.insert(m.id, node_method(m, impl_did, impl_path));
-    cx.map.insert(m.self_id, node_local(cx.local_id));
-    cx.local_id += 1u;
+    cx.map.insert(m.self_id, node_local(special_idents::self_));
 }
 
 pub fn map_item(i: @item, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
@@ -293,6 +276,7 @@ pub fn map_item(i: @item, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
         }
         _ => ()
     }
+
     match i.node {
         item_mod(_) | item_foreign_mod(_) => {
             cx.path.push(path_mod(i.ident));
@@ -328,6 +312,18 @@ pub fn map_struct_def(
 
 pub fn map_expr(ex: @expr, cx: @mut Ctx, v: visit::vt<@mut Ctx>) {
     cx.map.insert(ex.id, node_expr(ex));
+    match ex.node {
+        // Expressions which are or might be calls:
+        ast::expr_call(*) |
+        ast::expr_method_call(*) |
+        ast::expr_index(*) |
+        ast::expr_binary(*) |
+        ast::expr_assign_op(*) |
+        ast::expr_unary(*) => {
+            cx.map.insert(ex.callee_id, node_callee_scope(ex));
+        }
+        _ => {}
+    }
     visit::visit_expr(ex, cx, v);
 }
 
@@ -377,15 +373,18 @@ pub fn node_id_to_str(map: map, id: node_id, itr: @ident_interner) -> ~str {
       Some(&node_expr(expr)) => {
         fmt!("expr %s (id=%?)", pprust::expr_to_str(expr, itr), id)
       }
+      Some(&node_callee_scope(expr)) => {
+        fmt!("callee_scope %s (id=%?)", pprust::expr_to_str(expr, itr), id)
+      }
       Some(&node_stmt(stmt)) => {
         fmt!("stmt %s (id=%?)",
              pprust::stmt_to_str(stmt, itr), id)
       }
-      Some(&node_arg(_, _)) => { // add more info here
+      Some(&node_arg) => {
         fmt!("arg (id=%?)", id)
       }
-      Some(&node_local(_)) => { // add more info here
-        fmt!("local (id=%?)", id)
+      Some(&node_local(ident)) => {
+        fmt!("local (id=%?, name=%s)", id, *itr.get(ident))
       }
       Some(&node_block(_)) => {
         fmt!("block")
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 10350413f2d68..a6094903d7b79 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -368,8 +368,20 @@ pub struct id_range {
     max: node_id,
 }
 
-pub fn empty(range: id_range) -> bool {
-    range.min >= range.max
+pub impl id_range {
+    fn max() -> id_range {
+        id_range {min: int::max_value,
+                  max: int::min_value}
+    }
+
+    fn empty(&self) -> bool {
+        self.min >= self.max
+    }
+
+    fn add(&mut self, id: node_id) {
+        self.min = int::min(self.min, id);
+        self.max = int::max(self.max, id + 1);
+    }
 }
 
 pub fn id_visitor(vfn: @fn(node_id)) -> visit::vt<()> {
@@ -467,13 +479,11 @@ pub fn visit_ids_for_inlined_item(item: &inlined_item, vfn: @fn(node_id)) {
 }
 
 pub fn compute_id_range(visit_ids_fn: &fn(@fn(node_id))) -> id_range {
-    let min = @mut int::max_value;
-    let max = @mut int::min_value;
+    let result = @mut id_range::max();
     do visit_ids_fn |id| {
-        *min = int::min(*min, id);
-        *max = int::max(*max, id + 1);
+        result.add(id);
     }
-    id_range { min: *min, max: *max }
+    *result
 }
 
 pub fn compute_id_range_for_inlined_item(item: &inlined_item) -> id_range {
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 66ed52f03528e..846097550d14f 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -248,7 +248,7 @@ pub impl FileMap {
         // the new charpos must be > the last one (or it's the first one).
         let lines = &mut *self.lines;
         assert!((lines.len() == 0) || (lines[lines.len() - 1] < pos));
-        self.lines.push(pos);
+        lines.push(pos);
     }
 
     // get a line from the list of pre-computed line-beginnings
@@ -310,7 +310,7 @@ pub impl CodeMap {
             multibyte_chars: @mut ~[],
         };
 
-        self.files.push(filemap);
+        files.push(filemap);
 
         return filemap;
     }
@@ -357,7 +357,7 @@ pub impl CodeMap {
     }
 
     pub fn span_to_str(&self, sp: span) -> ~str {
-        let files = &mut *self.files;
+        let files = &*self.files;
         if files.len() == 0 && sp == dummy_sp() {
             return ~"no-location";
         }
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index 0f2374a892b4a..b313a2fc6fcc9 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/diagnostic.rs
@@ -24,6 +24,7 @@ pub trait handler {
     fn fatal(@mut self, msg: &str) -> !;
     fn err(@mut self, msg: &str);
     fn bump_err_count(@mut self);
+    fn err_count(@mut self) -> uint;
     fn has_errors(@mut self) -> bool;
     fn abort_if_errors(@mut self);
     fn warn(@mut self, msg: &str);
@@ -98,7 +99,12 @@ impl handler for HandlerT {
     fn bump_err_count(@mut self) {
         self.err_count += 1u;
     }
-    fn has_errors(@mut self) -> bool { self.err_count > 0u }
+    fn err_count(@mut self) -> uint {
+        self.err_count
+    }
+    fn has_errors(@mut self) -> bool {
+        self.err_count > 0u
+    }
     fn abort_if_errors(@mut self) {
         let s;
         match self.err_count {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index e56dab6db207e..da8f87d389194 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -210,29 +210,29 @@ pub fn syntax_expander_table() -> SyntaxEnv {
 // when a macro expansion occurs, the resulting nodes have the backtrace()
 // -> expn_info of their expansion context stored into their span.
 pub trait ext_ctxt {
-    fn codemap(@mut self) -> @CodeMap;
-    fn parse_sess(@mut self) -> @mut parse::ParseSess;
-    fn cfg(@mut self) -> ast::crate_cfg;
-    fn call_site(@mut self) -> span;
-    fn print_backtrace(@mut self);
-    fn backtrace(@mut self) -> Option<@ExpnInfo>;
-    fn mod_push(@mut self, mod_name: ast::ident);
-    fn mod_pop(@mut self);
-    fn mod_path(@mut self) -> ~[ast::ident];
-    fn bt_push(@mut self, ei: codemap::ExpnInfo);
-    fn bt_pop(@mut self);
-    fn span_fatal(@mut self, sp: span, msg: &str) -> !;
-    fn span_err(@mut self, sp: span, msg: &str);
-    fn span_warn(@mut self, sp: span, msg: &str);
-    fn span_unimpl(@mut self, sp: span, msg: &str) -> !;
-    fn span_bug(@mut self, sp: span, msg: &str) -> !;
-    fn bug(@mut self, msg: &str) -> !;
-    fn next_id(@mut self) -> ast::node_id;
-    fn trace_macros(@mut self) -> bool;
-    fn set_trace_macros(@mut self, x: bool);
+    fn codemap(&self) -> @CodeMap;
+    fn parse_sess(&self) -> @mut parse::ParseSess;
+    fn cfg(&self) -> ast::crate_cfg;
+    fn call_site(&self) -> span;
+    fn print_backtrace(&self);
+    fn backtrace(&self) -> Option<@ExpnInfo>;
+    fn mod_push(&self, mod_name: ast::ident);
+    fn mod_pop(&self);
+    fn mod_path(&self) -> ~[ast::ident];
+    fn bt_push(&self, ei: codemap::ExpnInfo);
+    fn bt_pop(&self);
+    fn span_fatal(&self, sp: span, msg: &str) -> !;
+    fn span_err(&self, sp: span, msg: &str);
+    fn span_warn(&self, sp: span, msg: &str);
+    fn span_unimpl(&self, sp: span, msg: &str) -> !;
+    fn span_bug(&self, sp: span, msg: &str) -> !;
+    fn bug(&self, msg: &str) -> !;
+    fn next_id(&self) -> ast::node_id;
+    fn trace_macros(&self) -> bool;
+    fn set_trace_macros(&self, x: bool);
     /* for unhygienic identifier transformation */
-    fn str_of(@mut self, id: ast::ident) -> ~str;
-    fn ident_of(@mut self, st: ~str) -> ast::ident;
+    fn str_of(&self, id: ast::ident) -> ~str;
+    fn ident_of(&self, st: ~str) -> ast::ident;
 }
 
 pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
@@ -241,25 +241,31 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
         parse_sess: @mut parse::ParseSess,
         cfg: ast::crate_cfg,
         backtrace: @mut Option<@ExpnInfo>,
-        mod_path: ~[ast::ident],
-        trace_mac: bool
+
+        // These two @mut's should really not be here,
+        // but the self types for CtxtRepr are all wrong
+        // and there are bugs in the code for object
+        // types that make this hard to get right at the
+        // moment. - nmatsakis
+        mod_path: @mut ~[ast::ident],
+        trace_mac: @mut bool
     }
     impl ext_ctxt for CtxtRepr {
-        fn codemap(@mut self) -> @CodeMap { self.parse_sess.cm }
-        fn parse_sess(@mut self) -> @mut parse::ParseSess { self.parse_sess }
-        fn cfg(@mut self) -> ast::crate_cfg { copy self.cfg }
-        fn call_site(@mut self) -> span {
+        fn codemap(&self) -> @CodeMap { self.parse_sess.cm }
+        fn parse_sess(&self) -> @mut parse::ParseSess { self.parse_sess }
+        fn cfg(&self) -> ast::crate_cfg { copy self.cfg }
+        fn call_site(&self) -> span {
             match *self.backtrace {
                 Some(@ExpandedFrom(CallInfo {call_site: cs, _})) => cs,
                 None => self.bug(~"missing top span")
             }
         }
-        fn print_backtrace(@mut self) { }
-        fn backtrace(@mut self) -> Option<@ExpnInfo> { *self.backtrace }
-        fn mod_push(@mut self, i: ast::ident) { self.mod_path.push(i); }
-        fn mod_pop(@mut self) { self.mod_path.pop(); }
-        fn mod_path(@mut self) -> ~[ast::ident] { copy self.mod_path }
-        fn bt_push(@mut self, ei: codemap::ExpnInfo) {
+        fn print_backtrace(&self) { }
+        fn backtrace(&self) -> Option<@ExpnInfo> { *self.backtrace }
+        fn mod_push(&self, i: ast::ident) { self.mod_path.push(i); }
+        fn mod_pop(&self) { self.mod_path.pop(); }
+        fn mod_path(&self) -> ~[ast::ident] { copy *self.mod_path }
+        fn bt_push(&self, ei: codemap::ExpnInfo) {
             match ei {
               ExpandedFrom(CallInfo {call_site: cs, callee: ref callee}) => {
                 *self.backtrace =
@@ -270,7 +276,7 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
               }
             }
         }
-        fn bt_pop(@mut self) {
+        fn bt_pop(&self) {
             match *self.backtrace {
               Some(@ExpandedFrom(CallInfo {
                   call_site: span {expn_info: prev, _}, _
@@ -280,52 +286,52 @@ pub fn mk_ctxt(parse_sess: @mut parse::ParseSess, cfg: ast::crate_cfg)
               _ => self.bug(~"tried to pop without a push")
             }
         }
-        fn span_fatal(@mut self, sp: span, msg: &str) -> ! {
+        fn span_fatal(&self, sp: span, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_fatal(sp, msg);
         }
-        fn span_err(@mut self, sp: span, msg: &str) {
+        fn span_err(&self, sp: span, msg: &str) {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_err(sp, msg);
         }
-        fn span_warn(@mut self, sp: span, msg: &str) {
+        fn span_warn(&self, sp: span, msg: &str) {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_warn(sp, msg);
         }
-        fn span_unimpl(@mut self, sp: span, msg: &str) -> ! {
+        fn span_unimpl(&self, sp: span, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
         }
-        fn span_bug(@mut self, sp: span, msg: &str) -> ! {
+        fn span_bug(&self, sp: span, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.span_bug(sp, msg);
         }
-        fn bug(@mut self, msg: &str) -> ! {
+        fn bug(&self, msg: &str) -> ! {
             self.print_backtrace();
             self.parse_sess.span_diagnostic.handler().bug(msg);
         }
-        fn next_id(@mut self) -> ast::node_id {
+        fn next_id(&self) -> ast::node_id {
             return parse::next_node_id(self.parse_sess);
         }
-        fn trace_macros(@mut self) -> bool {
-            self.trace_mac
+        fn trace_macros(&self) -> bool {
+            *self.trace_mac
         }
-        fn set_trace_macros(@mut self, x: bool) {
-            self.trace_mac = x
+        fn set_trace_macros(&self, x: bool) {
+            *self.trace_mac = x
         }
-        fn str_of(@mut self, id: ast::ident) -> ~str {
+        fn str_of(&self, id: ast::ident) -> ~str {
             copy *self.parse_sess.interner.get(id)
         }
-        fn ident_of(@mut self, st: ~str) -> ast::ident {
+        fn ident_of(&self, st: ~str) -> ast::ident {
             self.parse_sess.interner.intern(@/*bad*/ copy st)
         }
     }
-    let imp: @mut CtxtRepr = @mut CtxtRepr {
+    let imp: @CtxtRepr = @CtxtRepr {
         parse_sess: parse_sess,
         cfg: cfg,
         backtrace: @mut None,
-        mod_path: ~[],
-        trace_mac: false
+        mod_path: @mut ~[],
+        trace_mac: @mut false
     };
     ((imp) as @ext_ctxt)
 }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 9363807ab9b9e..0f2ad4cd54a3d 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -27,6 +27,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv,
                    fld: @ast_fold,
                    orig: @fn(&expr_, span, @ast_fold) -> (expr_, span))
                 -> (expr_, span) {
+    let mut cx = cx;
     match *e {
         // expr_mac should really be expr_ext or something; it's the
         // entry-point for all syntax extensions.
@@ -112,6 +113,8 @@ pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
                         fld: @ast_fold,
                         orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod)
                      -> ast::_mod {
+    let mut cx = cx;
+
     // Fold the contents first:
     let module_ = orig(module_, fld);
 
diff --git a/src/libsyntax/ext/pipes/liveness.rs b/src/libsyntax/ext/pipes/liveness.rs
index 18faab8c88d70..8799bd064f658 100644
--- a/src/libsyntax/ext/pipes/liveness.rs
+++ b/src/libsyntax/ext/pipes/liveness.rs
@@ -38,11 +38,11 @@ updating the states using rule (2) until there are no changes.
 */
 
 use ext::base::ext_ctxt;
-use ext::pipes::proto::protocol;
+use ext::pipes::proto::{protocol_};
 
 use std::bitv::Bitv;
 
-pub fn analyze(proto: protocol, _cx: @ext_ctxt) {
+pub fn analyze(proto: @mut protocol_, _cx: @ext_ctxt) {
     debug!("initializing colive analysis");
     let num_states = proto.num_states();
     let mut colive = do (copy proto.states).map_to_vec |state| {
diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs
index 64e2f1041c1e1..647c7741bd897 100644
--- a/src/libsyntax/ext/pipes/proto.rs
+++ b/src/libsyntax/ext/pipes/proto.rs
@@ -138,26 +138,26 @@ pub struct protocol_ {
 
 pub impl protocol_ {
     /// Get a state.
-    fn get_state(&mut self, name: ~str) -> state {
+    fn get_state(&self, name: ~str) -> state {
         self.states.find(|i| i.name == name).get()
     }
 
-    fn get_state_by_id(&mut self, id: uint) -> state { self.states[id] }
+    fn get_state_by_id(&self, id: uint) -> state { self.states[id] }
 
-    fn has_state(&mut self, name: ~str) -> bool {
+    fn has_state(&self, name: ~str) -> bool {
         self.states.find(|i| i.name == name).is_some()
     }
 
-    fn filename(&mut self) -> ~str {
+    fn filename(&self) -> ~str {
         ~"proto://" + self.name
     }
 
-    fn num_states(&mut self) -> uint {
+    fn num_states(&self) -> uint {
         let states = &mut *self.states;
         states.len()
     }
 
-    fn has_ty_params(&mut self) -> bool {
+    fn has_ty_params(&self) -> bool {
         for self.states.each |s| {
             if s.generics.ty_params.len() > 0 {
                 return true;
@@ -165,7 +165,7 @@ pub impl protocol_ {
         }
         false
     }
-    fn is_bounded(&mut self) -> bool {
+    fn is_bounded(&self) -> bool {
         let bounded = self.bounded.get();
         bounded
     }
@@ -179,7 +179,7 @@ pub impl protocol_ {
                       generics: ast::Generics)
                    -> state {
         let messages = @mut ~[];
-        let states = &*self.states;
+        let states = &mut *self.states;
 
         let state = @state_ {
             id: states.len(),
@@ -192,7 +192,7 @@ pub impl protocol_ {
             proto: self
         };
 
-        self.states.push(state);
+        states.push(state);
         state
     }
 }
diff --git a/src/libsyntax/parse/lexer.rs b/src/libsyntax/parse/lexer.rs
index 16a7ef71317ef..8956622a06b58 100644
--- a/src/libsyntax/parse/lexer.rs
+++ b/src/libsyntax/parse/lexer.rs
@@ -163,7 +163,7 @@ fn string_advance_token(r: @mut StringReader) {
     }
 }
 
-fn byte_offset(rdr: @mut StringReader) -> BytePos {
+fn byte_offset(rdr: &StringReader) -> BytePos {
     (rdr.pos - rdr.filemap.start_pos)
 }
 
@@ -176,7 +176,7 @@ pub fn get_str_from(rdr: @mut StringReader, start: BytePos) -> ~str {
 
 // EFFECT: advance the StringReader by one character. If a newline is
 // discovered, add it to the FileMap's list of line start offsets.
-pub fn bump(rdr: @mut StringReader) {
+pub fn bump(rdr: &mut StringReader) {
     rdr.last_pos = rdr.pos;
     let current_byte_offset = byte_offset(rdr).to_uint();;
     if current_byte_offset < (*rdr.src).len() {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 8d52b8eea5ea2..6b8411a9ead15 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1602,9 +1602,9 @@ pub impl Parser {
             token::LBRACE | token::LPAREN | token::LBRACKET => {
                 self.parse_matcher_subseq(
                     name_idx,
-                    &*self.token,
+                    *self.token,
                     // tjc: not sure why we need a copy
-                    &token::flip_delimiter(&*self.token)
+                    token::flip_delimiter(&*self.token)
                 )
             }
             _ => self.fatal(~"expected open delimiter")
@@ -1618,15 +1618,15 @@ pub impl Parser {
     fn parse_matcher_subseq(
         &self,
         name_idx: @mut uint,
-        bra: &token::Token,
-        ket: &token::Token
+        bra: token::Token,
+        ket: token::Token
     ) -> ~[matcher] {
         let mut ret_val = ~[];
         let mut lparens = 0u;
 
-        self.expect(bra);
+        self.expect(&bra);
 
-        while *self.token != *ket || lparens > 0u {
+        while *self.token != ket || lparens > 0u {
             if *self.token == token::LPAREN { lparens += 1u; }
             if *self.token == token::RPAREN { lparens -= 1u; }
             ret_val.push(self.parse_matcher(name_idx));
@@ -1646,8 +1646,8 @@ pub impl Parser {
                 let name_idx_lo = *name_idx;
                 let ms = self.parse_matcher_subseq(
                     name_idx,
-                    &token::LPAREN,
-                    &token::RPAREN
+                    token::LPAREN,
+                    token::RPAREN
                 );
                 if ms.len() == 0u {
                     self.fatal(~"repetition body must be nonempty");
@@ -3688,12 +3688,11 @@ pub impl Parser {
                                first_item_attrs: ~[attribute])
                             -> foreign_mod {
         let ParsedItemsAndViewItems {
-            attrs_remaining: attrs_remaining,
+            attrs_remaining: _,
             view_items: view_items,
             items: _,
             foreign_items: foreign_items
         } = self.parse_foreign_items(first_item_attrs, true);
-        let _initial_attrs = attrs_remaining;
         assert!(*self.token == token::RBRACE);
         ast::foreign_mod {
             sort: sort,
diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs
index 3fa615939b77e..43f62d72a9fad 100644
--- a/src/libsyntax/print/pp.rs
+++ b/src/libsyntax/print/pp.rs
@@ -491,9 +491,9 @@ pub impl Printer {
           }
           END => {
             debug!("print END -> pop END");
-            let print_stack = &*self.print_stack;
+            let print_stack = &mut *self.print_stack;
             assert!((print_stack.len() != 0u));
-            self.print_stack.pop();
+            print_stack.pop();
           }
           BREAK(b) => {
             let top = self.get_top();
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 76de109eebd30..6f3d6604d5b98 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -72,6 +72,12 @@ pub fn end(s: @ps) {
 }
 
 pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps {
+    return rust_printer_annotated(writer, intr, no_ann());
+}
+
+pub fn rust_printer_annotated(writer: @io::Writer,
+                              intr: @ident_interner,
+                              ann: pp_ann) -> @ps {
     return @ps {
         s: pp::mk_printer(writer, default_columns),
         cm: None::<@CodeMap>,
@@ -83,7 +89,7 @@ pub fn rust_printer(writer: @io::Writer, intr: @ident_interner) -> @ps {
             cur_lit: 0
         },
         boxes: @mut ~[],
-        ann: no_ann()
+        ann: ann
     };
 }
 
diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs
index 9ab7d4bc443ef..e3a8727762218 100644
--- a/src/libsyntax/util/interner.rs
+++ b/src/libsyntax/util/interner.rs
@@ -44,10 +44,10 @@ pub impl<T:Eq + IterBytes + Hash + Const + Copy> Interner<T> {
             None => (),
         }
 
-        let vect = &*self.vect;
+        let vect = &mut *self.vect;
         let new_idx = vect.len();
         self.map.insert(val, new_idx);
-        self.vect.push(val);
+        vect.push(val);
         new_idx
     }
 
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 9a0c97d76162b..90dd49d684843 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -21,6 +21,12 @@ use opt_vec::OptVec;
 // children (potentially passing in different contexts to each), call
 // visit::visit_* to apply the default traversal algorithm (again, it can
 // override the context), or prevent deeper traversal by doing nothing.
+//
+// Note: it is an important invariant that the default visitor walks the body
+// of a function in "execution order" (more concretely, reverse post-order
+// with respect to the CFG implied by the AST), meaning that if AST node A may
+// execute before AST node B, then A is visited first.  The borrow checker in
+// particular relies on this property.
 
 // Our typesystem doesn't do circular types, so the visitor record can not
 // hold functions that take visitors. A vt enum is used to break the cycle.
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index 8b7b89680fcca..a491379153e1a 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -683,6 +683,20 @@ rust_task_local_data_atexit(rust_task *task, void (*cleanup_fn)(void *data)) {
     task->task_local_data_cleanup = cleanup_fn;
 }
 
+// set/get/atexit task_borrow_list can run on the rust stack for speed.
+extern "C" void *
+rust_take_task_borrow_list(rust_task *task) {
+    void *r = task->borrow_list;
+    task->borrow_list = NULL;
+    return r;
+}
+extern "C" void
+rust_set_task_borrow_list(rust_task *task, void *data) {
+    assert(task->borrow_list == NULL);
+    assert(data != NULL);
+    task->borrow_list = data;
+}
+
 extern "C" void
 task_clear_event_reject(rust_task *task) {
     task->clear_event_reject();
diff --git a/src/rt/rust_env.cpp b/src/rt/rust_env.cpp
index 041b4efac52a2..360d611492853 100644
--- a/src/rt/rust_env.cpp
+++ b/src/rt/rust_env.cpp
@@ -24,6 +24,7 @@
 #define RUST_SEED "RUST_SEED"
 #define RUST_POISON_ON_FREE "RUST_POISON_ON_FREE"
 #define RUST_DEBUG_MEM "RUST_DEBUG_MEM"
+#define RUST_DEBUG_BORROW "RUST_DEBUG_BORROW"
 
 #if defined(__WIN32__)
 static int
@@ -130,6 +131,7 @@ load_env(int argc, char **argv) {
     env->argc = argc;
     env->argv = argv;
     env->debug_mem = getenv(RUST_DEBUG_MEM) != NULL;
+    env->debug_borrow = getenv(RUST_DEBUG_BORROW) != NULL;
     return env;
 }
 
diff --git a/src/rt/rust_env.h b/src/rt/rust_env.h
index df27f7674f265..b897f0c09a90b 100644
--- a/src/rt/rust_env.h
+++ b/src/rt/rust_env.h
@@ -28,6 +28,7 @@ struct rust_env {
     int argc;
     char **argv;
     rust_bool debug_mem;
+    rust_bool debug_borrow;
 };
 
 rust_env* load_env(int argc, char **argv);
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 7e3c91f62113e..23e705357685d 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -42,6 +42,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state,
     total_stack_sz(0),
     task_local_data(NULL),
     task_local_data_cleanup(NULL),
+    borrow_list(NULL),
     state(state),
     cond(NULL),
     cond_name("none"),
@@ -75,6 +76,16 @@ rust_task::delete_this()
     assert(ref_count == 0); // ||
     //   (ref_count == 1 && this == sched->root_task));
 
+    if (borrow_list) {
+        // NOTE should free borrow_list from within rust code!
+        // If there is a pointer in there, it is a ~[BorrowRecord] pointer,
+        // which are currently allocated with LIBC malloc/free. But this is
+        // not really the right way to do this, we should be freeing this
+        // pointer from Rust code.
+        free(borrow_list);
+        borrow_list = NULL;
+    }
+
     sched_loop->release_task(this);
 }
 
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index fd4e8451b642a..b76a177e1c87a 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -245,6 +245,11 @@ rust_task : public kernel_owned<rust_task>
     void *task_local_data;
     void (*task_local_data_cleanup)(void *data);
 
+    // Contains a ~[BorrowRecord] pointer, or NULL.
+    //
+    // Used by borrow management code in libcore/unstable/lang.rs.
+    void *borrow_list;
+
 private:
 
     // Protects state, cond, cond_name
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 3ca05b94711e8..1c3f6370deda4 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -233,3 +233,5 @@ rust_boxed_region_malloc
 rust_boxed_region_free
 rust_try
 rust_begin_unwind
+rust_take_task_borrow_list
+rust_set_task_borrow_list
diff --git a/src/test/compile-fail/access-mode-in-closures.rs b/src/test/compile-fail/access-mode-in-closures.rs
index f6b9a82ec676c..61fb754f7619f 100644
--- a/src/test/compile-fail/access-mode-in-closures.rs
+++ b/src/test/compile-fail/access-mode-in-closures.rs
@@ -16,6 +16,6 @@ fn unpack(_unpack: &fn(v: &sty) -> ~[int]) {}
 fn main() {
     let _foo = unpack(|s| {
         // Test that `s` is moved here.
-        match *s { sty(v) => v } //~ ERROR moving out of dereference of immutable & pointer
+        match *s { sty(v) => v } //~ ERROR cannot move out
     });
 }
diff --git a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs
index e2dd13a4405d1..85f60f34bdb80 100644
--- a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs
+++ b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs
@@ -17,6 +17,7 @@ fn main() {
         y = Some(x.downgrade(write_mode));
         //~^ ERROR cannot infer an appropriate lifetime
     }
+    y.get();
     // Adding this line causes a method unification failure instead
     // do (&option::unwrap(y)).read |state| { assert!(*state == 1); }
 }
diff --git a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs
index 78a50a4f21242..c7ae6a0dc6c52 100644
--- a/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs
+++ b/src/test/compile-fail/arc-rw-write-mode-shouldnt-escape.rs
@@ -17,6 +17,7 @@ fn main() {
     do x.write_downgrade |write_mode| {
         y = Some(write_mode);
     }
+    y.get();
     // Adding this line causes a method unification failure instead
     // do (&option::unwrap(y)).write |state| { assert!(*state == 1); }
 }
diff --git a/src/test/compile-fail/attempted-access-non-fatal.rs b/src/test/compile-fail/attempted-access-non-fatal.rs
index ba15abc3f8965..1d9249bc17b1f 100644
--- a/src/test/compile-fail/attempted-access-non-fatal.rs
+++ b/src/test/compile-fail/attempted-access-non-fatal.rs
@@ -11,6 +11,6 @@
 // Check that bogus field access is non-fatal
 fn main() {
     let x = 0;
-    debug!(x.foo); //~ ERROR attempted access of field
-    debug!(x.bar); //~ ERROR attempted access of field
+    let _ = x.foo; //~ ERROR attempted access of field
+    let _ = x.bar; //~ ERROR attempted access of field
 }
diff --git a/src/test/compile-fail/borrowck-addr-of-upvar.rs b/src/test/compile-fail/borrowck-addr-of-upvar.rs
index 640bc887731f9..83baedc789277 100644
--- a/src/test/compile-fail/borrowck-addr-of-upvar.rs
+++ b/src/test/compile-fail/borrowck-addr-of-upvar.rs
@@ -9,12 +9,12 @@
 // except according to those terms.
 
 fn foo(x: @int) -> @fn() -> &'static int {
-    let result: @fn() -> &'static int = || &*x;  //~ ERROR illegal borrow
+    let result: @fn() -> &'static int = || &*x;  //~ ERROR cannot root
     result
 }
 
 fn bar(x: @int) -> @fn() -> &int {
-    let result: @fn() -> &int = || &*x; //~ ERROR illegal borrow
+    let result: @fn() -> &int = || &*x; //~ ERROR cannot root
     result
 }
 
diff --git a/src/test/compile-fail/borrowck-assign-comp-idx.rs b/src/test/compile-fail/borrowck-assign-comp-idx.rs
index a284cd5b4a791..9b21cbf9768f7 100644
--- a/src/test/compile-fail/borrowck-assign-comp-idx.rs
+++ b/src/test/compile-fail/borrowck-assign-comp-idx.rs
@@ -17,9 +17,11 @@ fn a() {
     let mut p = ~[1];
 
     // Create an immutable pointer into p's contents:
-    let _q: &int = &p[0]; //~ NOTE loan of mutable vec content granted here
+    let q: &int = &p[0];
 
-    p[0] = 5; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan
+    p[0] = 5; //~ ERROR cannot assign
+
+    debug!("%d", *q);
 }
 
 fn borrow(_x: &[int], _f: &fn()) {}
@@ -30,8 +32,8 @@ fn b() {
 
     let mut p = ~[1];
 
-    do borrow(p) { //~ NOTE loan of mutable vec content granted here
-        p[0] = 5; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan
+    do borrow(p) {
+        p[0] = 5; //~ ERROR cannot assign to
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-assign-comp.rs b/src/test/compile-fail/borrowck-assign-comp.rs
index eb832fe738da2..ccd0542ca7f59 100644
--- a/src/test/compile-fail/borrowck-assign-comp.rs
+++ b/src/test/compile-fail/borrowck-assign-comp.rs
@@ -12,12 +12,13 @@ struct point { x: int, y: int }
 
 fn a() {
     let mut p = point {x: 3, y: 4};
-    let _q = &p; //~ NOTE loan of mutable local variable granted here
+    let q = &p;
 
     // This assignment is illegal because the field x is not
     // inherently mutable; since `p` was made immutable, `p.x` is now
     // immutable.  Otherwise the type of &_q.x (&int) would be wrong.
-    p.x = 5; //~ ERROR assigning to mutable field prohibited due to outstanding loan
+    p.x = 5; //~ ERROR cannot assign to `p.x`
+    q.x;
 }
 
 fn c() {
@@ -25,9 +26,10 @@ fn c() {
     // and then try to overwrite `p` as a whole.
 
     let mut p = point {x: 3, y: 4};
-    let _q = &p.y; //~ NOTE loan of mutable local variable granted here
-    p = point {x: 5, y: 7};//~ ERROR assigning to mutable local variable prohibited due to outstanding loan
-    copy p;
+    let q = &p.y;
+    p = point {x: 5, y: 7};//~ ERROR cannot assign to `p`
+    p.x; // silence warning
+    *q; // stretch loan
 }
 
 fn d() {
@@ -35,9 +37,9 @@ fn d() {
     // address of a subcomponent and then modify that subcomponent:
 
     let mut p = point {x: 3, y: 4};
-    let _q = &p.y; //~ NOTE loan of mutable field granted here
-    p.y = 5; //~ ERROR assigning to mutable field prohibited due to outstanding loan
-    copy p;
+    let q = &p.y;
+    p.y = 5; //~ ERROR cannot assign to `p.y`
+    *q;
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-assign-to-constants.rs b/src/test/compile-fail/borrowck-assign-to-constants.rs
index 0d65aacb65b7a..f0dc28b736d16 100644
--- a/src/test/compile-fail/borrowck-assign-to-constants.rs
+++ b/src/test/compile-fail/borrowck-assign-to-constants.rs
@@ -12,6 +12,6 @@ static foo: int = 5;
 
 fn main() {
     // assigning to various global constants
-    None = Some(3); //~ ERROR assigning to static item
-    foo = 6; //~ ERROR assigning to static item
+    None = Some(3); //~ ERROR cannot assign to immutable static item
+    foo = 6; //~ ERROR cannot assign to immutable static item
 }
diff --git a/src/test/compile-fail/borrowck-assign-to-enum.rs b/src/test/compile-fail/borrowck-assign-to-enum.rs
index a35d88a76f393..fcaba0adc46eb 100644
--- a/src/test/compile-fail/borrowck-assign-to-enum.rs
+++ b/src/test/compile-fail/borrowck-assign-to-enum.rs
@@ -12,5 +12,5 @@ struct foo(int);
 
 fn main() {
     let x = foo(3);
-    *x = 4; //~ ERROR assigning to anonymous field
+    *x = 4; //~ ERROR cannot assign to immutable anonymous field
 }
diff --git a/src/test/compile-fail/borrowck-assign-to-subfield.rs b/src/test/compile-fail/borrowck-assign-to-subfield.rs
index 610802ca68b31..2ee5ecfcb9ce0 100644
--- a/src/test/compile-fail/borrowck-assign-to-subfield.rs
+++ b/src/test/compile-fail/borrowck-assign-to-subfield.rs
@@ -34,6 +34,6 @@ fn main() {
 
     // in these cases we pass through a box, so the mut
     // of the box is dominant
-    p.x.a = 2;     //~ ERROR assigning to immutable field
+    p.x.a = 2;     //~ ERROR cannot assign to immutable field
     p.z.a = 2;
 }
diff --git a/src/test/compile-fail/auto-ref-borrowck-failure.rs b/src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs
similarity index 81%
rename from src/test/compile-fail/auto-ref-borrowck-failure.rs
rename to src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs
index 894b71357b75a..2ba5d0473cc62 100644
--- a/src/test/compile-fail/auto-ref-borrowck-failure.rs
+++ b/src/test/compile-fail/borrowck-auto-mut-ref-to-immut-var.rs
@@ -14,17 +14,13 @@ struct Foo {
     x: int
 }
 
-trait Stuff {
-    fn printme(self);
-}
-
-impl<'self> Stuff for &'self mut Foo {
-    fn printme(self) {
+pub impl Foo {
+    fn printme(&mut self) {
         io::println(fmt!("%d", self.x));
     }
 }
 
 fn main() {
     let x = Foo { x: 3 };
-    x.printme();    //~ ERROR illegal borrow
+    x.printme();    //~ ERROR cannot borrow
 }
diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs
index c95b93445adca..192fe669f57ae 100644
--- a/src/test/compile-fail/borrowck-autoref-3261.rs
+++ b/src/test/compile-fail/borrowck-autoref-3261.rs
@@ -17,10 +17,10 @@ pub impl X {
 }
 fn main() {
     let mut x = X(Right(main));
-    do (&mut x).with |opt| {  //~ ERROR illegal borrow
+    do (&mut x).with |opt| {
         match opt {
             &Right(ref f) => {
-                x = X(Left((0,0))); //~ ERROR assigning to captured outer mutable variable
+                x = X(Left((0,0))); //~ ERROR cannot assign to `x`
                 (*f)()
             },
             _ => fail!()
diff --git a/src/test/compile-fail/borrowck-bad-nested-calls-free.rs b/src/test/compile-fail/borrowck-bad-nested-calls-free.rs
new file mode 100644
index 0000000000000..ff1ec38ad6406
--- /dev/null
+++ b/src/test/compile-fail/borrowck-bad-nested-calls-free.rs
@@ -0,0 +1,43 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we detect nested calls that could free pointers evaluated
+// for earlier arguments.
+
+fn rewrite(v: &mut ~uint) -> uint {
+    *v = ~22;
+    **v
+}
+
+fn add(v: &uint, w: uint) -> uint {
+    *v + w
+}
+
+fn implicit() {
+    let mut a = ~1;
+
+    // Note the danger here:
+    //
+    //    the pointer for the first argument has already been
+    //    evaluated, but it gets freed when evaluating the second
+    //    argument!
+    add(
+        a,
+        rewrite(&mut a)); //~ ERROR cannot borrow
+}
+
+fn explicit() {
+    let mut a = ~1;
+    add(
+        &*a,
+        rewrite(&mut a)); //~ ERROR cannot borrow
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-bad-nested-calls-move.rs b/src/test/compile-fail/borrowck-bad-nested-calls-move.rs
new file mode 100644
index 0000000000000..0adf486b8b3ab
--- /dev/null
+++ b/src/test/compile-fail/borrowck-bad-nested-calls-move.rs
@@ -0,0 +1,43 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we detect nested calls that could free pointers evaluated
+// for earlier arguments.
+
+fn rewrite(v: &mut ~uint) -> uint {
+    *v = ~22;
+    **v
+}
+
+fn add(v: &uint, w: ~uint) -> uint {
+    *v + *w
+}
+
+fn implicit() {
+    let mut a = ~1;
+
+    // Note the danger here:
+    //
+    //    the pointer for the first argument has already been
+    //    evaluated, but it gets moved when evaluating the second
+    //    argument!
+    add(
+        a,
+        a); //~ ERROR cannot move
+}
+
+fn explicit() {
+    let mut a = ~1;
+    add(
+        &*a,
+        a); //~ ERROR cannot move
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
index 005908f86d87d..1051c5829ec38 100644
--- a/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
+++ b/src/test/compile-fail/borrowck-borrow-from-owned-ptr.rs
@@ -22,32 +22,37 @@ fn make_foo() -> ~Foo { fail!() }
 
 fn borrow_same_field_twice_mut_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_mut_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
+    let bar1 = &foo.bar1;
     let _bar2 = &foo.bar1;
+    *bar1;
 }
 
-fn borrow_both_mut() {
+fn borrow_both_fields_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _bar2 = &mut foo.bar2;
+    *bar1;
 }
 
 fn borrow_both_mut_pattern() {
@@ -59,66 +64,77 @@ fn borrow_both_mut_pattern() {
 
 fn borrow_var_and_pattern() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     match *foo {
         Foo { bar1: ref mut _bar1, bar2: _ } => {}
-        //~^ ERROR conflicts with prior loan
+        //~^ ERROR cannot borrow
     }
+    *bar1;
 }
 
 fn borrow_mut_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan
-    let _foo2 = &*foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &foo.bar1; //~ ERROR cannot borrow
+    let _foo2 = &*foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
+    let bar1 = &foo.bar1.int1;
     let _foo1 = &foo.bar1;
     let _foo2 = &*foo;
+    *bar1;
 }
 
 fn borrow_mut_and_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _foo1 = &foo.bar2;
+    *bar1;
 }
 
 fn borrow_mut_from_imm() {
     let foo = make_foo();
-    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+    let bar1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_long_path_both_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar2.int2;
+    let bar1 = &mut foo.bar1.int1;
+    let foo1 = &mut foo.bar2.int2;
+    *bar1;
+    *foo1;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
index 035e293bc36b6..cdcf50c906e36 100644
--- a/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
+++ b/src/test/compile-fail/borrowck-borrow-from-stack-variable.rs
@@ -22,32 +22,37 @@ fn make_foo() -> Foo { fail!() }
 
 fn borrow_same_field_twice_mut_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_mut_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
-    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1;
+    let _bar2 = &foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1;
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_same_field_twice_imm_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1;
+    let bar1 = &foo.bar1;
     let _bar2 = &foo.bar1;
+    *bar1;
 }
 
 fn borrow_both_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _bar2 = &mut foo.bar2;
+    *bar1;
 }
 
 fn borrow_both_mut_pattern() {
@@ -59,66 +64,76 @@ fn borrow_both_mut_pattern() {
 
 fn borrow_var_and_pattern() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     match foo {
-        Foo { bar1: ref mut _bar1, bar2: _ } => {}
-        //~^ ERROR conflicts with prior loan
+        Foo { bar1: ref mut _bar1, bar2: _ } => {} //
+        //~^ ERROR cannot borrow
     }
+    *bar1;
 }
 
 fn borrow_mut_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan
-    let _foo2 = &foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &foo.bar1; //~ ERROR cannot borrow
+    let _foo2 = &foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_mut_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
-    let _foo2 = &mut foo; //~ ERROR conflicts with prior loan
+    let bar1 = &mut foo.bar1.int1;
+    let _foo2 = &mut foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_mut2() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
-    let _foo2 = &mut foo; //~ ERROR conflicts with prior loan
+    let bar1 = &foo.bar1.int1;
+    let _foo2 = &mut foo; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_imm_and_base_imm() {
     let mut foo = make_foo();
-    let _bar1 = &foo.bar1.int1;
+    let bar1 = &foo.bar1.int1;
     let _foo1 = &foo.bar1;
     let _foo2 = &foo;
+    *bar1;
 }
 
 fn borrow_mut_and_imm() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1;
+    let bar1 = &mut foo.bar1;
     let _foo1 = &foo.bar2;
+    *bar1;
 }
 
 fn borrow_mut_from_imm() {
     let foo = make_foo();
-    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+    let bar1 = &mut foo.bar1; //~ ERROR cannot borrow
+    *bar1;
 }
 
 fn borrow_long_path_both_mut() {
     let mut foo = make_foo();
-    let _bar1 = &mut foo.bar1.int1;
+    let bar1 = &mut foo.bar1.int1;
     let _foo1 = &mut foo.bar2.int2;
+    *bar1;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs
index 4a6a90ae5167f..1e5c4c5cc410c 100644
--- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs
+++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue-2.rs
@@ -28,5 +28,6 @@ fn defer<'r>(x: &'r [&'r str]) -> defer<'r> {
 }
 
 fn main() {
-    let _x = defer(~["Goodbye", "world!"]); //~ ERROR illegal borrow
+    let x = defer(~["Goodbye", "world!"]); //~ ERROR borrowed value does not live long enough
+    x.x[0];
 }
diff --git a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs
index bda659aa7b97e..887cb59930ebc 100644
--- a/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs
+++ b/src/test/compile-fail/borrowck-borrowed-uniq-rvalue.rs
@@ -15,7 +15,7 @@ use core::hashmap::HashMap;
 fn main() {
     let mut buggy_map :HashMap<uint, &uint> =
       HashMap::new::<uint, &uint>();
-    buggy_map.insert(42, &*~1); //~ ERROR illegal borrow
+    buggy_map.insert(42, &*~1); //~ ERROR borrowed value does not live long enough
 
     // but it is ok if we use a temporary
     let tmp = ~2;
diff --git a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
index f2c6ae98819e2..3abd19e5a1136 100644
--- a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
+++ b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
@@ -27,13 +27,15 @@ fn a(x: &mut Foo) {
 fn b(x: &Foo) {
     x.f();
     x.g();
-    x.h(); //~ ERROR illegal borrow
+    x.h(); //~ ERROR cannot borrow
 }
 
 fn c(x: &const Foo) {
-    x.f(); //~ ERROR illegal borrow unless pure
+    x.f(); //~ ERROR cannot borrow
+    //~^ ERROR unsafe borrow
     x.g();
-    x.h(); //~ ERROR illegal borrow
+    x.h(); //~ ERROR cannot borrow
+    //~^ ERROR unsafe borrow
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs b/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs
index 88db5f5434116..8af10231921aa 100644
--- a/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs
+++ b/src/test/compile-fail/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs
@@ -10,9 +10,9 @@
 
 fn main() {
     let mut _a = 3;
-    let _b = &mut _a; //~ NOTE loan of mutable local variable granted here
+    let _b = &mut _a;
     {
         let _c = &*_b;
-        _a = 4; //~ ERROR assigning to mutable local variable prohibited
+        _a = 4; //~ ERROR cannot assign to `_a`
     }
 }
diff --git a/src/test/compile-fail/borrowck-insert-during-each.rs b/src/test/compile-fail/borrowck-insert-during-each.rs
index 17c0efe225e4d..109753b38e70b 100644
--- a/src/test/compile-fail/borrowck-insert-during-each.rs
+++ b/src/test/compile-fail/borrowck-insert-during-each.rs
@@ -23,8 +23,8 @@ pub impl Foo {
 }
 
 fn bar(f: &mut Foo) {
-  do f.foo |a| { //~ NOTE prior loan as mutable granted here
-    f.n.insert(*a); //~ ERROR conflicts with prior loan
+  do f.foo |a| {
+    f.n.insert(*a); //~ ERROR cannot borrow
   }
 }
 
diff --git a/src/test/compile-fail/borrowck-issue-2657-1.rs b/src/test/compile-fail/borrowck-issue-2657-1.rs
index ce183c1888f13..8bcd5f9a72e70 100644
--- a/src/test/compile-fail/borrowck-issue-2657-1.rs
+++ b/src/test/compile-fail/borrowck-issue-2657-1.rs
@@ -10,9 +10,9 @@
 
 fn main() {
 let x = Some(~1);
-match x { //~ NOTE loan of immutable local variable granted here
+match x {
   Some(ref _y) => {
-    let _a = x; //~ ERROR moving out of immutable local variable prohibited due to outstanding loan
+    let _a = x; //~ ERROR cannot move
   }
   _ => {}
 }
diff --git a/src/test/compile-fail/borrowck-issue-2657-2.rs b/src/test/compile-fail/borrowck-issue-2657-2.rs
index d2217778d4148..fac805c57ca09 100644
--- a/src/test/compile-fail/borrowck-issue-2657-2.rs
+++ b/src/test/compile-fail/borrowck-issue-2657-2.rs
@@ -12,7 +12,7 @@ fn main() {
 let x = Some(~1);
 match x {
   Some(ref y) => {
-    let _b = *y; //~ ERROR moving out of dereference of immutable & pointer
+    let _b = *y; //~ ERROR cannot move out
   }
   _ => {}
 }
diff --git a/src/test/compile-fail/borrowck-lend-flow-if.rs b/src/test/compile-fail/borrowck-lend-flow-if.rs
new file mode 100644
index 0000000000000..563f63b98be05
--- /dev/null
+++ b/src/test/compile-fail/borrowck-lend-flow-if.rs
@@ -0,0 +1,52 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Note: the borrowck analysis is currently flow-insensitive.
+// Therefore, some of these errors are marked as spurious and could be
+// corrected by a simple change to the analysis.  The others are
+// either genuine or would require more advanced changes.  The latter
+// cases are noted.
+
+fn borrow(_v: &int) {}
+fn borrow_mut(_v: &mut int) {}
+fn cond() -> bool { fail!() }
+fn for_func(_f: &fn() -> bool) { fail!() }
+fn produce<T>() -> T { fail!(); }
+
+fn inc(v: &mut ~int) {
+    *v = ~(**v + 1);
+}
+
+fn pre_freeze_cond() {
+    // In this instance, the freeze is conditional and starts before
+    // the mut borrow.
+
+    let mut v = ~3;
+    let _w;
+    if cond() {
+        _w = &v;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn pre_freeze_else() {
+    // In this instance, the freeze and mut borrow are on separate sides
+    // of the if.
+
+    let mut v = ~3;
+    let _w;
+    if cond() {
+        _w = &v;
+    } else {
+        borrow_mut(v);
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-lend-flow-loop.rs b/src/test/compile-fail/borrowck-lend-flow-loop.rs
new file mode 100644
index 0000000000000..b6384ad9590ab
--- /dev/null
+++ b/src/test/compile-fail/borrowck-lend-flow-loop.rs
@@ -0,0 +1,164 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Note: the borrowck analysis is currently flow-insensitive.
+// Therefore, some of these errors are marked as spurious and could be
+// corrected by a simple change to the analysis.  The others are
+// either genuine or would require more advanced changes.  The latter
+// cases are noted.
+
+fn borrow(_v: &int) {}
+fn borrow_mut(_v: &mut int) {}
+fn cond() -> bool { fail!() }
+fn for_func(_f: &fn() -> bool) { fail!() }
+fn produce<T>() -> T { fail!(); }
+
+fn inc(v: &mut ~int) {
+    *v = ~(**v + 1);
+}
+
+fn loop_overarching_alias_mut() {
+    // In this instance, the borrow encompasses the entire loop.
+
+    let mut v = ~3;
+    let mut x = &mut v;
+    **x += 1;
+    loop {
+        borrow(v); //~ ERROR cannot borrow
+    }
+}
+
+fn block_overarching_alias_mut() {
+    // In this instance, the borrow encompasses the entire closure call.
+
+    let mut v = ~3;
+    let mut x = &mut v;
+    for 3.times {
+        borrow(v); //~ ERROR cannot borrow
+    }
+    *x = ~5;
+}
+
+fn loop_aliased_mut() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    loop {
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+    }
+}
+
+fn while_aliased_mut() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    while cond() {
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+    }
+}
+
+fn for_loop_aliased_mut() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    for for_func {
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+    }
+}
+
+fn loop_aliased_mut_break() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    loop {
+        borrow_mut(v);
+        _x = &v;
+        break;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn while_aliased_mut_break() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    while cond() {
+        borrow_mut(v);
+        _x = &v;
+        break;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn for_aliased_mut_break() {
+    // In this instance, the borrow is carried through the loop.
+
+    let mut v = ~3, w = ~4;
+    let mut _x = &w;
+    for for_func {
+        // here we cannot be sure that `for_func` respects the break below
+        borrow_mut(v); //~ ERROR cannot borrow
+        _x = &v;
+        break;
+    }
+    borrow_mut(v); //~ ERROR cannot borrow
+}
+
+fn while_aliased_mut_cond(cond: bool, cond2: bool) {
+    let mut v = ~3, w = ~4;
+    let mut x = &mut w;
+    while cond {
+        **x += 1;
+        borrow(v); //~ ERROR cannot borrow
+        if cond2 {
+            x = &mut v; //~ ERROR cannot borrow
+        }
+    }
+}
+
+fn loop_break_pops_scopes<'r>(_v: &'r mut [uint], f: &fn(&'r mut uint) -> bool) {
+    // Here we check that when you break out of an inner loop, the
+    // borrows that go out of scope as you exit the inner loop are
+    // removed from the bitset.
+
+    while cond() {
+        while cond() {
+            // this borrow is limited to the scope of `r`...
+            let r: &'r mut uint = produce();
+            if !f(&mut *r) {
+                break; // ...so it is not live as exit the `while` loop here
+            }
+        }
+    }
+}
+
+fn loop_loop_pops_scopes<'r>(_v: &'r mut [uint], f: &fn(&'r mut uint) -> bool) {
+    // Similar to `loop_break_pops_scopes` but for the `loop` keyword
+
+    while cond() {
+        while cond() {
+            // this borrow is limited to the scope of `r`...
+            let r: &'r mut uint = produce();
+            if !f(&mut *r) {
+                loop; // ...so it is not live as exit (and re-enter) the `while` loop here
+            }
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-lend-flow-match.rs b/src/test/compile-fail/borrowck-lend-flow-match.rs
new file mode 100644
index 0000000000000..7603fdc82a824
--- /dev/null
+++ b/src/test/compile-fail/borrowck-lend-flow-match.rs
@@ -0,0 +1,60 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-pretty -- comments are infaithfully preserved
+
+#[allow(unused_variable)];
+#[allow(dead_assignment)];
+
+fn cond() -> bool { fail!() }
+fn link<'a>(v: &'a uint, w: &mut &'a uint) -> bool { *w = v; true }
+
+fn separate_arms() {
+    // Here both arms perform assignments, but only is illegal.
+
+    let mut x = None;
+    match x {
+        None => {
+            // It is ok to reassign x here, because there is in
+            // fact no outstanding loan of x!
+            x = Some(0);
+        }
+        Some(ref _i) => {
+            x = Some(1); //~ ERROR cannot assign
+        }
+    }
+    copy x; // just to prevent liveness warnings
+}
+
+fn guard() {
+    // Here the guard performs a borrow. This borrow "infects" all
+    // subsequent arms (but not the prior ones).
+
+    let mut a = ~3;
+    let mut b = ~4;
+    let mut w = &*a;
+    match 22 {
+        _ if cond() => {
+            b = ~5;
+        }
+
+        _ if link(&*b, &mut w) => {
+            b = ~6; //~ ERROR cannot assign
+        }
+
+        _ => {
+            b = ~7; //~ ERROR cannot assign
+        }
+    }
+
+    b = ~8; //~ ERROR cannot assign
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck-lend-flow.rs b/src/test/compile-fail/borrowck-lend-flow.rs
index ed6446a6311b8..59cac0c5d953a 100644
--- a/src/test/compile-fail/borrowck-lend-flow.rs
+++ b/src/test/compile-fail/borrowck-lend-flow.rs
@@ -15,96 +15,37 @@
 // cases are noted.
 
 fn borrow(_v: &int) {}
+fn borrow_mut(_v: &mut int) {}
+fn cond() -> bool { fail!() }
+fn for_func(_f: &fn() -> bool) { fail!() }
+fn produce<T>() -> T { fail!(); }
 
 fn inc(v: &mut ~int) {
     *v = ~(**v + 1);
 }
 
-fn post_aliased_const() {
-    let mut v = ~3;
-    borrow(v);
-    let _w = &const v;
-}
-
-fn post_aliased_mut() {
-    // SPURIOUS--flow
-    let mut v = ~3;
-    borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    let _w = &mut v; //~ NOTE prior loan as mutable granted here
-}
+fn pre_freeze() {
+    // In this instance, the freeze starts before the mut borrow.
 
-fn post_aliased_scope(cond: bool) {
     let mut v = ~3;
-    borrow(v);
-    if cond { inc(&mut v); }
+    let _w = &v;
+    borrow_mut(v); //~ ERROR cannot borrow
 }
 
-fn loop_overarching_alias_mut() {
-    let mut v = ~3;
-    let mut _x = &mut v; //~ NOTE prior loan as mutable granted here
-    loop {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    }
-}
+fn pre_const() {
+    // In this instance, the freeze starts before the mut borrow.
 
-fn block_overarching_alias_mut() {
     let mut v = ~3;
-    let mut _x = &mut v; //~ NOTE prior loan as mutable granted here
-    for 3.times {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    }
-}
-
-fn loop_aliased_mut() {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    loop {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
-}
-
-fn while_aliased_mut(cond: bool) {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    while cond {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
-}
-
-fn while_aliased_mut_cond(cond: bool, cond2: bool) {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    while cond {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        if cond2 {
-            _x = &mut v; //~ NOTE prior loan as mutable granted here
-        }
-    }
-}
-
-fn loop_in_block() {
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    for uint::range(0u, 10u) |_i| {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
+    let _w = &const v;
+    borrow_mut(v);
 }
 
-fn at_most_once_block() {
-    fn at_most_once(f: &fn()) { f() }
+fn post_freeze() {
+    // In this instance, the const alias starts after the borrow.
 
-    // Here, the borrow check has no way of knowing that the block is
-    // executed at most once.
-
-    let mut v = ~3, w = ~4;
-    let mut _x = &mut w;
-    do at_most_once {
-        borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-        _x = &mut v; //~ NOTE prior loan as mutable granted here
-    }
+    let mut v = ~3;
+    borrow_mut(v);
+    let _w = &v;
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
index 784fce1300f76..50dd815d49302 100644
--- a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
+++ b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs
@@ -14,17 +14,17 @@ fn borrow(v: &int, f: &fn(x: &int)) {
 
 fn box_imm() {
     let v = ~3;
-    let _w = &v; //~ NOTE loan of immutable local variable granted here
+    let _w = &v;
     do task::spawn {
         debug!("v=%d", *v);
-        //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
+        //~^ ERROR cannot move `v` into closure
     }
 
     let v = ~3;
-    let _w = &v; //~ NOTE loan of immutable local variable granted here
+    let _w = &v;
     task::spawn(|| {
         debug!("v=%d", *v);
-        //~^ ERROR by-move capture of immutable local variable prohibited due to outstanding loan
+        //~^ ERROR cannot move
     });
 }
 
diff --git a/src/test/compile-fail/borrowck-loan-blocks-move.rs b/src/test/compile-fail/borrowck-loan-blocks-move.rs
index 3af77d2df7f01..b9a79f4f3b1b1 100644
--- a/src/test/compile-fail/borrowck-loan-blocks-move.rs
+++ b/src/test/compile-fail/borrowck-loan-blocks-move.rs
@@ -13,8 +13,8 @@ fn take(_v: ~int) {
 
 fn box_imm() {
     let v = ~3;
-    let _w = &v; //~ NOTE loan of immutable local variable granted here
-    take(v); //~ ERROR moving out of immutable local variable prohibited due to outstanding loan
+    let _w = &v;
+    take(v); //~ ERROR cannot move out of `v` because it is borrowed
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
index 14cb37d775c43..f8415a38573c4 100644
--- a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
+++ b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs
@@ -14,8 +14,8 @@ fn borrow(v: &int, f: &fn(x: &int)) {
 
 fn box_imm() {
     let mut v = ~3;
-    do borrow(v) |w| { //~ NOTE loan of mutable local variable granted here
-        v = ~4; //~ ERROR assigning to captured outer mutable variable in a stack closure prohibited due to outstanding loan
+    do borrow(v) |w| {
+        v = ~4; //~ ERROR cannot assign to `v` because it is borrowed
         assert!(*v == 3);
         assert!(*w == 4);
     }
diff --git a/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs
index 482d1b6b8b617..0361213af2226 100644
--- a/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs
+++ b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs
@@ -8,18 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// xfail-test #3387
-
 struct foo(~uint);
 
 impl Add<foo, foo> for foo {
-    fn add(f: &foo) -> foo {
-        foo(~(**self + **(*f)))
+    fn add(&self, f: &foo) -> foo {
+        foo(~(***self + **(*f)))
     }
 }
 
 fn main() {
     let x = foo(~3);
-    let _y = x + x;
-    //~^ ERROR moving out of immutable local variable prohibited due to outstanding loan
+    let _y = x + {x}; // the `{x}` forces a move to occur
+    //~^ ERROR cannot move out of `x`
 }
diff --git a/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs b/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs
index 1c2bd8dc8e10f..6e8e3da143e4f 100644
--- a/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs
+++ b/src/test/compile-fail/borrowck-loan-local-as-both-mut-and-imm.rs
@@ -22,13 +22,13 @@ use core::either::{Either, Left, Right};
 
     fn g() {
         let mut x: Either<int,float> = Left(3);
-        io::println(f(&mut x, &x).to_str()); //~ ERROR conflicts with prior loan
+        io::println(f(&mut x, &x).to_str()); //~ ERROR cannot borrow
     }
 
     fn h() {
         let mut x: Either<int,float> = Left(3);
         let y: &Either<int, float> = &x;
-        let z: &mut Either<int, float> = &mut x; //~ ERROR conflicts with prior loan
+        let z: &mut Either<int, float> = &mut x; //~ ERROR cannot borrow
         *z = *y;
     }
 
diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
index 21bb7434f8d33..061a6c553e4b0 100644
--- a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
+++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
@@ -38,12 +38,13 @@ fn b() {
 
     // Here I create an outstanding loan and check that we get conflicts:
 
-    let q = &mut p; //~ NOTE prior loan as mutable granted here
+    let q = &mut p;
 
-    p + 3;  // ok for pure fns
-    p.times(3); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    p + 3;  //~ ERROR cannot borrow `p`
+    p.times(3); //~ ERROR cannot borrow `p`
 
-    q.x += 1;
+    *q + 3; // OK to use the new alias `q`
+    q.x += 1; // and OK to mutate it
 }
 
 fn c() {
diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs
index 36007abf05ea3..27a66557434b0 100644
--- a/src/test/compile-fail/borrowck-loan-rcvr.rs
+++ b/src/test/compile-fail/borrowck-loan-rcvr.rs
@@ -13,7 +13,6 @@ struct point { x: int, y: int }
 trait methods {
     fn impurem(&self);
     fn blockm(&self, f: &fn());
-    fn purem(&self);
 }
 
 impl methods for point {
@@ -21,9 +20,6 @@ impl methods for point {
     }
 
     fn blockm(&self, f: &fn()) { f() }
-
-    fn purem(&self) {
-    }
 }
 
 fn a() {
@@ -31,12 +27,11 @@ fn a() {
 
     // Here: it's ok to call even though receiver is mutable, because we
     // can loan it out.
-    p.purem();
     p.impurem();
 
     // But in this case we do not honor the loan:
-    do p.blockm { //~ NOTE loan of mutable local variable granted here
-        p.x = 10; //~ ERROR assigning to mutable field prohibited due to outstanding loan
+    do p.blockm {
+        p.x = 10; //~ ERROR cannot assign
     }
 }
 
@@ -45,20 +40,21 @@ fn b() {
 
     // Here I create an outstanding loan and check that we get conflicts:
 
-    let l = &mut p; //~ NOTE prior loan as mutable granted here
-    //~^ NOTE prior loan as mutable granted here
-
-    p.purem(); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
-    p.impurem(); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    let l = &mut p;
+    p.impurem(); //~ ERROR cannot borrow
 
     l.x += 1;
 }
 
 fn c() {
-    // Loaning @mut as & is considered legal due to dynamic checks:
+    // Loaning @mut as & is considered legal due to dynamic checks...
     let q = @mut point {x: 3, y: 4};
-    q.purem();
     q.impurem();
+
+    // ...but we still detect errors statically when we can.
+    do q.blockm {
+        q.x = 10; //~ ERROR cannot assign
+    }
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-loan-vec-content.rs b/src/test/compile-fail/borrowck-loan-vec-content.rs
index d27d690437aff..6a8e64377aab2 100644
--- a/src/test/compile-fail/borrowck-loan-vec-content.rs
+++ b/src/test/compile-fail/borrowck-loan-vec-content.rs
@@ -24,8 +24,8 @@ fn has_mut_vec_and_does_not_try_to_change_it() {
 
 fn has_mut_vec_but_tries_to_change_it() {
     let mut v = ~[1, 2, 3];
-    do takes_imm_elt(&v[0]) { //~ NOTE loan of mutable vec content granted here
-        v[1] = 4; //~ ERROR assigning to mutable vec content prohibited due to outstanding loan
+    do takes_imm_elt(&v[0]) {
+        v[1] = 4; //~ ERROR cannot assign
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs
index 18b4ce0640c41..c199c8795756d 100644
--- a/src/test/compile-fail/borrowck-move-by-capture.rs
+++ b/src/test/compile-fail/borrowck-move-by-capture.rs
@@ -4,7 +4,7 @@ fn main() {
     let foo = ~3;
     let _pfoo = &foo;
     let _f: @fn() -> int = || *foo + 5;
-    //~^ ERROR by-move capture
+    //~^ ERROR cannot move `foo`
 
     let bar = ~3;
     let _g = || {
diff --git a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
index d0b0f51d0cf77..e4e449822768b 100644
--- a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
+++ b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
@@ -10,7 +10,7 @@
 
 fn main() {
     let x: int = 3;
-    let y: &mut int = &mut x; //~ ERROR illegal borrow
+    let y: &mut int = &mut x; //~ ERROR cannot borrow
     *y = 5;
     debug!(*y);
 }
diff --git a/src/test/compile-fail/borrowck-mut-boxed-vec.rs b/src/test/compile-fail/borrowck-mut-boxed-vec.rs
index e8ed362176f57..716f70b291398 100644
--- a/src/test/compile-fail/borrowck-mut-boxed-vec.rs
+++ b/src/test/compile-fail/borrowck-mut-boxed-vec.rs
@@ -10,7 +10,7 @@
 
 fn main() {
     let v = @mut [ 1, 2, 3 ];
-    for v.each |_x| {   //~ ERROR illegal borrow
-        v[1] = 4;
+    for v.each |_x| {
+        v[1] = 4; //~ ERROR cannot assign
     }
 }
diff --git a/src/test/compile-fail/borrowck-mut-deref-comp.rs b/src/test/compile-fail/borrowck-mut-deref-comp.rs
index 540793d4135f2..d1dc296197892 100644
--- a/src/test/compile-fail/borrowck-mut-deref-comp.rs
+++ b/src/test/compile-fail/borrowck-mut-deref-comp.rs
@@ -11,8 +11,8 @@
 struct foo(~int);
 
 fn borrow(x: @mut foo) {
-    let _y = &***x; //~ ERROR illegal borrow unless pure
-    *x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
+    let _y = &***x;
+    *x = foo(~4); //~ ERROR cannot assign
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
index bc0340983ae34..ec17976c5065c 100644
--- a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
+++ b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
@@ -14,5 +14,5 @@ fn write(v: &mut [int]) {
 
 fn main() {
     let v = ~[1, 2, 3];
-    write(v); //~ ERROR illegal borrow
+    write(v); //~ ERROR cannot borrow
 }
diff --git a/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs b/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs
index 4af3bc17240ce..ed270de51e2ed 100644
--- a/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs
+++ b/src/test/compile-fail/borrowck-no-cycle-in-exchange-heap.rs
@@ -19,9 +19,9 @@ enum cycle {
 fn main() {
     let mut x = ~node(node_ {a: ~empty});
     // Create a cycle!
-    match *x { //~ NOTE loan of mutable local variable granted here
+    match *x {
       node(ref mut y) => {
-        y.a = x; //~ ERROR moving out of mutable local variable prohibited due to outstanding loan
+        y.a = x; //~ ERROR cannot move out of
       }
       empty => {}
     };
diff --git a/src/test/compile-fail/borrowck-pat-by-value-binding.rs b/src/test/compile-fail/borrowck-pat-by-value-binding.rs
index d8c8841d391a2..d60ed3d0e372b 100644
--- a/src/test/compile-fail/borrowck-pat-by-value-binding.rs
+++ b/src/test/compile-fail/borrowck-pat-by-value-binding.rs
@@ -12,23 +12,24 @@ fn process<T>(_t: T) {}
 
 fn match_const_opt_by_mut_ref(v: &const Option<int>) {
     match *v {
-      Some(ref mut i) => process(i), //~ ERROR illegal borrow
+      Some(ref mut i) => process(i), //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow of aliasable, const value
       None => ()
     }
 }
 
 fn match_const_opt_by_const_ref(v: &const Option<int>) {
     match *v {
-      Some(ref const i) => process(i), //~ ERROR illegal borrow unless pure
-      //~^ NOTE impure due to
+      Some(ref const i) => process(i),
+        //~^ ERROR unsafe borrow of aliasable, const value
       None => ()
     }
 }
 
 fn match_const_opt_by_imm_ref(v: &const Option<int>) {
     match *v {
-      Some(ref i) => process(i), //~ ERROR illegal borrow unless pure
-      //~^ NOTE impure due to
+      Some(ref i) => process(i), //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow of aliasable, const value
       None => ()
     }
 }
diff --git a/src/test/compile-fail/borrowck-pat-enum.rs b/src/test/compile-fail/borrowck-pat-enum.rs
index 4aa1ecc0ce3fe..c50357e8b9c62 100644
--- a/src/test/compile-fail/borrowck-pat-enum.rs
+++ b/src/test/compile-fail/borrowck-pat-enum.rs
@@ -26,7 +26,8 @@ fn match_ref_unused(&&v: Option<int>) {
 
 fn match_const_reg(v: &const Option<int>) -> int {
     match *v {
-      Some(ref i) => {*i} // OK because this is pure
+      Some(ref i) => {*i} //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow
       None => {0}
     }
 }
@@ -43,8 +44,8 @@ fn match_const_reg_unused(v: &const Option<int>) {
 
 fn match_const_reg_impure(v: &const Option<int>) {
     match *v {
-      Some(ref i) => {impure(*i)} //~ ERROR illegal borrow unless pure
-      //~^ NOTE impure due to access to impure function
+      Some(ref i) => {impure(*i)} //~ ERROR cannot borrow
+        //~^ ERROR unsafe borrow
       None => {}
     }
 }
@@ -56,5 +57,12 @@ fn match_imm_reg(v: &Option<int>) {
     }
 }
 
+fn match_mut_reg(v: &mut Option<int>) {
+    match *v {
+      Some(ref i) => {impure(*i)} // OK, frozen
+      None => {}
+    }
+}
+
 fn main() {
 }
diff --git a/src/test/compile-fail/borrowck-pat-reassign-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-binding.rs
index ca1fdc97c22f8..d05160132c6c2 100644
--- a/src/test/compile-fail/borrowck-pat-reassign-binding.rs
+++ b/src/test/compile-fail/borrowck-pat-reassign-binding.rs
@@ -12,11 +12,14 @@
 
 fn main() {
     let mut x: Option<int> = None;
-    match x { //~ NOTE loan of mutable local variable granted here
-      None => {}
+    match x {
+      None => {
+          // Note: on this branch, no borrow has occurred.
+          x = Some(0);
+      }
       Some(ref i) => {
-        // Not ok: i is an outstanding ptr into x.
-        x = Some(*i+1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
+          // But on this branch, `i` is an outstanding borrow
+          x = Some(*i+1); //~ ERROR cannot assign to `x`
       }
     }
     copy x; // just to prevent liveness warnings
diff --git a/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs b/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs
deleted file mode 100644
index dd6eca951b8f3..0000000000000
--- a/src/test/compile-fail/borrowck-pat-reassign-sometimes-binding.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// xfail-pretty -- comments are infaithfully preserved
-
-fn main() {
-    let mut x = None;
-    match x { //~ NOTE loan of mutable local variable granted here
-      None => {
-        // It is ok to reassign x here, because there is in
-        // fact no outstanding loan of x!
-        x = Some(0);
-      }
-      Some(ref _i) => {
-        x = Some(1); //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
-      }
-    }
-    copy x; // just to prevent liveness warnings
-}
diff --git a/src/test/compile-fail/borrowck-reborrow-from-mut.rs b/src/test/compile-fail/borrowck-reborrow-from-mut.rs
index 60f817dee0c54..b4bd64f213586 100644
--- a/src/test/compile-fail/borrowck-reborrow-from-mut.rs
+++ b/src/test/compile-fail/borrowck-reborrow-from-mut.rs
@@ -20,17 +20,17 @@ struct Bar {
 
 fn borrow_same_field_twice_mut_mut(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
 }
 
 fn borrow_same_field_twice_mut_imm(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1;
-    let _bar2 = &foo.bar1;  //~ ERROR conflicts with prior loan
+    let _bar2 = &foo.bar1;  //~ ERROR cannot borrow
 }
 
 fn borrow_same_field_twice_imm_mut(foo: &mut Foo) {
     let _bar1 = &foo.bar1;
-    let _bar2 = &mut foo.bar1;  //~ ERROR conflicts with prior loan
+    let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
 }
 
 fn borrow_same_field_twice_imm_imm(foo: &mut Foo) {
@@ -53,34 +53,34 @@ fn borrow_var_and_pattern(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1;
     match *foo {
         Foo { bar1: ref mut _bar1, bar2: _ } => {}
-        //~^ ERROR conflicts with prior loan
+        //~^ ERROR cannot borrow
     }
 }
 
 fn borrow_mut_and_base_imm(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &foo.bar1; //~ ERROR conflicts with prior loan
-    let _foo2 = &*foo; //~ ERROR conflicts with prior loan
+    let _foo1 = &foo.bar1; //~ ERROR cannot borrow
+    let _foo2 = &*foo; //~ ERROR cannot borrow
 }
 
 fn borrow_mut_and_base_mut(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
 }
 
 fn borrow_mut_and_base_mut2(foo: &mut Foo) {
     let _bar1 = &mut foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
 }
 
 fn borrow_imm_and_base_mut(foo: &mut Foo) {
     let _bar1 = &foo.bar1.int1;
-    let _foo1 = &mut foo.bar1; //~ ERROR conflicts with prior loan
+    let _foo1 = &mut foo.bar1; //~ ERROR cannot borrow
 }
 
 fn borrow_imm_and_base_mut2(foo: &mut Foo) {
     let _bar1 = &foo.bar1.int1;
-    let _foo2 = &mut *foo; //~ ERROR conflicts with prior loan
+    let _foo2 = &mut *foo; //~ ERROR cannot borrow
 }
 
 fn borrow_imm_and_base_imm(foo: &mut Foo) {
@@ -95,7 +95,7 @@ fn borrow_mut_and_imm(foo: &mut Foo) {
 }
 
 fn borrow_mut_from_imm(foo: &Foo) {
-    let _bar1 = &mut foo.bar1; //~ ERROR illegal borrow
+    let _bar1 = &mut foo.bar1; //~ ERROR cannot borrow
 }
 
 fn borrow_long_path_both_mut(foo: &mut Foo) {
diff --git a/src/test/compile-fail/borrowck-ref-into-rvalue.rs b/src/test/compile-fail/borrowck-ref-into-rvalue.rs
index 84acd0df20b75..7026f06c2b7bb 100644
--- a/src/test/compile-fail/borrowck-ref-into-rvalue.rs
+++ b/src/test/compile-fail/borrowck-ref-into-rvalue.rs
@@ -10,8 +10,8 @@
 
 fn main() {
     let msg;
-    match Some(~"Hello") { //~ ERROR illegal borrow
-        Some(ref m) => {
+    match Some(~"Hello") {
+        Some(ref m) => { //~ ERROR borrowed value does not live long enough
             msg = m;
         },
         None => { fail!() }
diff --git a/src/test/compile-fail/borrowck-ref-mut-of-imm.rs b/src/test/compile-fail/borrowck-ref-mut-of-imm.rs
index aad86241e9a43..3a37116a1664d 100644
--- a/src/test/compile-fail/borrowck-ref-mut-of-imm.rs
+++ b/src/test/compile-fail/borrowck-ref-mut-of-imm.rs
@@ -11,7 +11,7 @@
 fn destructure(x: Option<int>) -> int {
     match x {
       None => 0,
-      Some(ref mut v) => *v //~ ERROR illegal borrow
+      Some(ref mut v) => *v //~ ERROR cannot borrow
     }
 }
 
diff --git a/src/test/compile-fail/borrowck-unary-move-2.rs b/src/test/compile-fail/borrowck-unary-move-2.rs
index 520772f1ceea9..898830bbe55ba 100644
--- a/src/test/compile-fail/borrowck-unary-move-2.rs
+++ b/src/test/compile-fail/borrowck-unary-move-2.rs
@@ -28,5 +28,5 @@ struct wrapper(noncopyable);
 
 fn main() {
     let x1 = wrapper(noncopyable());
-    let _x2 = *x1; //~ ERROR moving out of anonymous field
+    let _x2 = *x1; //~ ERROR cannot move out
 }
diff --git a/src/test/compile-fail/borrowck-unary-move.rs b/src/test/compile-fail/borrowck-unary-move.rs
index f95b365ee2ef9..107e478004abb 100644
--- a/src/test/compile-fail/borrowck-unary-move.rs
+++ b/src/test/compile-fail/borrowck-unary-move.rs
@@ -9,8 +9,8 @@
 // except according to those terms.
 
 fn foo(+x: ~int) -> int {
-    let y = &*x; //~ NOTE loan of argument granted here
-    free(x); //~ ERROR moving out of argument prohibited due to outstanding loan
+    let y = &*x;
+    free(x); //~ ERROR cannot move out of `*x` because it is borrowed
     *y
 }
 
diff --git a/src/test/compile-fail/borrowck-uniq-via-box.rs b/src/test/compile-fail/borrowck-uniq-via-box.rs
deleted file mode 100644
index 97414ff5e786c..0000000000000
--- a/src/test/compile-fail/borrowck-uniq-via-box.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-struct Rec {
-    f: ~int,
-}
-
-struct Outer {
-    f: Inner
-}
-
-struct Inner {
-    g: Innermost
-}
-
-struct Innermost {
-    h: ~int,
-}
-
-fn borrow(_v: &int) {}
-
-fn box_mut(v: @mut ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure
-}
-
-fn box_mut_rec(v: @mut Rec) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure
-}
-
-fn box_mut_recs(v: @mut Outer) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
-}
-
-fn box_imm(v: @~int) {
-    borrow(*v); // OK
-}
-
-fn box_imm_rec(v: @Rec) {
-    borrow(v.f); // OK
-}
-
-fn box_imm_recs(v: @Outer) {
-    borrow(v.f.g.h); // OK
-}
-
-fn main() {
-}
diff --git a/src/test/compile-fail/borrowck-uniq-via-lend.rs b/src/test/compile-fail/borrowck-uniq-via-lend.rs
index ee96237a26c82..80ba1968bc751 100644
--- a/src/test/compile-fail/borrowck-uniq-via-lend.rs
+++ b/src/test/compile-fail/borrowck-uniq-via-lend.rs
@@ -43,8 +43,8 @@ fn aliased_const() {
 
 fn aliased_mut() {
     let mut v = ~3;
-    let _w = &mut v; //~ NOTE prior loan as mutable granted here
-    borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    let _w = &mut v;
+    borrow(v); //~ ERROR cannot borrow `*v`
 }
 
 fn aliased_other() {
@@ -56,8 +56,8 @@ fn aliased_other() {
 fn aliased_other_reassign() {
     let mut v = ~3, w = ~4;
     let mut _x = &mut w;
-    _x = &mut v; //~ NOTE prior loan as mutable granted here
-    borrow(v); //~ ERROR loan of mutable local variable as immutable conflicts with prior loan
+    _x = &mut v;
+    borrow(v); //~ ERROR cannot borrow `*v`
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-uniq-via-ref.rs b/src/test/compile-fail/borrowck-uniq-via-ref.rs
index 2cf363e13ee09..8bf627d991911 100644
--- a/src/test/compile-fail/borrowck-uniq-via-ref.rs
+++ b/src/test/compile-fail/borrowck-uniq-via-ref.rs
@@ -25,6 +25,7 @@ struct Innermost {
 }
 
 fn borrow(_v: &int) {}
+fn borrow_const(_v: &const int) {}
 
 fn box_mut(v: &mut ~int) {
     borrow(*v); // OK: &mut -> &imm
@@ -51,15 +52,15 @@ fn box_imm_recs(v: &Outer) {
 }
 
 fn box_const(v: &const ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure
+    borrow_const(*v); //~ ERROR unsafe borrow
 }
 
 fn box_const_rec(v: &const Rec) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure
+    borrow_const(v.f); //~ ERROR unsafe borrow
 }
 
 fn box_const_recs(v: &const Outer) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
+    borrow_const(v.f.g.h); //~ ERROR unsafe borrow
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs
index c8a0dbedd5d95..0c21b64bb0fb0 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-element-loan.rs
@@ -1,7 +1,7 @@
 fn a() -> &[int] {
     let vec = [1, 2, 3, 4];
-    let tail = match vec { //~ ERROR illegal borrow
-        [_, ..tail] => tail,
+    let tail = match vec {
+        [_, ..tail] => tail, //~ ERROR does not live long enough
         _ => fail!(~"a")
     };
     tail
@@ -9,8 +9,8 @@ fn a() -> &[int] {
 
 fn b() -> &[int] {
     let vec = [1, 2, 3, 4];
-    let init = match vec { //~ ERROR illegal borrow
-        [..init, _] => init,
+    let init = match vec {
+        [..init, _] => init, //~ ERROR does not live long enough
         _ => fail!(~"b")
     };
     init
@@ -18,8 +18,8 @@ fn b() -> &[int] {
 
 fn c() -> &[int] {
     let vec = [1, 2, 3, 4];
-    let slice = match vec { //~ ERROR illegal borrow
-        [_, ..slice, _] => slice,
+    let slice = match vec {
+        [_, ..slice, _] => slice, //~ ERROR does not live long enough
         _ => fail!(~"c")
     };
     slice
diff --git a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs
index 805b162f1d363..635ce77bb8a5b 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-loan-from-mut.rs
@@ -2,7 +2,7 @@ fn a() {
     let mut v = ~[1, 2, 3];
     match v {
         [_a, ..tail] => {
-            v.push(tail[0] + tail[1]); //~ ERROR conflicts with prior loan
+            v.push(tail[0] + tail[1]); //~ ERROR cannot borrow
         }
         _ => {}
     };
diff --git a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
index 16b48aedb0c7f..2898e312930fe 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs
@@ -1,8 +1,9 @@
 fn main() {
     let mut a = [1, 2, 3, 4];
-    let _ = match a {
+    let t = match a {
         [1, 2, ..tail] => tail,
         _ => core::util::unreachable()
     };
-    a[0] = 0; //~ ERROR: assigning to mutable vec content prohibited due to outstanding loan
+    a[0] = 0; //~ ERROR cannot assign to `a[]` because it is borrowed
+    t[0];
 }
diff --git a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs
index eef99aafd687c..941455d086c8c 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs
@@ -2,7 +2,7 @@ fn a() {
     let mut vec = [~1, ~2, ~3];
     match vec {
         [~ref _a] => {
-            vec[0] = ~4; //~ ERROR prohibited due to outstanding loan
+            vec[0] = ~4; //~ ERROR cannot assign to `vec[]` because it is borrowed
         }
         _ => fail!(~"foo")
     }
@@ -12,7 +12,7 @@ fn b() {
     let mut vec = [~1, ~2, ~3];
     match vec {
         [.._b] => {
-            vec[0] = ~4; //~ ERROR prohibited due to outstanding loan
+            vec[0] = ~4; //~ ERROR cannot assign to `vec[]` because it is borrowed
         }
     }
 }
diff --git a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs
index 714a80def9358..dbdd8f0809a6e 100644
--- a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs
+++ b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs
@@ -1,7 +1,7 @@
 fn a() -> &int {
     let vec = [1, 2, 3, 4];
-    let tail = match vec { //~ ERROR illegal borrow
-        [_a, ..tail] => &tail[0],
+    let tail = match vec {
+        [_a, ..tail] => &tail[0], //~ ERROR borrowed value does not live long enough
         _ => fail!(~"foo")
     };
     tail
diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs
index f9e6bc1b22e8c..451f023f5fcf7 100644
--- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs
+++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-2.rs
@@ -1,5 +1,5 @@
 fn main() {
     let mut b = ~3;
-    let _x = &mut *b;   //~ NOTE prior loan as mutable granted here
-    let _y = &mut *b;   //~ ERROR loan of dereference of mutable ~ pointer as mutable conflicts with prior loan
+    let _x = &mut *b;
+    let _y = &mut *b; //~ ERROR cannot borrow
 }
diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs
index 6c82b25a6a140..c455de888a330 100644
--- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs
+++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail-3.rs
@@ -1,7 +1,7 @@
 fn main() {
     let mut a = ~3;
-    let mut b = &mut a; //~ NOTE loan of mutable local variable granted here
+    let mut b = &mut a;
     let _c = &mut *b;
-    let mut d = /*move*/ a; //~ ERROR moving out of mutable local variable prohibited due to outstanding loan
+    let mut d = /*move*/ a; //~ ERROR cannot move out
     *d += 1;
 }
diff --git a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs
index dc453d981934d..e18808dfe538a 100644
--- a/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs
+++ b/src/test/compile-fail/borrowck-wg-borrow-mut-to-imm-fail.rs
@@ -1,6 +1,6 @@
 fn main() {
     let mut b = ~3;
-    let _x = &mut *b;   //~ NOTE loan of mutable local variable granted here
-    let mut y = /*move*/ b; //~ ERROR moving out of mutable local variable prohibited
+    let _x = &mut *b;
+    let mut y = /*move*/ b; //~ ERROR cannot move out
     *y += 1;
 }
diff --git a/src/test/compile-fail/borrowck-wg-move-base-2.rs b/src/test/compile-fail/borrowck-wg-move-base-2.rs
index 0ede523daa4c6..4050b4c5971a0 100644
--- a/src/test/compile-fail/borrowck-wg-move-base-2.rs
+++ b/src/test/compile-fail/borrowck-wg-move-base-2.rs
@@ -2,7 +2,7 @@ fn foo(x: &mut int) {
     let mut a = 3;
     let mut _y = &mut *x;
     let _z = &mut *_y;
-    _y = &mut a; //~ ERROR assigning to mutable local variable prohibited
+    _y = &mut a; //~ ERROR cannot assign
 }
 
 fn main() {
diff --git a/src/test/compile-fail/dead-code-ret.rs b/src/test/compile-fail/dead-code-ret.rs
index 97f6149b162fc..5fa796db88444 100644
--- a/src/test/compile-fail/dead-code-ret.rs
+++ b/src/test/compile-fail/dead-code-ret.rs
@@ -10,8 +10,12 @@
 // except according to those terms.
 
 
-// error-pattern: dead
+fn f(caller: &str) {
+    debug!(caller);
+    let x: uint = 0u32; // induce type error //~ ERROR mismatched types
+}
 
-fn f(caller: str) { debug!(caller); }
-
-fn main() { return f("main"); debug!("Paul is dead"); }
+fn main() {
+    return f("main");
+    debug!("Paul is dead"); //~ WARNING unreachable
+}
diff --git a/src/test/compile-fail/die-not-static.rs b/src/test/compile-fail/die-not-static.rs
index b30e3942e6330..d33c591d8c87f 100644
--- a/src/test/compile-fail/die-not-static.rs
+++ b/src/test/compile-fail/die-not-static.rs
@@ -1,7 +1,6 @@
-// error-pattern:illegal borrow: borrowed value does not live long enough
-
 fn main() {
     let v = ~"test";
     let sslice = str::slice(v, 0, v.len());
+    //~^ ERROR borrowed value does not live long enough
     fail!(sslice);
 }
diff --git a/src/test/compile-fail/fn-variance-3.rs b/src/test/compile-fail/fn-variance-3.rs
index 5df2007721def..4d145d3f9ea3a 100644
--- a/src/test/compile-fail/fn-variance-3.rs
+++ b/src/test/compile-fail/fn-variance-3.rs
@@ -31,5 +31,5 @@ fn main() {
     // mutability check will fail, because the
     // type of r has been inferred to be
     // fn(@const int) -> @const int
-    *r(@mut 3) = 4; //~ ERROR assigning to dereference of const @ pointer
+    *r(@mut 3) = 4; //~ ERROR cannot assign to const dereference of @ pointer
 }
diff --git a/src/test/compile-fail/for-loop-decl.rs b/src/test/compile-fail/for-loop-decl.rs
deleted file mode 100644
index de28d72677728..0000000000000
--- a/src/test/compile-fail/for-loop-decl.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// error-pattern: mismatched types
-extern mod std;
-use std::bitv;
-use core::hashmap::HashMap;
-
-struct FnInfo {
-    vars: HashMap<uint, VarInfo>
-}
-
-struct VarInfo {
-    a: uint,
-    b: uint,
-}
-
-fn bitv_to_str(enclosing: FnInfo, v: ~bitv::Bitv) -> str {
-    let s = "";
-
-    // error is that the value type in the hash map is var_info, not a box
-    for enclosing.vars.each_value |val| {
-        if *v.get(val) { s += "foo"; }
-    }
-    return s;
-}
-
-fn main() { debug!("OK"); }
diff --git a/src/test/compile-fail/immut-function-arguments.rs b/src/test/compile-fail/immut-function-arguments.rs
index 2084729372d1d..66b5bd172cace 100644
--- a/src/test/compile-fail/immut-function-arguments.rs
+++ b/src/test/compile-fail/immut-function-arguments.rs
@@ -9,11 +9,11 @@
 // except according to those terms.
 
 fn f(y: ~int) {
-    *y = 5; //~ ERROR assigning to dereference of immutable ~ pointer
+    *y = 5; //~ ERROR cannot assign
 }
 
 fn g() {
-    let _frob: &fn(~int) = |q| { *q = 2; }; //~ ERROR assigning to dereference of immutable ~ pointer
+    let _frob: &fn(~int) = |q| { *q = 2; }; //~ ERROR cannot assign
 
 }
 
diff --git a/src/test/compile-fail/index_message.rs b/src/test/compile-fail/index_message.rs
index 3611dbb6866cb..26dd98757a8c2 100644
--- a/src/test/compile-fail/index_message.rs
+++ b/src/test/compile-fail/index_message.rs
@@ -10,5 +10,5 @@
 
 fn main() {
     let z = ();
-    debug!(z[0]); //~ ERROR cannot index a value of type `()`
+    let _ = z[0]; //~ ERROR cannot index a value of type `()`
 }
diff --git a/src/test/compile-fail/issue-1896-1.rs b/src/test/compile-fail/issue-1896-1.rs
index fc5132d65104f..13adcd42da2b8 100644
--- a/src/test/compile-fail/issue-1896-1.rs
+++ b/src/test/compile-fail/issue-1896-1.rs
@@ -8,11 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// Test that we require managed closures to be rooted when borrowed.
+
 struct boxedFn<'self> { theFn: &'self fn() -> uint }
 
 fn createClosure (closedUint: uint) -> boxedFn {
     let theFn: @fn() -> uint = || closedUint;
-    boxedFn {theFn: theFn} //~ ERROR illegal borrow
+    boxedFn {theFn: theFn} //~ ERROR cannot root
 }
 
 fn main () {
diff --git a/src/test/compile-fail/issue-2149.rs b/src/test/compile-fail/issue-2149.rs
index 2842d884c9918..cdc8d546dd848 100644
--- a/src/test/compile-fail/issue-2149.rs
+++ b/src/test/compile-fail/issue-2149.rs
@@ -23,5 +23,4 @@ impl<A> vec_monad<A> for ~[A] {
 fn main() {
     ["hi"].bind(|x| [x] );
     //~^ ERROR type `[&'static str, .. 1]` does not implement any method in scope named `bind`
-    //~^^ ERROR Unconstrained region variable
 }
diff --git a/src/test/compile-fail/issue-2151.rs b/src/test/compile-fail/issue-2151.rs
index e2bbda7d65a99..bb6d47a47622b 100644
--- a/src/test/compile-fail/issue-2151.rs
+++ b/src/test/compile-fail/issue-2151.rs
@@ -10,7 +10,6 @@
 
 fn main() {
     for vec::each(fail!()) |i| {
-        debug!(i * 2);
-        //~^ ERROR the type of this value must be known
+        let _ = i * 2; //~ ERROR the type of this value must be known
    };
 }
diff --git a/src/test/compile-fail/issue-2590.rs b/src/test/compile-fail/issue-2590.rs
index 7a99ab8a94f16..a0b967d59593a 100644
--- a/src/test/compile-fail/issue-2590.rs
+++ b/src/test/compile-fail/issue-2590.rs
@@ -18,7 +18,7 @@ trait parse {
 
 impl parse for parser {
     fn parse(&self) -> ~[int] {
-        self.tokens //~ ERROR moving out of immutable field
+        self.tokens //~ ERROR cannot move out of field
     }
 }
 
diff --git a/src/test/compile-fail/issue-3044.rs b/src/test/compile-fail/issue-3044.rs
index 635d0aa3df1e5..06fb18d7e4777 100644
--- a/src/test/compile-fail/issue-3044.rs
+++ b/src/test/compile-fail/issue-3044.rs
@@ -11,7 +11,6 @@
 fn main() {
     let needlesArr: ~[char] = ~['a', 'f'];
     do vec::foldr(needlesArr) |x, y| {
-        //~^ ERROR Unconstrained region variable #2
     }
     //~^ ERROR 2 parameters were supplied (including the closure passed by the `do` keyword)
     //
diff --git a/src/test/compile-fail/issue-511.rs b/src/test/compile-fail/issue-511.rs
index 90c46e5d602c9..c872f89d88450 100644
--- a/src/test/compile-fail/issue-511.rs
+++ b/src/test/compile-fail/issue-511.rs
@@ -17,5 +17,5 @@ fn f<T:Eq>(o: &mut Option<T>) {
 
 fn main() {
     f::<int>(&mut option::None);
-    //~^ ERROR illegal borrow: creating mutable alias to static item
+    //~^ ERROR cannot borrow
 }
diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs
index 54ee8bcc70e37..6bb90bff228d4 100644
--- a/src/test/compile-fail/kindck-owned-trait-contains.rs
+++ b/src/test/compile-fail/kindck-owned-trait-contains.rs
@@ -29,4 +29,7 @@ fn main() {
     };
     assert!(3 == *(y.get())); //~ ERROR dereference of reference outside its lifetime
     //~^ ERROR reference is not valid outside of its lifetime
+    //~^^ ERROR reference is not valid outside of its lifetime
+    //~^^^ ERROR reference is not valid outside of its lifetime
+    //~^^^^ ERROR cannot infer an appropriate lifetime
 }
diff --git a/src/test/compile-fail/lambda-mutate-nested.rs b/src/test/compile-fail/lambda-mutate-nested.rs
index 8b009b91af96c..bfd1e12f3a6e0 100644
--- a/src/test/compile-fail/lambda-mutate-nested.rs
+++ b/src/test/compile-fail/lambda-mutate-nested.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to captured outer immutable variable in a stack closure
 // Make sure that nesting a block within a @fn doesn't let us
 // mutate upvars from a @fn.
 fn f2(x: &fn()) { x(); }
@@ -16,6 +15,7 @@ fn f2(x: &fn()) { x(); }
 fn main() {
     let i = 0;
     let ctr: @fn() -> int = || { f2(|| i = i + 1 ); i };
+    //~^ ERROR cannot assign
     error!(ctr());
     error!(ctr());
     error!(ctr());
diff --git a/src/test/compile-fail/lambda-mutate.rs b/src/test/compile-fail/lambda-mutate.rs
index ee5b3d8968418..a848d8698a3d6 100644
--- a/src/test/compile-fail/lambda-mutate.rs
+++ b/src/test/compile-fail/lambda-mutate.rs
@@ -8,11 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to captured outer variable in a heap closure
 // Make sure we can't write to upvars from @fns
 fn main() {
     let i = 0;
     let ctr: @fn() -> int = || { i = i + 1; i };
+    //~^ ERROR cannot assign
     error!(ctr());
     error!(ctr());
     error!(ctr());
diff --git a/src/test/compile-fail/moves-based-on-type-block-bad.rs b/src/test/compile-fail/moves-based-on-type-block-bad.rs
index 67eb06ab424c5..76d50710bb8c1 100644
--- a/src/test/compile-fail/moves-based-on-type-block-bad.rs
+++ b/src/test/compile-fail/moves-based-on-type-block-bad.rs
@@ -16,7 +16,7 @@ fn main() {
     let s = S { x: ~Bar(~42) };
     loop {
         do f(&s) |hellothere| {
-            match hellothere.x {    //~ ERROR moving out of immutable field
+            match hellothere.x {    //~ ERROR cannot move out
                 ~Foo(_) => {}
                 ~Bar(x) => io::println(x.to_str()),
                 ~Baz => {}
diff --git a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
index 3c15047a29697..ecd58d485a89d 100644
--- a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
+++ b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs
@@ -13,6 +13,6 @@ fn test(_x: ~uint) {}
 fn main() {
     let i = ~3;
     for uint::range(0, 10) |_x| {
-        test(i); //~ ERROR moving out of captured outer immutable variable in a stack closure
+        test(i); //~ ERROR cannot move out
     }
 }
diff --git a/src/test/compile-fail/mutable-class-fields-2.rs b/src/test/compile-fail/mutable-class-fields-2.rs
index 56c715c9847a5..f5d24b316414e 100644
--- a/src/test/compile-fail/mutable-class-fields-2.rs
+++ b/src/test/compile-fail/mutable-class-fields-2.rs
@@ -8,7 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to immutable field
 struct cat {
   priv mut meows : uint,
 
@@ -17,7 +16,7 @@ struct cat {
 
 pub impl cat {
   fn eat(&self) {
-    self.how_hungry -= 5;
+    self.how_hungry -= 5; //~ ERROR cannot assign
   }
 
 }
diff --git a/src/test/compile-fail/mutable-class-fields.rs b/src/test/compile-fail/mutable-class-fields.rs
index 6d11a98c0cb2f..8bebec7134cc3 100644
--- a/src/test/compile-fail/mutable-class-fields.rs
+++ b/src/test/compile-fail/mutable-class-fields.rs
@@ -8,12 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to immutable field
 struct cat {
   priv mut meows : uint,
-
   how_hungry : int,
-
 }
 
 fn cat(in_x : uint, in_y : int) -> cat {
@@ -25,5 +22,5 @@ fn cat(in_x : uint, in_y : int) -> cat {
 
 fn main() {
   let nyan : cat = cat(52u, 99);
-  nyan.how_hungry = 0;
+  nyan.how_hungry = 0; //~ ERROR cannot assign
 }
diff --git a/src/test/compile-fail/mutable-huh-ptr-assign.rs b/src/test/compile-fail/mutable-huh-ptr-assign.rs
index ed356f4001dd6..6b3fd4f715384 100644
--- a/src/test/compile-fail/mutable-huh-ptr-assign.rs
+++ b/src/test/compile-fail/mutable-huh-ptr-assign.rs
@@ -12,7 +12,7 @@ extern mod std;
 
 fn main() {
     unsafe fn f(&&v: *const int) {
-        *v = 1 //~ ERROR assigning to dereference of const * pointer
+        *v = 1 //~ ERROR cannot assign
     }
 
     unsafe {
diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs
index 7f2140d96e16c..4fff5a6f87c78 100644
--- a/src/test/compile-fail/regions-addr-of-arg.rs
+++ b/src/test/compile-fail/regions-addr-of-arg.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn foo(a: int) {
-    let _p: &'static int = &a; //~ ERROR illegal borrow
+    let _p: &'static int = &a; //~ ERROR borrowed value does not live long enough
 }
 
 fn bar(a: int) {
diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs
index cccd135e9f836..ab2620d46fdc5 100644
--- a/src/test/compile-fail/regions-bounds.rs
+++ b/src/test/compile-fail/regions-bounds.rs
@@ -23,10 +23,8 @@ fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
     return e; //~ ERROR mismatched types: expected `a_class/&'b ` but found `a_class/&'a `
 }
 
-fn a_fn4<'a,'b>(e: int<'a>) -> int<'b> {
-    //~^ ERROR region parameters are not allowed on this type
-    //~^^ ERROR region parameters are not allowed on this type
-    return e;
+fn a_fn4<'a,'b>() {
+    let _: int<'a> = 1; //~ ERROR region parameters are not allowed on this type
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/regions-creating-enums.rs b/src/test/compile-fail/regions-creating-enums.rs
index 120428e02f4cb..2ab0c14b49b65 100644
--- a/src/test/compile-fail/regions-creating-enums.rs
+++ b/src/test/compile-fail/regions-creating-enums.rs
@@ -30,12 +30,12 @@ fn compute(x: &ast) -> uint {
 fn map_nums(x: &ast, f: &fn(uint) -> uint) -> &ast {
     match *x {
       num(x) => {
-        return &num(f(x)); //~ ERROR illegal borrow
+        return &num(f(x)); //~ ERROR borrowed value does not live long enough
       }
       add(x, y) => {
         let m_x = map_nums(x, f);
         let m_y = map_nums(y, f);
-        return &add(m_x, m_y);  //~ ERROR illegal borrow
+        return &add(m_x, m_y);  //~ ERROR borrowed value does not live long enough
       }
     }
 }
diff --git a/src/test/compile-fail/regions-creating-enums4.rs b/src/test/compile-fail/regions-creating-enums4.rs
index 1cb378cf406f8..8f764745697c7 100644
--- a/src/test/compile-fail/regions-creating-enums4.rs
+++ b/src/test/compile-fail/regions-creating-enums4.rs
@@ -14,8 +14,7 @@ enum ast<'self> {
 }
 
 fn mk_add_bad2<'a>(x: &'a ast<'a>, y: &'a ast<'a>, z: &ast) -> ast {
-    add(x, y)
-         //~^ ERROR cannot infer an appropriate lifetime
+    add(x, y) //~ ERROR cannot infer an appropriate lifetime
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-escape-bound-fn.rs b/src/test/compile-fail/regions-escape-bound-fn.rs
index c81ef77f497db..5ac5e334be23d 100644
--- a/src/test/compile-fail/regions-escape-bound-fn.rs
+++ b/src/test/compile-fail/regions-escape-bound-fn.rs
@@ -14,6 +14,6 @@ fn with_int(f: &fn(x: &int)) {
 }
 
 fn main() {
-    let mut x: Option<&int> = None; //~ ERROR cannot infer
+    let mut x: Option<&int> = None;   //~ ERROR cannot infer
     with_int(|y| x = Some(y));
 }
diff --git a/src/test/compile-fail/regions-escape-loop-via-variable.rs b/src/test/compile-fail/regions-escape-loop-via-variable.rs
index ac10b5c454a85..19bd0bf9747bb 100644
--- a/src/test/compile-fail/regions-escape-loop-via-variable.rs
+++ b/src/test/compile-fail/regions-escape-loop-via-variable.rs
@@ -18,6 +18,6 @@ fn main() {
 
     loop {
         let x = 1 + *p;
-        p = &x; //~ ERROR illegal borrow
+        p = &x; //~ ERROR borrowed value does not live long enough
     }
 }
diff --git a/src/test/compile-fail/regions-escape-loop-via-vec.rs b/src/test/compile-fail/regions-escape-loop-via-vec.rs
index da5e3c2660ef7..92e2cd73dfbd8 100644
--- a/src/test/compile-fail/regions-escape-loop-via-vec.rs
+++ b/src/test/compile-fail/regions-escape-loop-via-vec.rs
@@ -14,8 +14,8 @@ fn broken() {
     let mut _y = ~[&mut x];
     while x < 10 {
         let mut z = x;
-        _y.push(&mut z); //~ ERROR illegal borrow
-        x += 1; //~ ERROR assigning to mutable local variable prohibited due to outstanding loan
+        _y.push(&mut z); //~ ERROR borrowed value does not live long enough
+        x += 1; //~ ERROR cannot assign
     }
 }
 
diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs
index f7165784c7975..aa431d6b81c6e 100644
--- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs
+++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs
@@ -23,13 +23,8 @@ fn with<R:deref>(f: &fn(x: &int) -> R) -> int {
 }
 
 fn return_it() -> int {
-    with(|o| o)
-    //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements
-    //~^^ ERROR reference is not valid outside of its lifetime
-    //~^^^ ERROR reference is not valid outside of its lifetime
+    with(|o| o) //~ ERROR reference is not valid outside of its lifetime
 }
 
 fn main() {
-    let x = return_it();
-    debug!("foo=%d", x);
 }
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
index 6ee0216655e92..d519397f68c58 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-too-big.rs
@@ -18,7 +18,7 @@ fn x_coord<'r>(p: &'r point) -> &'r int {
 }
 
 fn foo(p: @point) -> &int {
-    let xc = x_coord(p); //~ ERROR illegal borrow
+    let xc = x_coord(p); //~ ERROR cannot root
     assert!(*xc == 3);
     return xc;
 }
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
index 602f5dc6983c2..50ac5f65772fc 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
@@ -15,9 +15,9 @@ fn foo(cond: &fn() -> bool, box: &fn() -> @int) {
     loop {
         let x = box();
 
-        // Here we complain because the resulting region
-        // of this borrow is the fn body as a whole.
-        y = borrow(x); //~ ERROR illegal borrow: cannot root managed value long enough
+	    // Here we complain because the resulting region
+	    // of this borrow is the fn body as a whole.
+        y = borrow(x); //~ ERROR cannot root
 
         assert!(*x == *y);
         if cond() { break; }
diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs
index 2e9a4eb141037..fe995052c52e4 100644
--- a/src/test/compile-fail/regions-nested-fns-2.rs
+++ b/src/test/compile-fail/regions-nested-fns-2.rs
@@ -13,7 +13,7 @@ fn ignore(_f: &fn<'z>(&'z int) -> &'z int) {}
 fn nested() {
     let y = 3;
     ignore(|z| {
-        if false { &y } else { z } //~ ERROR illegal borrow
+        if false { &y } else { z } //~ ERROR borrowed value does not live long enough
     });
 }
 
diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs
index 3089c362a5044..74399967446ea 100644
--- a/src/test/compile-fail/regions-nested-fns.rs
+++ b/src/test/compile-fail/regions-nested-fns.rs
@@ -16,7 +16,7 @@ fn nested<'x>(x: &'x int) {
 
     ignore::<&fn<'z>(&'z int)>(|z| {
         ay = x;
-        ay = &y;  //~ ERROR cannot infer an appropriate lifetime
+        ay = &y;
         ay = z;
     });
 
diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs
index f916b0d95c2ee..a572d90313b6a 100644
--- a/src/test/compile-fail/regions-ret-borrowed-1.rs
+++ b/src/test/compile-fail/regions-ret-borrowed-1.rs
@@ -18,7 +18,6 @@ fn with<'a, R>(f: &fn(x: &'a int) -> R) -> R {
 
 fn return_it<'a>() -> &'a int {
     with(|o| o) //~ ERROR mismatched types
-        //~^ ERROR reference is not valid outside of its lifetime
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs
index 157b99de9e806..ec9a908ba9876 100644
--- a/src/test/compile-fail/regions-ret-borrowed.rs
+++ b/src/test/compile-fail/regions-ret-borrowed.rs
@@ -21,7 +21,6 @@ fn with<R>(f: &fn(x: &int) -> R) -> R {
 
 fn return_it() -> &int {
     with(|o| o) //~ ERROR mismatched types
-        //~^ ERROR reference is not valid outside of its lifetime
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-ret.rs b/src/test/compile-fail/regions-ret.rs
index cf7cb175bb8a4..eccffb4051e23 100644
--- a/src/test/compile-fail/regions-ret.rs
+++ b/src/test/compile-fail/regions-ret.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn f<'a>(_x : &'a int) -> &'a int {
-    return &3; //~ ERROR illegal borrow
+    return &3; //~ ERROR borrowed value does not live long enough
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-var-type-out-of-scope.rs b/src/test/compile-fail/regions-var-type-out-of-scope.rs
index 7d75ac7434931..addf20fd70249 100644
--- a/src/test/compile-fail/regions-var-type-out-of-scope.rs
+++ b/src/test/compile-fail/regions-var-type-out-of-scope.rs
@@ -14,7 +14,7 @@ fn foo(cond: bool) {
     let mut x;
 
     if cond {
-        x = &3; //~ ERROR illegal borrow: borrowed value does not live long enough
+        x = &3; //~ ERROR borrowed value does not live long enough
         assert!((*x == 3));
     }
 }
diff --git a/src/test/compile-fail/swap-no-lval.rs b/src/test/compile-fail/swap-no-lval.rs
index 4fe30792e4b31..eca5fb0d315d8 100644
--- a/src/test/compile-fail/swap-no-lval.rs
+++ b/src/test/compile-fail/swap-no-lval.rs
@@ -10,6 +10,6 @@
 
 fn main() {
     5 <-> 3;
-    //~^ ERROR swapping to and from non-lvalue
-    //~^^ ERROR swapping to and from non-lvalue
+    //~^ ERROR cannot assign
+    //~^^ ERROR cannot assign
 }
diff --git a/src/test/compile-fail/type-shadow.rs b/src/test/compile-fail/type-shadow.rs
index a9b4a85e6385c..c4a412f64c8d4 100644
--- a/src/test/compile-fail/type-shadow.rs
+++ b/src/test/compile-fail/type-shadow.rs
@@ -9,14 +9,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
-// error-pattern: mismatched types
-
 fn main() {
     type X = int;
     type Y = X;
     if true {
-        type X = str;
-        let y: Y = "hello";
+        type X = &'static str;
+        let y: Y = "hello"; //~ ERROR mismatched types
     }
 }
diff --git a/src/test/compile-fail/writing-to-immutable-vec.rs b/src/test/compile-fail/writing-to-immutable-vec.rs
index 3f4c8ccef8175..faa3d6cfe47e7 100644
--- a/src/test/compile-fail/writing-to-immutable-vec.rs
+++ b/src/test/compile-fail/writing-to-immutable-vec.rs
@@ -8,5 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// error-pattern:assigning to immutable vec content
-fn main() { let v: ~[int] = ~[1, 2, 3]; v[1] = 4; }
+fn main() {
+    let v: ~[int] = ~[1, 2, 3];
+    v[1] = 4; //~ ERROR cannot assign
+}
diff --git a/src/test/run-fail/borrowck-wg-fail-2.rs b/src/test/run-fail/borrowck-wg-fail-2.rs
index 126135772ade0..59a5fecd34003 100644
--- a/src/test/run-fail/borrowck-wg-fail-2.rs
+++ b/src/test/run-fail/borrowck-wg-fail-2.rs
@@ -1,5 +1,8 @@
 // error-pattern:borrowed
 
+// Test that write guards trigger when there is a write to a field
+// of a frozen structure.
+
 struct S {
     x: int
 }
@@ -7,5 +10,6 @@ struct S {
 fn main() {
     let x = @mut S { x: 3 };
     let y: &S = x;
-    x.x = 5;
+    let z = x;
+    z.x = 5;
 }
diff --git a/src/test/run-fail/borrowck-wg-fail-3.rs b/src/test/run-fail/borrowck-wg-fail-3.rs
index b239bfc3b3142..a40faa1ac6fc3 100644
--- a/src/test/run-fail/borrowck-wg-fail-3.rs
+++ b/src/test/run-fail/borrowck-wg-fail-3.rs
@@ -1,7 +1,11 @@
 // error-pattern:borrowed
 
+// Test that write guards trigger when there is a write to a directly
+// frozen @mut box.
+
 fn main() {
     let x = @mut 3;
     let y: &mut int = x;
-    *x = 5;
+    let z = x;
+    *z = 5;
 }
diff --git a/src/test/run-fail/borrowck-wg-fail.rs b/src/test/run-fail/borrowck-wg-fail.rs
index 93e7f9275b6cf..201db14eb17d5 100644
--- a/src/test/run-fail/borrowck-wg-fail.rs
+++ b/src/test/run-fail/borrowck-wg-fail.rs
@@ -1,9 +1,10 @@
 // error-pattern:borrowed
 
-fn f(x: &int, y: @mut int) {
-    unsafe {
-        *y = 2;
-    }
+// Test that write guards trigger when mut box is frozen
+// as part of argument coercion.
+
+fn f(_x: &int, y: @mut int) {
+    *y = 2;
 }
 
 fn main() {
diff --git a/src/test/run-fail/borrowck-wg-imm-then-mut.rs b/src/test/run-fail/borrowck-wg-imm-then-mut.rs
new file mode 100644
index 0000000000000..e2c8a0b549c36
--- /dev/null
+++ b/src/test/run-fail/borrowck-wg-imm-then-mut.rs
@@ -0,0 +1,19 @@
+// error-pattern:borrowed
+
+// Test that if you imm borrow then mut borrow it fails.
+
+fn add1(a:@mut int)
+{
+    add2(a); // already frozen
+}
+
+fn add2(_:&mut int)
+{
+}
+
+pub fn main()
+{
+    let a = @mut 3;
+    let b = &*a; // freezes a
+    add1(a);
+}
diff --git a/src/test/run-fail/borrowck-wg-mut-then-imm.rs b/src/test/run-fail/borrowck-wg-mut-then-imm.rs
new file mode 100644
index 0000000000000..58b2a1d87beed
--- /dev/null
+++ b/src/test/run-fail/borrowck-wg-mut-then-imm.rs
@@ -0,0 +1,19 @@
+// error-pattern:borrowed
+
+// Test that if you mut borrow then imm borrow it fails.
+
+fn add1(a:@mut int)
+{
+    add2(a); // already frozen
+}
+
+fn add2(_:&int)
+{
+}
+
+pub fn main()
+{
+    let a = @mut 3;
+    let b = &mut *a; // freezes a
+    add1(a);
+}
diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs
new file mode 100644
index 0000000000000..91df90f8b3ac9
--- /dev/null
+++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slice-method.rs
@@ -0,0 +1,37 @@
+// error-pattern:borrowed
+
+// Test that write guards trigger when there is a coercion to
+// a slice on the receiver of a method.
+
+trait MyMutSlice {
+    fn my_mut_slice(self) -> Self;
+}
+
+impl<'self, T> MyMutSlice for &'self mut [T] {
+    fn my_mut_slice(self) -> &'self mut [T] {
+        self
+    }
+}
+
+trait MySlice {
+    fn my_slice(self) -> Self;
+}
+
+impl<'self, T> MySlice for &'self [T] {
+    fn my_slice(self) -> &'self [T] {
+        self
+    }
+}
+
+fn add(x:&mut [int], y:&[int])
+{
+    x[0] = x[0] + y[0];
+}
+
+pub fn main()
+{
+    let z = @mut [1,2,3];
+    let z2 = z;
+    add(z.my_mut_slice(), z2.my_slice());
+    print(fmt!("%d\n", z[0]));
+}
diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs
new file mode 100644
index 0000000000000..bae693ce4eae2
--- /dev/null
+++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm-slices.rs
@@ -0,0 +1,16 @@
+// error-pattern:borrowed
+
+// Test that write guards trigger when arguments are coerced to slices.
+
+fn add(x:&mut [int], y:&[int])
+{
+    x[0] = x[0] + y[0];
+}
+
+pub fn main()
+{
+    let z = @mut [1,2,3];
+    let z2 = z;
+    add(z, z2);
+    print(fmt!("%d\n", z[0]));
+}
diff --git a/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs b/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs
new file mode 100644
index 0000000000000..9e2a02b32dfed
--- /dev/null
+++ b/src/test/run-fail/borrowck-wg-one-mut-one-imm.rs
@@ -0,0 +1,17 @@
+// error-pattern:borrowed
+
+// Test that write guards trigger when we are indexing into
+// an @mut vector.
+
+fn add(x:&mut int, y:&int)
+{
+    *x = *x + *y;
+}
+
+pub fn main()
+{
+    let z = @mut [1,2,3];
+    let z2 = z;
+    add(&mut z[0], &z2[0]);
+    print(fmt!("%d\n", z[0]));
+}
diff --git a/src/test/run-fail/borrowck-wg-two-array-indices.rs b/src/test/run-fail/borrowck-wg-two-array-indices.rs
new file mode 100644
index 0000000000000..ad68448876028
--- /dev/null
+++ b/src/test/run-fail/borrowck-wg-two-array-indices.rs
@@ -0,0 +1,17 @@
+// error-pattern:borrowed
+
+// Test that arguments trigger when there are *two mutable* borrows
+// of indices.
+
+fn add(x:&mut int, y:&mut int)
+{
+    *x = *x + *y;
+}
+
+pub fn main()
+{
+    let z = @mut [1,2,3];
+    let z2 = z;
+    add(&mut z[0], &mut z2[0]);
+    print(fmt!("%d\n", z[0]));
+}
diff --git a/src/test/run-pass/auto-ref-slice-plus-ref.rs b/src/test/run-pass/auto-ref-slice-plus-ref.rs
index 1dc56132875d4..58a477900c324 100644
--- a/src/test/run-pass/auto-ref-slice-plus-ref.rs
+++ b/src/test/run-pass/auto-ref-slice-plus-ref.rs
@@ -17,13 +17,13 @@ trait MyIter {
 }
 
 impl<'self> MyIter for &'self [int] {
-    fn test_imm(&self) { assert!(self[0] == 1) }
-    fn test_const(&const self) { assert!(self[0] == 1) }
+    fn test_imm(&self) { assert_eq!(self[0], 1) }
+    fn test_const(&const self) { assert_eq!(self[0], 1) }
 }
 
 impl<'self> MyIter for &'self str {
-    fn test_imm(&self) { assert!(*self == "test") }
-    fn test_const(&const self) { assert!(*self == "test") }
+    fn test_imm(&self) { assert_eq!(*self, "test") }
+    fn test_const(&const self) { assert_eq!(self[0], 't' as u8) }
 }
 
 pub fn main() {
diff --git a/src/test/compile-fail/issue-4500.rs b/src/test/run-pass/borrowck-nested-calls.rs
similarity index 51%
rename from src/test/compile-fail/issue-4500.rs
rename to src/test/run-pass/borrowck-nested-calls.rs
index 356a64498219a..4494f5f2fa337 100644
--- a/src/test/compile-fail/issue-4500.rs
+++ b/src/test/run-pass/borrowck-nested-calls.rs
@@ -8,7 +8,25 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-fn main () {
-    let mut _p: & int = & 4;
-    _p = &*~3; //~ ERROR illegal borrow
+// xfail-test #5074 nested method calls
+
+// Test that (safe) nested calls with `&mut` receivers are permitted.
+
+struct Foo {a: uint, b: uint}
+
+pub impl Foo {
+    fn inc_a(&mut self, v: uint) { self.a += v; }
+
+    fn next_b(&mut self) -> uint {
+        let b = self.b;
+        self.b += 1;
+        b
+    }
+}
+
+fn main() {
+    let mut f = Foo {a: 22, b: 23};
+    f.inc_a(f.next_b());
+    assert_eq!(f.a, 22+23);
+    assert_eq!(f.b, 24);
 }
diff --git a/src/test/run-pass/borrowck-wg-two-imm-borrows.rs b/src/test/run-pass/borrowck-wg-two-imm-borrows.rs
new file mode 100644
index 0000000000000..20f824e969a48
--- /dev/null
+++ b/src/test/run-pass/borrowck-wg-two-imm-borrows.rs
@@ -0,0 +1,14 @@
+// Test that we can borrow the same @mut box twice, so long as both are imm.
+
+fn add(x:&int, y:&int)
+{
+    *x + *y;
+}
+
+pub fn main()
+{
+    let z = @mut [1,2,3];
+    let z2 = z;
+    add(&z[0], &z2[0]);
+    print(fmt!("%d\n", z[0]));
+}
diff --git a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs
index 0e67532d7a1fc..b0d06dae10dc0 100644
--- a/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs
+++ b/src/test/run-pass/coerce-reborrow-mut-vec-rcvr.rs
@@ -1,10 +1,10 @@
 trait Reverser {
-    fn reverse(&self);
+    fn reverse(self);
 }
 
 impl<'self> Reverser for &'self mut [uint] {
-    fn reverse(&self) {
-        vec::reverse(*self);
+    fn reverse(self) {
+        vec::reverse(self);
     }
 }
 
diff --git a/src/test/run-pass/issue-2735-2.rs b/src/test/run-pass/issue-2735-2.rs
index 96f76b0fd6ba6..ca584e1a6e3b8 100644
--- a/src/test/run-pass/issue-2735-2.rs
+++ b/src/test/run-pass/issue-2735-2.rs
@@ -9,27 +9,25 @@
 // except according to those terms.
 
 // This test should behave exactly like issue-2735-3
-struct defer<'self> {
-    b: &'self mut bool,
+struct defer {
+    b: @mut bool,
 }
 
 #[unsafe_destructor]
-impl<'self> Drop for defer<'self> {
+impl Drop for defer {
     fn finalize(&self) {
-        unsafe {
-            *(self.b) = true;
-        }
+        *self.b = true;
     }
 }
 
-fn defer<'r>(b: &'r mut bool) -> defer<'r> {
+fn defer(b: @mut bool) -> defer {
     defer {
         b: b
     }
 }
 
 pub fn main() {
-    let mut dtor_ran = false;
-    let _  = defer(&mut dtor_ran);
-    assert!((dtor_ran));
+    let dtor_ran = @mut false;
+    let _  = defer(dtor_ran);
+    assert!(*dtor_ran);
 }
diff --git a/src/test/run-pass/issue-2735-3.rs b/src/test/run-pass/issue-2735-3.rs
index 50e3c946f50ef..44ca5d6929bd6 100644
--- a/src/test/run-pass/issue-2735-3.rs
+++ b/src/test/run-pass/issue-2735-3.rs
@@ -9,27 +9,25 @@
 // except according to those terms.
 
 // This test should behave exactly like issue-2735-2
-struct defer<'self> {
-    b: &'self mut bool,
+struct defer {
+    b: @mut bool,
 }
 
 #[unsafe_destructor]
-impl<'self> Drop for defer<'self> {
+impl Drop for defer {
     fn finalize(&self) {
-        unsafe {
-            *(self.b) = true;
-        }
+        *self.b = true;
     }
 }
 
-fn defer<'r>(b: &'r mut bool) -> defer<'r> {
+fn defer(b: @mut bool) -> defer {
     defer {
         b: b
     }
 }
 
 pub fn main() {
-    let mut dtor_ran = false;
-    defer(&mut dtor_ran);
-    assert!((dtor_ran));
+    let dtor_ran = @mut false;
+    defer(dtor_ran);
+    assert!(*dtor_ran);
 }
diff --git a/src/test/run-pass/lambda-infer-unresolved.rs b/src/test/run-pass/lambda-infer-unresolved.rs
index 2e70e90038971..4aeeda8312cac 100644
--- a/src/test/run-pass/lambda-infer-unresolved.rs
+++ b/src/test/run-pass/lambda-infer-unresolved.rs
@@ -17,5 +17,5 @@ struct Refs { refs: ~[int], n: int }
 pub fn main() {
     let e = @mut Refs{refs: ~[], n: 0};
     let f: @fn() = || error!(copy e.n);
-    e.refs += ~[1];
+    e.refs.push(1);
 }
diff --git a/src/test/run-pass/reflect-visit-data.rs b/src/test/run-pass/reflect-visit-data.rs
index e520d221c9935..5b01d24aa8be1 100644
--- a/src/test/run-pass/reflect-visit-data.rs
+++ b/src/test/run-pass/reflect-visit-data.rs
@@ -513,16 +513,16 @@ impl TyVisitor for my_visitor {
     fn visit_bot(&self) -> bool { true }
     fn visit_nil(&self) -> bool { true }
     fn visit_bool(&self) -> bool {
-      do self.get::<bool>() |b| {
-            self.vals += ~[bool::to_str(b)];
-      };
-      true
+        do self.get::<bool>() |b| {
+            self.vals.push(bool::to_str(b));
+        };
+        true
     }
     fn visit_int(&self) -> bool {
-      do self.get::<int>() |i| {
-            self.vals += ~[int::to_str(i)];
-      };
-      true
+        do self.get::<int>() |i| {
+            self.vals.push(int::to_str(i));
+        };
+        true
     }
     fn visit_i8(&self) -> bool { true }
     fn visit_i16(&self) -> bool { true }
diff --git a/src/test/run-pass/regions-mock-trans-impls.rs b/src/test/run-pass/regions-mock-trans-impls.rs
deleted file mode 100644
index d54aae7bb6337..0000000000000
--- a/src/test/run-pass/regions-mock-trans-impls.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-// xfail-fast
-
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-extern mod std;
-use core::libc;
-use core::sys;
-use core::cast;
-use std::arena::Arena;
-
-struct Bcx<'self> {
-    fcx: &'self Fcx<'self>
-}
-
-struct Fcx<'self> {
-    arena: &'self mut Arena,
-    ccx: &'self Ccx
-}
-
-struct Ccx {
-    x: int
-}
-
-fn h<'r>(bcx : &'r mut Bcx<'r>) -> &'r mut Bcx<'r> {
-    // XXX: Arena has a bad interface here; it should return mutable pointers.
-    // But this patch is too big to roll that in.
-    unsafe {
-        cast::transmute(bcx.fcx.arena.alloc(|| Bcx { fcx: bcx.fcx }))
-    }
-}
-
-fn g(fcx: &mut Fcx) {
-    let mut bcx = Bcx { fcx: fcx };
-    h(&mut bcx);
-}
-
-fn f(ccx: &mut Ccx) {
-    let mut a = Arena();
-    let mut fcx = Fcx { arena: &mut a, ccx: ccx };
-    return g(&mut fcx);
-}
-
-pub fn main() {
-    let mut ccx = Ccx { x: 0 };
-    f(&mut ccx);
-}