about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/at_vec.rs4
-rw-r--r--src/libcore/core.rc2
-rw-r--r--src/libcore/managed.rs2
-rw-r--r--src/libcore/rt.rs39
-rw-r--r--src/libcore/str.rs16
-rw-r--r--src/libcore/task/spawn.rs8
-rw-r--r--src/libcore/vec.rs24
-rw-r--r--src/libfuzzer/fuzzer.rc2
-rw-r--r--src/librustc/driver/driver.rs5
-rw-r--r--src/librustc/middle/astencode.rs1
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs56
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs14
-rw-r--r--src/librustc/middle/borrowck/loan.rs5
-rw-r--r--src/librustc/middle/borrowck/mod.rs28
-rw-r--r--src/librustc/middle/borrowck/preserve.rs36
-rw-r--r--src/librustc/middle/lang_items.rs23
-rw-r--r--src/librustc/middle/mem_categorization.rs196
-rw-r--r--src/librustc/middle/resolve.rs6
-rw-r--r--src/librustc/middle/trans/_match.rs20
-rw-r--r--src/librustc/middle/trans/base.rs33
-rw-r--r--src/librustc/middle/trans/build.rs56
-rw-r--r--src/librustc/middle/trans/common.rs26
-rw-r--r--src/librustc/middle/trans/datum.rs128
-rw-r--r--src/librustc/middle/trans/expr.rs12
-rw-r--r--src/librustc/middle/trans/shape.rs2
-rw-r--r--src/librustc/middle/ty.rs11
-rw-r--r--src/librustc/middle/typeck/check/mod.rs6
-rw-r--r--src/librustc/middle/typeck/coherence.rs2
-rw-r--r--src/librustc/middle/typeck/infer/region_inference.rs2
-rw-r--r--src/libstd/arc.rs14
-rw-r--r--src/libstd/rope.rs21
-rw-r--r--src/libstd/time.rs46
-rw-r--r--src/libsyntax/parse/comments.rs2
m---------src/libuv0
-rw-r--r--src/test/run-fail/write-guard-fail-2.rs11
-rw-r--r--src/test/run-fail/write-guard-fail-3.rs8
-rw-r--r--src/test/run-fail/write-guard-fail.rs13
-rw-r--r--src/test/run-pass/write-guard.rs9
38 files changed, 523 insertions, 366 deletions
diff --git a/src/libcore/at_vec.rs b/src/libcore/at_vec.rs
index 1b6ac13ceab..1d964edd537 100644
--- a/src/libcore/at_vec.rs
+++ b/src/libcore/at_vec.rs
@@ -216,7 +216,7 @@ pub mod raw {
     }
 
     pub unsafe fn push_slow<T>(v: &mut @[const T], initval: T) {
-        reserve_at_least(v, v.len() + 1u);
+        reserve_at_least(&mut *v, v.len() + 1u);
         push_fast(v, move initval);
     }
 
@@ -234,7 +234,7 @@ pub mod raw {
     pub unsafe fn reserve<T>(v: &mut @[const T], n: uint) {
         // Only make the (slow) call into the runtime if we have to
         if capacity(*v) < n {
-            let ptr: **VecRepr = transmute(copy v);
+            let ptr: **VecRepr = transmute(v);
             rustrt::vec_reserve_shared_actual(sys::get_type_desc::<T>(),
                                               ptr, n as libc::size_t);
         }
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index 81b9077fb3e..b800564b8e1 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -95,8 +95,8 @@ pub mod at_vec;
 pub mod str;
 
 pub mod ptr;
-pub mod managed;
 pub mod owned;
+pub mod managed;
 
 
 /* Core language traits */
diff --git a/src/libcore/managed.rs b/src/libcore/managed.rs
index 8395526276c..32fc5f527b7 100644
--- a/src/libcore/managed.rs
+++ b/src/libcore/managed.rs
@@ -14,7 +14,9 @@
 #[forbid(deprecated_mode)];
 #[forbid(deprecated_pattern)];
 
+use cast::transmute;
 use cmp::{Eq, Ord};
+use managed::raw::BoxRepr;
 use prelude::*;
 use ptr;
 
diff --git a/src/libcore/rt.rs b/src/libcore/rt.rs
index e0eb2fcfd83..c5268e2d4ed 100644
--- a/src/libcore/rt.rs
+++ b/src/libcore/rt.rs
@@ -15,7 +15,9 @@
 #[forbid(deprecated_pattern)];
 //! Runtime calls emitted by the compiler.
 
+use cast::transmute;
 use libc::{c_char, c_void, size_t, uintptr_t};
+use managed::raw::BoxRepr;
 use str;
 use sys;
 
@@ -24,6 +26,11 @@ use gc::{cleanup_stack_for_failure, gc, Word};
 #[allow(non_camel_case_types)]
 pub type rust_task = c_void;
 
+#[cfg(target_word_size = "32")]
+const FROZEN_BIT: uint = 0x80000000;
+#[cfg(target_word_size = "64")]
+const FROZEN_BIT: uint = 0x8000000000000000;
+
 extern mod rustrt {
     #[rust_stack]
     unsafe fn rust_upcall_exchange_malloc(td: *c_char, size: uintptr_t)
@@ -56,6 +63,15 @@ pub unsafe fn rt_fail_bounds_check(file: *c_char, line: size_t,
     }
 }
 
+pub unsafe fn rt_fail_borrowed() {
+    let msg = "borrowed";
+    do str::as_buf(msg) |msg_p, _| {
+        do str::as_buf("???") |file_p, _| {
+            rt_fail_(msg_p as *c_char, file_p as *c_char, 0);
+        }
+    }
+}
+
 #[rt(exchange_malloc)]
 #[lang="exchange_malloc"]
 pub unsafe fn rt_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
@@ -86,6 +102,29 @@ pub unsafe fn rt_free(ptr: *c_char) {
     rustrt::rust_upcall_free(ptr);
 }
 
+#[lang="borrow_as_imm"]
+#[inline(always)]
+pub unsafe fn borrow_as_imm(a: *u8) {
+    let a: *mut BoxRepr = transmute(a);
+    (*a).header.ref_count |= FROZEN_BIT;
+}
+
+#[lang="return_to_mut"]
+#[inline(always)]
+pub unsafe fn return_to_mut(a: *u8) {
+    let a: *mut BoxRepr = transmute(a);
+    (*a).header.ref_count &= !FROZEN_BIT;
+}
+
+#[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 {
+        rt_fail_borrowed();
+    }
+}
+
 // Local Variables:
 // mode: rust;
 // fill-column: 78;
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index f32dc746e12..d1c85d6482d 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -80,7 +80,7 @@ pub fn push_char(s: &mut ~str, ch: char) {
         else { 6u };
         let len = len(*s);
         let new_len = len + nb;
-        reserve_at_least(s, new_len);
+        reserve_at_least(&mut *s, new_len);
         let off = len;
         do as_buf(*s) |buf, _len| {
             let buf: *mut u8 = ::cast::reinterpret_cast(&buf);
@@ -164,7 +164,7 @@ pub fn push_str_no_overallocate(lhs: &mut ~str, rhs: &str) {
     unsafe {
         let llen = lhs.len();
         let rlen = rhs.len();
-        reserve(lhs, llen + rlen);
+        reserve(&mut *lhs, llen + rlen);
         do as_buf(*lhs) |lbuf, _llen| {
             do as_buf(rhs) |rbuf, _rlen| {
                 let dst = ptr::offset(lbuf, llen);
@@ -181,7 +181,7 @@ pub fn push_str(lhs: &mut ~str, rhs: &str) {
     unsafe {
         let llen = lhs.len();
         let rlen = rhs.len();
-        reserve_at_least(lhs, llen + rlen);
+        reserve_at_least(&mut *lhs, llen + rlen);
         do as_buf(*lhs) |lbuf, _llen| {
             do as_buf(rhs) |rbuf, _rlen| {
                 let dst = ptr::offset(lbuf, llen);
@@ -2056,18 +2056,18 @@ pub mod raw {
 
     /// Appends a byte to a string. (Not UTF-8 safe).
     pub unsafe fn push_byte(s: &mut ~str, b: u8) {
-        reserve_at_least(s, s.len() + 1);
+        reserve_at_least(&mut *s, s.len() + 1);
         do as_buf(*s) |buf, len| {
             let buf: *mut u8 = ::cast::reinterpret_cast(&buf);
             *ptr::mut_offset(buf, len) = b;
         }
-        set_len(s, s.len() + 1);
+        set_len(&mut *s, s.len() + 1);
     }
 
     /// Appends a vector of bytes to a string. (Not UTF-8 safe).
     unsafe fn push_bytes(s: &mut ~str, bytes: &[u8]) {
-        reserve_at_least(s, s.len() + bytes.len());
-        for vec::each(bytes) |byte| { push_byte(s, *byte); }
+        reserve_at_least(&mut *s, s.len() + bytes.len());
+        for vec::each(bytes) |byte| { push_byte(&mut *s, *byte); }
     }
 
     /// Removes the last byte from a string and returns it. (Not UTF-8 safe).
@@ -2090,7 +2090,7 @@ pub mod raw {
 
     /// Sets the length of the string and adds the null terminator
     pub unsafe fn set_len(v: &mut ~str, new_len: uint) {
-        let v: **vec::raw::VecRepr = cast::transmute(copy v);
+        let v: **vec::raw::VecRepr = cast::transmute(v);
         let repr: *vec::raw::VecRepr = *v;
         (*repr).unboxed.fill = new_len + 1u;
         let null = ptr::mut_offset(ptr::mut_addr_of(&((*repr).unboxed.data)),
diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs
index 1c5531303e1..28bbeaad35b 100644
--- a/src/libcore/task/spawn.rs
+++ b/src/libcore/task/spawn.rs
@@ -187,7 +187,7 @@ fn each_ancestor(list:        &mut AncestorList,
                 forward_blk:     fn(TaskGroupInner) -> bool,
                 last_generation: uint) -> bool {
         // Need to swap the list out to use it, to appease borrowck.
-        let tmp_list = util::replace(list, AncestorList(None));
+        let tmp_list = util::replace(&mut *list, AncestorList(None));
         let (coalesce_this, early_break) =
             iterate(&tmp_list, bail_opt, forward_blk, last_generation);
         // What should our next ancestor end up being?
@@ -289,7 +289,7 @@ fn each_ancestor(list:        &mut AncestorList,
         fn with_parent_tg<U>(parent_group: &mut Option<TaskGroupArc>,
                              blk: fn(TaskGroupInner) -> U) -> U {
             // If this trips, more likely the problem is 'blk' failed inside.
-            let tmp_arc = option::swap_unwrap(parent_group);
+            let tmp_arc = option::swap_unwrap(&mut *parent_group);
             let result = do access_group(&tmp_arc) |tg_opt| { blk(tg_opt) };
             *parent_group = move Some(move tmp_arc);
             move result
@@ -363,7 +363,7 @@ fn AutoNotify(chan: Chan<TaskResult>) -> AutoNotify {
 
 fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task,
                            is_member: bool) -> bool {
-    let newstate = util::replace(state, None);
+    let newstate = util::replace(&mut *state, None);
     // If 'None', the group was failing. Can't enlist.
     if newstate.is_some() {
         let group = option::unwrap(move newstate);
@@ -379,7 +379,7 @@ fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task,
 // NB: Runs in destructor/post-exit context. Can't 'fail'.
 fn leave_taskgroup(state: TaskGroupInner, me: *rust_task,
                        is_member: bool) {
-    let newstate = util::replace(state, None);
+    let newstate = util::replace(&mut *state, None);
     // If 'None', already failing and we've already gotten a kill signal.
     if newstate.is_some() {
         let group = option::unwrap(move newstate);
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 49a0e5b2109..1caf569e77d 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -472,7 +472,7 @@ pub fn shift<T>(v: &mut ~[T]) -> T unsafe {
     assert capacity(v) >= ln;
     // Pretend like we have the original length so we can use
     // the vector copy_memory to overwrite the hole we just made
-    raw::set_len(v, ln);
+    raw::set_len(&mut *v, ln);
 
     // Memcopy the head element (the one we want) to the location we just
     // popped. For the moment it unsafely exists at both the head and last
@@ -487,7 +487,7 @@ pub fn shift<T>(v: &mut ~[T]) -> T unsafe {
     raw::copy_memory(init_slice, tail_slice, next_ln);
 
     // Set the new length. Now the vector is back to normal
-    raw::set_len(v, next_ln);
+    raw::set_len(&mut *v, next_ln);
 
     // Swap out the element we want from the end
     let vp = raw::to_mut_ptr(*v);
@@ -592,7 +592,7 @@ pub fn swap_remove<T>(v: &mut ~[T], index: uint) -> T {
 #[inline(always)]
 pub fn push<T>(v: &mut ~[T], initval: T) {
     unsafe {
-        let repr: **raw::VecRepr = ::cast::transmute(copy v);
+        let repr: **raw::VecRepr = ::cast::transmute(&mut *v);
         let fill = (**repr).unboxed.fill;
         if (**repr).unboxed.alloc > fill {
             push_fast(v, initval);
@@ -616,30 +616,30 @@ unsafe fn push_fast<T>(v: &mut ~[T], initval: T) {
 
 #[inline(never)]
 fn push_slow<T>(v: &mut ~[T], initval: T) {
-    reserve_at_least(v, v.len() + 1u);
+    reserve_at_least(&mut *v, v.len() + 1u);
     unsafe { push_fast(v, initval) }
 }
 
 #[inline(always)]
 pub fn push_all<T: Copy>(v: &mut ~[T], rhs: &[const T]) {
-    reserve(v, v.len() + rhs.len());
+    reserve(&mut *v, v.len() + rhs.len());
 
     for uint::range(0u, rhs.len()) |i| {
-        push(v, unsafe { raw::get(rhs, i) })
+        push(&mut *v, unsafe { raw::get(rhs, i) })
     }
 }
 
 #[inline(always)]
 pub fn push_all_move<T>(v: &mut ~[T], rhs: ~[T]) {
     let mut rhs = rhs; // FIXME(#3488)
-    reserve(v, v.len() + rhs.len());
+    reserve(&mut *v, v.len() + rhs.len());
     unsafe {
         do as_mut_buf(rhs) |p, len| {
             for uint::range(0, len) |i| {
                 // FIXME #4204 Should be rusti::uninit() - don't need to zero
                 let mut x = rusti::init();
                 x <-> *ptr::mut_offset(p, i);
-                push(v, x);
+                push(&mut *v, x);
             }
         }
         raw::set_len(&mut rhs, 0);
@@ -657,7 +657,7 @@ pub fn truncate<T>(v: &mut ~[T], newlen: uint) {
                 let mut dropped = rusti::init();
                 dropped <-> *ptr::mut_offset(p, i);
             }
-            raw::set_len(v, newlen);
+            raw::set_len(&mut *v, newlen);
         }
     }
 }
@@ -731,7 +731,7 @@ pub pure fn append_mut<T: Copy>(lhs: ~[mut T], rhs: &[const T]) -> ~[mut T] {
  * * initval - The value for the new elements
  */
 pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
-    reserve_at_least(v, v.len() + n);
+    reserve_at_least(&mut *v, v.len() + n);
     let mut i: uint = 0u;
 
     while i < n {
@@ -754,7 +754,7 @@ pub fn grow<T: Copy>(v: &mut ~[T], n: uint, initval: &T) {
  *             value
  */
 pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<T>) {
-    reserve_at_least(v, v.len() + n);
+    reserve_at_least(&mut *v, v.len() + n);
     let mut i: uint = 0u;
     while i < n {
         v.push(op(i));
@@ -772,7 +772,7 @@ pub fn grow_fn<T>(v: &mut ~[T], n: uint, op: iter::InitOp<T>) {
  */
 pub fn grow_set<T: Copy>(v: &mut ~[T], index: uint, initval: &T, val: T) {
     let l = v.len();
-    if index >= l { grow(v, index - l + 1u, initval); }
+    if index >= l { grow(&mut *v, index - l + 1u, initval); }
     v[index] = val;
 }
 
diff --git a/src/libfuzzer/fuzzer.rc b/src/libfuzzer/fuzzer.rc
index fd36c1d7701..3f37b6d1794 100644
--- a/src/libfuzzer/fuzzer.rc
+++ b/src/libfuzzer/fuzzer.rc
@@ -69,7 +69,7 @@ fn find_rust_files(files: &mut ~[Path], path: &Path) {
         && !contains(path.to_str(), ~"compile-fail")
         && !contains(path.to_str(), ~"build") {
         for os::list_dir_path(path).each |p| {
-            find_rust_files(files, *p);
+            find_rust_files(&mut *files, *p);
         }
     }
 }
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index bc30924322e..740cc526310 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -291,7 +291,7 @@ fn compile_upto(sess: Session, cfg: ast::crate_cfg,
             time(time_passes, ~"liveness checking", ||
                  middle::liveness::check_crate(ty_cx, method_map, crate));
 
-        let (root_map, mutbl_map) =
+        let (root_map, mutbl_map, write_guard_map) =
             time(time_passes, ~"borrow checking", ||
                  middle::borrowck::check_crate(ty_cx, method_map,
                                                last_use_map, crate));
@@ -308,7 +308,8 @@ fn compile_upto(sess: Session, cfg: ast::crate_cfg,
                     root_map: root_map,
                     last_use_map: last_use_map,
                     method_map: method_map,
-                    vtable_map: vtable_map};
+                    vtable_map: vtable_map,
+                    write_guard_map: write_guard_map};
 
         time(time_passes, ~"translation", ||
              trans::base::trans_crate(sess, crate, ty_cx,
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 0edce246dbd..09b6904faa3 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -60,6 +60,7 @@ type maps = {
     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,
 };
 
 type decode_ctxt = @{
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 3d5bdac596b..5635d5d18e5 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -21,8 +21,8 @@ use core::prelude::*;
 
 use middle::borrowck::{Loan, bckerr, borrowck_ctxt, cmt, inherent_mutability};
 use middle::borrowck::{req_maps, save_and_restore};
-use middle::mem_categorization::{cat_arg, cat_binding, cat_deref, cat_local};
-use middle::mem_categorization::{cat_rvalue, cat_special};
+use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
+use middle::mem_categorization::{cat_local, cat_rvalue, cat_special, gc_ptr};
 use middle::mem_categorization::{loan_path, lp_arg, lp_comp, lp_deref};
 use middle::mem_categorization::{lp_local};
 use middle::ty::{CopyValue, MoveValue, ReadValue};
@@ -54,6 +54,7 @@ enum check_loan_ctxt = @{
 };
 
 // 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,
@@ -64,26 +65,6 @@ enum purity_cause {
     pc_cmt(bckerr)
 }
 
-impl purity_cause : cmp::Eq {
-    pure fn eq(&self, other: &purity_cause) -> bool {
-        match (*self) {
-            pc_pure_fn => {
-                match (*other) {
-                    pc_pure_fn => true,
-                    _ => false
-                }
-            }
-            pc_cmt(ref e0a) => {
-                match (*other) {
-                    pc_cmt(ref e0b) => (*e0a) == (*e0b),
-                    _ => false
-                }
-            }
-        }
-    }
-    pure fn ne(&self, other: &purity_cause) -> bool { !(*self).eq(other) }
-}
-
 fn check_loans(bccx: borrowck_ctxt,
                req_maps: req_maps,
                crate: @ast::crate) {
@@ -100,18 +81,12 @@ fn check_loans(bccx: borrowck_ctxt,
     visit::visit_crate(*crate, clcx, vt);
 }
 
+#[deriving_eq]
 enum assignment_type {
     at_straight_up,
     at_swap
 }
 
-impl assignment_type : cmp::Eq {
-    pure fn eq(&self, other: &assignment_type) -> bool {
-        ((*self) as uint) == ((*other) as uint)
-    }
-    pure fn ne(&self, other: &assignment_type) -> bool { !(*self).eq(other) }
-}
-
 impl assignment_type {
     fn checked_by_liveness() -> bool {
         // the liveness pass guarantees that immutable local variables
@@ -410,6 +385,29 @@ impl check_loan_ctxt {
         }
 
         self.bccx.add_to_mutbl_map(cmt);
+
+        // Check for and insert write guards as necessary.
+        self.add_write_guards_if_necessary(cmt);
+    }
+
+    fn add_write_guards_if_necessary(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 = { id: base.id, derefs: deref_count };
+                        self.bccx.write_guard_map.insert(key, ());
+                    }
+                    _ => {}
+                }
+            }
+            cat_comp(base, _) => {
+                self.add_write_guards_if_necessary(base);
+            }
+            _ => {}
+        }
     }
 
     fn check_for_loan_conflicting_with_assignment(
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index 52cbd1da10c..30ed831c53f 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -454,10 +454,16 @@ impl gather_loan_ctxt {
             let e = {cmt: cmt,
                      code: err_mutbl(req_mutbl)};
             if req_mutbl == m_imm {
-                // you can treat mutable things as imm if you are pure
-                debug!("imm required, must be pure");
-
-                Ok(pc_if_pure(e))
+                // if this is an @mut box, then it's generally OK to borrow as
+                // &imm; this will result in a write guard
+                if cmt.cat.is_mutable_box() {
+                    Ok(pc_ok)
+                } else {
+                    // you can treat mutable things as imm if you are pure
+                    debug!("imm required, must be pure");
+
+                    Ok(pc_if_pure(e))
+                }
             } else {
                 Err(e)
             }
diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs
index 92a3ce24589..80e5dacae41 100644
--- a/src/librustc/middle/borrowck/loan.rs
+++ b/src/librustc/middle/borrowck/loan.rs
@@ -19,7 +19,8 @@ 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};
 use middle::mem_categorization::{cat_special, cat_stack_upvar, comp_field};
-use middle::mem_categorization::{comp_index, comp_variant, region_ptr};
+use middle::mem_categorization::{comp_index, comp_variant, gc_ptr};
+use middle::mem_categorization::{region_ptr};
 use middle::ty;
 use util::common::indenter;
 
@@ -162,7 +163,7 @@ impl LoanContext {
             self.loan_unstable_deref(cmt, cmt_base, req_mutbl)
           }
           cat_deref(_, _, unsafe_ptr) |
-          cat_deref(_, _, gc_ptr) |
+          cat_deref(_, _, gc_ptr(_)) |
           cat_deref(_, _, region_ptr(_)) => {
             // Aliased data is simply not lendable.
             self.bccx.tcx.sess.span_bug(
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 19fc9eb175d..91ea70f507e 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -263,13 +263,15 @@ pub mod preserve;
 fn check_crate(tcx: ty::ctxt,
                method_map: typeck::method_map,
                last_use_map: liveness::last_use_map,
-               crate: @ast::crate) -> (root_map, mutbl_map) {
+               crate: @ast::crate)
+            -> (root_map, mutbl_map, write_guard_map) {
 
     let bccx = borrowck_ctxt_(@{tcx: tcx,
                                 method_map: method_map,
                                 last_use_map: last_use_map,
                                 root_map: root_map(),
                                 mutbl_map: HashMap(),
+                                write_guard_map: HashMap(),
                                 mut loaned_paths_same: 0,
                                 mut loaned_paths_imm: 0,
                                 mut stable_paths: 0,
@@ -293,7 +295,7 @@ fn check_crate(tcx: ty::ctxt,
                          make_stat(bccx, bccx.req_pure_paths)));
     }
 
-    return (bccx.root_map, bccx.mutbl_map);
+    return (bccx.root_map, bccx.mutbl_map, bccx.write_guard_map);
 
     fn make_stat(bccx: borrowck_ctxt, stat: uint) -> ~str {
         let stat_f = stat as float;
@@ -310,6 +312,7 @@ type borrowck_ctxt_ = {tcx: ty::ctxt,
                        last_use_map: liveness::last_use_map,
                        root_map: root_map,
                        mutbl_map: mutbl_map,
+                       write_guard_map: write_guard_map,
 
                        // Statistics:
                        mut loaned_paths_same: uint,
@@ -322,10 +325,17 @@ enum borrowck_ctxt {
     borrowck_ctxt_(@borrowck_ctxt_)
 }
 
+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.
-type root_map = HashMap<root_map_key, ast::node_id>;
+type root_map = HashMap<root_map_key, RootInfo>;
 
 // the keys to the root map combine the `id` of the expression with
 // the number of types that it is autodereferenced.  So, for example,
@@ -338,6 +348,10 @@ type root_map_key = {id: ast::node_id, derefs: uint};
 // this is used in trans for optimization purposes.
 type mutbl_map = HashMap<ast::node_id, ()>;
 
+// A set containing IDs of expressions of gc'd type that need to have a write
+// guard.
+type write_guard_map = HashMap<root_map_key, ()>;
+
 // Errors that can occur"]
 enum bckerr_code {
     err_mut_uniq,
@@ -447,14 +461,6 @@ impl root_map_key : to_bytes::IterBytes {
 
 fn root_map() -> root_map {
     return HashMap();
-
-    pure fn root_map_key_eq(k1: &root_map_key, k2: &root_map_key) -> bool {
-        k1.id == k2.id && k1.derefs == k2.derefs
-    }
-
-    pure fn root_map_key_hash(k: &root_map_key) -> uint {
-        (k.id << 4) as uint | k.derefs
-    }
 }
 
 // ___________________________________________________________________________
diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs
index eb4d8614761..429df1a6aa5 100644
--- a/src/librustc/middle/borrowck/preserve.rs
+++ b/src/librustc/middle/borrowck/preserve.rs
@@ -15,13 +15,14 @@
 
 use core::prelude::*;
 
-use middle::borrowck::{bckerr, bckerr_code, bckres, borrowck_ctxt, cmt};
-use middle::borrowck::{err_mut_uniq, err_mut_variant, err_out_of_root_scope};
-use middle::borrowck::{err_out_of_scope, err_root_not_permitted};
+use middle::borrowck::{RootInfo, bckerr, bckerr_code, bckres, borrowck_ctxt};
+use middle::borrowck::{cmt, 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};
 use middle::mem_categorization::{cat_arg, cat_binding, cat_comp, cat_deref};
 use middle::mem_categorization::{cat_discr, cat_local, cat_special};
 use middle::mem_categorization::{cat_stack_upvar, comp_field, comp_index};
-use middle::mem_categorization::{comp_variant, region_ptr};
+use middle::mem_categorization::{comp_variant, gc_ptr, region_ptr};
 use middle::ty;
 use util::common::indenter;
 
@@ -180,14 +181,16 @@ priv impl &preserve_ctxt {
             // Unsafe pointers are the user's problem
             Ok(pc_ok)
           }
-          cat_deref(base, derefs, gc_ptr) => {
+          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 = %?", self.bccx.mut_to_str(base.mutbl));
-            if base.mutbl == m_imm {
+            if cmt.cat.derefs_through_mutable_box() {
+                self.attempt_root(cmt, base, derefs)
+            } else if base.mutbl == m_imm {
                 let non_rooting_ctxt =
                     preserve_ctxt({root_managed_data: false,.. **self});
                 match (&non_rooting_ctxt).preserve(base) {
@@ -326,8 +329,10 @@ priv impl &preserve_ctxt {
     /// 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(cmt: cmt, base: cmt,
-                    derefs: uint) -> bckres<preserve_condition> {
+    fn attempt_root(cmt: cmt,
+                    base: cmt,
+                    derefs: uint)
+                 -> bckres<preserve_condition> {
         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
@@ -352,21 +357,26 @@ priv impl &preserve_ctxt {
             if self.bccx.is_subregion_of(self.scope_region, root_region) {
                 debug!("Elected to root");
                 let rk = {id: base.id, derefs: derefs};
-                self.bccx.root_map.insert(rk, 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_id,
+                    freezes: cmt.cat.derefs_through_mutable_box()
+                });
                 return Ok(pc_ok);
             } else {
                 debug!("Unable to root");
                 return Err({cmt:cmt,
-                         code:err_out_of_root_scope(root_region,
-                                                    self.scope_region)});
+                            code:err_out_of_root_scope(root_region,
+                                                       self.scope_region)});
             }
           }
 
           // we won't be able to root long enough
           _ => {
               return Err({cmt:cmt,
-                       code:err_out_of_root_scope(root_region,
-                                                  self.scope_region)});
+                          code:err_out_of_root_scope(root_region,
+                                                     self.scope_region)});
           }
 
         }
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index ee9d96147e7..d70fcd1ae8a 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -71,16 +71,19 @@ pub enum LangItem {
     ExchangeFreeFnLangItem,     // 27
     MallocFnLangItem,           // 28
     FreeFnLangItem,             // 29
+    BorrowAsImmFnLangItem,      // 30
+    ReturnToMutFnLangItem,      // 31
+    CheckNotBorrowedFnLangItem, // 32
 }
 
 struct LanguageItems {
-    items: [ Option<def_id> * 30 ]
+    items: [ Option<def_id> * 33 ]
 }
 
 impl LanguageItems {
     static pub fn new() -> LanguageItems {
         LanguageItems {
-            items: [ None, ..30 ]
+            items: [ None, ..33 ]
         }
     }
 
@@ -127,6 +130,9 @@ impl LanguageItems {
             27 => "exchange_free",
             28 => "malloc",
             29 => "free",
+            30 => "borrow_as_imm",
+            31 => "return_to_mut",
+            32 => "check_not_borrowed",
 
             _ => "???"
         }
@@ -228,6 +234,15 @@ impl LanguageItems {
     pub fn free_fn(&const self) -> def_id {
         self.items[FreeFnLangItem as uint].get()
     }
+    pub fn borrow_as_imm_fn(&const self) -> def_id {
+        self.items[BorrowAsImmFnLangItem as uint].get()
+    }
+    pub fn return_to_mut_fn(&const self) -> def_id {
+        self.items[ReturnToMutFnLangItem as uint].get()
+    }
+    pub fn check_not_borrowed_fn(&const self) -> def_id {
+        self.items[CheckNotBorrowedFnLangItem as uint].get()
+    }
 }
 
 fn LanguageItemCollector(crate: @crate,
@@ -270,6 +285,10 @@ fn LanguageItemCollector(crate: @crate,
     item_refs.insert(~"exchange_free", ExchangeFreeFnLangItem as uint);
     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(~"return_to_mut", ReturnToMutFnLangItem as uint);
+    item_refs.insert(~"check_not_borrowed",
+                     CheckNotBorrowedFnLangItem as uint);
 
     LanguageItemCollector {
         crate: crate,
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 13e559e6ce4..3919c6bc8f1 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -62,6 +62,7 @@ use syntax::ast;
 use syntax::codemap::span;
 use syntax::print::pprust;
 
+#[deriving_eq]
 enum categorization {
     cat_rvalue,                     // result of eval'ing some misc expr
     cat_special(special_kind),      //
@@ -74,111 +75,18 @@ enum categorization {
     cat_discr(cmt, ast::node_id),   // match discriminant (see preserve())
 }
 
-impl categorization : cmp::Eq {
-    pure fn eq(&self, other: &categorization) -> bool {
-        match (*self) {
-            cat_rvalue => {
-                match (*other) {
-                    cat_rvalue => true,
-                    _ => false
-                }
-            }
-            cat_special(e0a) => {
-                match (*other) {
-                    cat_special(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            cat_local(e0a) => {
-                match (*other) {
-                    cat_local(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            cat_binding(e0a) => {
-                match (*other) {
-                    cat_binding(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            cat_arg(e0a) => {
-                match (*other) {
-                    cat_arg(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            cat_stack_upvar(e0a) => {
-                match (*other) {
-                    cat_stack_upvar(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            cat_deref(e0a, e1a, e2a) => {
-                match (*other) {
-                    cat_deref(e0b, e1b, e2b) =>
-                        e0a == e0b && e1a == e1b && e2a == e2b,
-                    _ => false
-                }
-            }
-            cat_comp(e0a, e1a) => {
-                match (*other) {
-                    cat_comp(e0b, e1b) => e0a == e0b && e1a == e1b,
-                    _ => false
-                }
-            }
-            cat_discr(e0a, e1a) => {
-                match (*other) {
-                    cat_discr(e0b, e1b) => e0a == e0b && e1a == e1b,
-                    _ => false
-                }
-            }
-        }
-    }
-    pure fn ne(&self, other: &categorization) -> bool { !(*self).eq(other) }
-}
-
 // different kinds of pointers:
+#[deriving_eq]
 pub enum ptr_kind {
     uniq_ptr,
-    gc_ptr,
+    gc_ptr(ast::mutability),
     region_ptr(ty::Region),
     unsafe_ptr
 }
 
-impl ptr_kind : cmp::Eq {
-    pure fn eq(&self, other: &ptr_kind) -> bool {
-        match (*self) {
-            uniq_ptr => {
-                match (*other) {
-                    uniq_ptr => true,
-                    _ => false
-                }
-            }
-            gc_ptr => {
-                match (*other) {
-                    gc_ptr => true,
-                    _ => false
-                }
-            }
-            region_ptr(e0a) => {
-                match (*other) {
-                    region_ptr(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            unsafe_ptr => {
-                match (*other) {
-                    unsafe_ptr => true,
-                    _ => false
-                }
-            }
-        }
-    }
-    pure fn ne(&self, other: &ptr_kind) -> bool { !(*self).eq(other) }
-}
-
 // I am coining the term "components" to mean "pieces of a data
 // structure accessible without a dereference":
+#[deriving_eq]
 pub enum comp_kind {
     comp_tuple,                  // elt in a tuple
     comp_anon_field,             // anonymous field (in e.g.
@@ -190,45 +98,8 @@ pub enum comp_kind {
                ast::mutability)  // mutability of vec content
 }
 
-impl comp_kind : cmp::Eq {
-    pure fn eq(&self, other: &comp_kind) -> bool {
-        match (*self) {
-            comp_tuple => {
-                match (*other) {
-                    comp_tuple => true,
-                    _ => false
-                }
-            }
-            comp_anon_field => {
-                match (*other) {
-                    comp_anon_field => true,
-                    _ => false
-                }
-            }
-            comp_variant(e0a) => {
-                match (*other) {
-                    comp_variant(e0b) => e0a == e0b,
-                    _ => false
-                }
-            }
-            comp_field(e0a, e1a) => {
-                match (*other) {
-                    comp_field(e0b, e1b) => e0a == e0b && e1a == e1b,
-                    _ => false
-                }
-            }
-            comp_index(e0a, e1a) => {
-                match (*other) {
-                    comp_index(e0b, e1b) => e0a == e0b && e1a == e1b,
-                    _ => false
-                }
-            }
-        }
-    }
-    pure fn ne(&self, other: &comp_kind) -> bool { !(*self).eq(other) }
-}
-
 // different kinds of expressions we might evaluate
+#[deriving_eq]
 enum special_kind {
     sk_method,
     sk_static_item,
@@ -237,13 +108,6 @@ enum special_kind {
     sk_heap_upvar
 }
 
-impl special_kind : cmp::Eq {
-    pure fn eq(&self, other: &special_kind) -> bool {
-        ((*self) as uint) == ((*other) as uint)
-    }
-    pure fn ne(&self, other: &special_kind) -> bool { !(*self).eq(other) }
-}
-
 // 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.
@@ -339,14 +203,17 @@ fn opt_deref_kind(t: ty::t) -> Option<deref_kind> {
         Some(deref_ptr(region_ptr((*f).meta.region)))
       }
 
-      ty::ty_box(*) |
-      ty::ty_evec(_, ty::vstore_box) |
+      ty::ty_box(mt) |
+      ty::ty_evec(mt, ty::vstore_box) => {
+        Some(deref_ptr(gc_ptr(mt.mutbl)))
+      }
+
       ty::ty_estr(ty::vstore_box) => {
-        Some(deref_ptr(gc_ptr))
+        Some(deref_ptr(gc_ptr(ast::m_imm)))
       }
 
       ty::ty_fn(ref f) if (*f).meta.proto == ast::ProtoBox => {
-        Some(deref_ptr(gc_ptr))
+        Some(deref_ptr(gc_ptr(ast::m_imm)))
       }
 
       ty::ty_ptr(*) => {
@@ -764,7 +631,7 @@ impl &mem_categorization_ctxt {
                     // not loanable.
                     match ptr {
                         uniq_ptr => {Some(@lp_deref(*l, ptr))}
-                        gc_ptr | region_ptr(_) | unsafe_ptr => {None}
+                        gc_ptr(*) | region_ptr(_) | unsafe_ptr => {None}
                     }
                 };
 
@@ -774,7 +641,7 @@ impl &mem_categorization_ctxt {
                     uniq_ptr => {
                         self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
                     }
-                    gc_ptr | region_ptr(_) | unsafe_ptr => {
+                    gc_ptr(*) | region_ptr(_) | unsafe_ptr => {
                         mt.mutbl
                     }
                 };
@@ -820,7 +687,7 @@ impl &mem_categorization_ctxt {
               uniq_ptr => {
                 self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
               }
-              gc_ptr | region_ptr(_) | unsafe_ptr => {
+              gc_ptr(_) | region_ptr(_) | unsafe_ptr => {
                 mt.mutbl
               }
             };
@@ -1027,7 +894,7 @@ impl &mem_categorization_ctxt {
     fn ptr_sigil(ptr: ptr_kind) -> ~str {
         match ptr {
           uniq_ptr => ~"~",
-          gc_ptr => ~"@",
+          gc_ptr(_) => ~"@",
           region_ptr(_) => ~"&",
           unsafe_ptr => ~"*"
         }
@@ -1160,3 +1027,34 @@ fn field_mutbl(tcx: ty::ctxt,
 
     return None;
 }
+
+impl categorization {
+    fn derefs_through_mutable_box(&const self) -> bool {
+        match *self {
+            cat_deref(_, _, gc_ptr(ast::m_mutbl)) => {
+                true
+            }
+            cat_deref(subcmt, _, _) |
+            cat_comp(subcmt, _) |
+            cat_discr(subcmt, _) |
+            cat_stack_upvar(subcmt) => {
+                subcmt.cat.derefs_through_mutable_box()
+            }
+            cat_rvalue |
+            cat_special(*) |
+            cat_local(*) |
+            cat_binding(*) |
+            cat_arg(*) => {
+                false
+            }
+        }
+    }
+
+    fn is_mutable_box(&const self) -> bool {
+        match *self {
+            cat_deref(_, _, gc_ptr(ast::m_mutbl)) => true,
+            _ => false
+        }
+    }
+}
+
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 0f1b2ee7fc4..4418ecfef2c 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -3466,12 +3466,12 @@ impl Resolver {
         for module_.children.each_ref |ident, namebindings| {
             debug!("(computing exports) maybe export '%s'",
                    self.session.str_of(*ident));
-            self.add_exports_of_namebindings(exports2,
+            self.add_exports_of_namebindings(&mut *exports2,
                                              *ident,
                                              *namebindings,
                                              TypeNS,
                                              false);
-            self.add_exports_of_namebindings(exports2,
+            self.add_exports_of_namebindings(&mut *exports2,
                                              *ident,
                                              *namebindings,
                                              ValueNS,
@@ -3489,7 +3489,7 @@ impl Resolver {
                     Some(target) => {
                         debug!("(computing exports) maybe reexport '%s'",
                                self.session.str_of(*ident));
-                        self.add_exports_of_namebindings(exports2,
+                        self.add_exports_of_namebindings(&mut *exports2,
                                                          *ident,
                                                          target.bindings,
                                                          *ns,
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index a059a2bfa90..63ccefbce0e 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -901,26 +901,32 @@ fn collect_record_or_struct_fields(bcx: block, m: &[@Match], col: uint) ->
     }
 }
 
-fn root_pats_as_necessary(bcx: block, m: &[@Match],
-                          col: uint, val: ValueRef)
-{
+fn root_pats_as_necessary(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;
 
         match bcx.ccx().maps.root_map.find({id:pat_id, derefs:0u}) {
             None => (),
-            Some(scope_id) => {
+            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: FromLvalue};
-                datum.root(bcx, scope_id);
-                return; // if we kept going, we'd only re-root the same value
+                bcx = datum.root(bcx, root_info);
+                // If we kept going, we'd only re-root the same value, so
+                // return now.
+                return bcx;
             }
         }
     }
+    return bcx;
 }
 
 // Macro for deciding whether any of the remaining matches fit a given kind of
@@ -1243,7 +1249,7 @@ fn compile_submatch(bcx: block,
         if pat_id == 0 { pat_id = br.pats[col].id; }
     }
 
-    root_pats_as_necessary(bcx, m, col, val);
+    bcx = root_pats_as_necessary(bcx, m, col, val);
 
     let rec_fields = collect_record_or_struct_fields(bcx, m, col);
     if rec_fields.len() > 0 {
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 9d363416be3..7838a963aaf 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -40,6 +40,7 @@ use lib;
 use metadata::common::link_meta;
 use metadata::{csearch, cstore, decoder, encoder};
 use middle::astencode;
+use middle::borrowck::RootInfo;
 use middle::pat_util::*;
 use middle::resolve;
 use middle::trans::_match;
@@ -963,15 +964,28 @@ fn get_landing_pad(bcx: block) -> BasicBlockRef {
 // 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.
-fn add_root_cleanup(bcx: block, scope_id: ast::node_id,
-                    root_loc: ValueRef, ty: ty::t) {
-
-    debug!("add_root_cleanup(bcx=%s, scope_id=%d, root_loc=%s, ty=%s)",
-           bcx.to_str(), scope_id, val_str(bcx.ccx().tn, root_loc),
+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, scope_id);
-    add_clean_temp_mem(bcx_scope, root_loc, 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;
@@ -1262,7 +1276,8 @@ fn trans_block_cleanups_(bcx: block,
 // In the last argument, Some(block) mean jump to this block, and none means
 // this is a landing pad and leaving should be accomplished with a resume
 // instruction.
-fn cleanup_and_leave(bcx: block, upto: Option<BasicBlockRef>,
+fn cleanup_and_leave(bcx: block,
+                     upto: Option<BasicBlockRef>,
                      leave: Option<BasicBlockRef>) {
     let _icx = bcx.insn_ctxt("cleanup_and_leave");
     let mut cur = bcx, bcx = bcx;
@@ -1992,7 +2007,7 @@ fn trans_enum_def(ccx: @crate_ctxt, enum_definition: ast::enum_def,
                                degen,
                                path,
                                vi,
-                               i);
+                               &mut *i);
             }
         }
     }
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index dcf1b488d6f..beaf3b68617 100644
--- a/src/librustc/middle/trans/build.rs
+++ b/src/librustc/middle/trans/build.rs
@@ -17,6 +17,7 @@ use lib::llvm::{ValueRef, TypeRef, BasicBlockRef, BuilderRef, ModuleRef};
 use libc::{c_uint, c_int};
 use middle::trans::common::*;
 
+use core::cast::transmute;
 use core::cast;
 use core::libc;
 use core::str;
@@ -24,6 +25,18 @@ use core::vec;
 use std::map::HashMap;
 use syntax::codemap;
 
+fn terminate(cx: block, _: &str) {
+    unsafe {
+        cx.terminated = true;
+    }
+}
+
+fn check_not_terminated(cx: block) {
+    if cx.terminated {
+        fail ~"already terminated!";
+    }
+}
+
 fn B(cx: block) -> BuilderRef {
     unsafe {
         let b = cx.fcx.ccx.builder.B;
@@ -86,8 +99,8 @@ fn count_insn(cx: block, category: &str) {
 fn RetVoid(cx: block) {
     unsafe {
         if cx.unreachable { return; }
-        assert (!cx.terminated);
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "RetVoid");
         count_insn(cx, "retvoid");
         llvm::LLVMBuildRetVoid(B(cx));
     }
@@ -96,8 +109,8 @@ fn RetVoid(cx: block) {
 fn Ret(cx: block, V: ValueRef) {
     unsafe {
         if cx.unreachable { return; }
-        assert (!cx.terminated);
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "Ret");
         count_insn(cx, "ret");
         llvm::LLVMBuildRet(B(cx), V);
     }
@@ -105,8 +118,8 @@ fn Ret(cx: block, V: ValueRef) {
 
 fn AggregateRet(cx: block, RetVals: ~[ValueRef]) {
     if cx.unreachable { return; }
-    assert (!cx.terminated);
-    cx.terminated = true;
+    check_not_terminated(cx);
+    terminate(cx, "AggregateRet");
     unsafe {
         llvm::LLVMBuildAggregateRet(B(cx), vec::raw::to_ptr(RetVals),
                                     RetVals.len() as c_uint);
@@ -116,8 +129,8 @@ fn AggregateRet(cx: block, RetVals: ~[ValueRef]) {
 fn Br(cx: block, Dest: BasicBlockRef) {
     unsafe {
         if cx.unreachable { return; }
-        assert (!cx.terminated);
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "Br");
         count_insn(cx, "br");
         llvm::LLVMBuildBr(B(cx), Dest);
     }
@@ -127,8 +140,8 @@ fn CondBr(cx: block, If: ValueRef, Then: BasicBlockRef,
           Else: BasicBlockRef) {
     unsafe {
         if cx.unreachable { return; }
-        assert (!cx.terminated);
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "CondBr");
         count_insn(cx, "condbr");
         llvm::LLVMBuildCondBr(B(cx), If, Then, Else);
     }
@@ -138,8 +151,8 @@ fn Switch(cx: block, V: ValueRef, Else: BasicBlockRef, NumCases: uint)
     -> ValueRef {
     unsafe {
         if cx.unreachable { return _Undef(V); }
-        assert !cx.terminated;
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "Switch");
         return llvm::LLVMBuildSwitch(B(cx), V, Else, NumCases as c_uint);
     }
 }
@@ -154,8 +167,8 @@ fn AddCase(S: ValueRef, OnVal: ValueRef, Dest: BasicBlockRef) {
 fn IndirectBr(cx: block, Addr: ValueRef, NumDests: uint) {
     unsafe {
         if cx.unreachable { return; }
-        assert (!cx.terminated);
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "IndirectBr");
         count_insn(cx, "indirectbr");
         llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests as c_uint);
     }
@@ -171,8 +184,8 @@ fn noname() -> *libc::c_char unsafe {
 fn Invoke(cx: block, Fn: ValueRef, Args: ~[ValueRef],
           Then: BasicBlockRef, Catch: BasicBlockRef) {
     if cx.unreachable { return; }
-    assert (!cx.terminated);
-    cx.terminated = true;
+    check_not_terminated(cx);
+    terminate(cx, "Invoke");
     debug!("Invoke(%s with arguments (%s))",
            val_str(cx.ccx().tn, Fn),
            str::connect(vec::map(Args, |a| val_str(cx.ccx().tn, *a)),
@@ -188,8 +201,8 @@ fn Invoke(cx: block, Fn: ValueRef, Args: ~[ValueRef],
 fn FastInvoke(cx: block, Fn: ValueRef, Args: ~[ValueRef],
               Then: BasicBlockRef, Catch: BasicBlockRef) {
     if cx.unreachable { return; }
-    assert (!cx.terminated);
-    cx.terminated = true;
+    check_not_terminated(cx);
+    terminate(cx, "FastInvoke");
     unsafe {
         count_insn(cx, "fastinvoke");
         let v = llvm::LLVMBuildInvoke(B(cx), Fn, vec::raw::to_ptr(Args),
@@ -985,7 +998,8 @@ fn Trap(cx: block) {
 fn LandingPad(cx: block, Ty: TypeRef, PersFn: ValueRef,
               NumClauses: uint) -> ValueRef {
     unsafe {
-        assert !cx.terminated && !cx.unreachable;
+        check_not_terminated(cx);
+        assert !cx.unreachable;
         count_insn(cx, "landingpad");
         return llvm::LLVMBuildLandingPad(B(cx), Ty, PersFn,
                                       NumClauses as c_uint, noname());
@@ -1001,8 +1015,8 @@ fn SetCleanup(cx: block, LandingPad: ValueRef) {
 
 fn Resume(cx: block, Exn: ValueRef) -> ValueRef {
     unsafe {
-        assert (!cx.terminated);
-        cx.terminated = true;
+        check_not_terminated(cx);
+        terminate(cx, "Resume");
         count_insn(cx, "resume");
         return llvm::LLVMBuildResume(B(cx), Exn);
     }
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 2af96f17f7d..f943ee0773d 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -32,6 +32,7 @@ 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::meth;
 use middle::trans::reachable;
@@ -442,6 +443,31 @@ fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
         scope_clean_changed(scope_info);
     }
 }
+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),
+           ty_to_str(bcx.ccx().tcx, t));
+    let {root, rooted} = root_for_cleanup(bcx, val, t);
+    let cleanup_type = cleanup_type(bcx.tcx(), t);
+    do in_scope_cx(bcx) |scope_info| {
+        scope_info.cleanups.push(
+            clean_temp(val, |bcx| {
+                let bcx = callee::trans_rtcall_or_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));
+        scope_clean_changed(scope_info);
+    }
+}
 fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
     let free_fn = match heap {
       heap_shared => {
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index b94d0658597..8c024b364c3 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -98,6 +98,7 @@
 use core::prelude::*;
 
 use lib::llvm::ValueRef;
+use middle::borrowck::RootInfo;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
@@ -534,7 +535,7 @@ impl Datum {
         }
     }
 
-    fn root(bcx: block, scope_id: ast::node_id) {
+    fn root(bcx: block, root_info: RootInfo) -> block {
         /*!
          *
          * In some cases, borrowck will decide that an @T/@[]/@str
@@ -542,18 +543,48 @@ impl Datum {
          * case, we will call this function, which will stash a copy
          * away until we exit the scope `scope_id`. */
 
-        debug!("root(scope_id=%?, self=%?)",
-               scope_id, self.to_str(bcx.ccx()));
+        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", scope_id));
+                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);
-        base::add_root_cleanup(bcx, scope_id, scratch.val, scratch.ty);
+        base::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_rtcall_or_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(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_rtcall_or_lang_call(
+            bcx,
+            bcx.tcx().lang_items.check_not_borrowed_fn(),
+            ~[ PointerCast(bcx, llval, T_ptr(T_i8())) ],
+            expr::Ignore)
     }
 
     fn drop_val(bcx: block) -> block {
@@ -610,7 +641,7 @@ impl Datum {
         expr_id: ast::node_id, // id of expr being deref'd
         derefs: uint,          // number of times deref'd already
         is_auto: bool)         // if true, only deref if auto-derefable
-        -> Option<Datum>
+        -> (Option<Datum>, block)
     {
         let ccx = bcx.ccx();
 
@@ -621,32 +652,39 @@ impl Datum {
         // root the autoderef'd value, if necessary:
         //
         // (Note: root'd values are always boxes)
-        match ccx.maps.root_map.find({id:expr_id, derefs:derefs}) {
-            None => (),
-            Some(scope_id) => {
-                self.root(bcx, scope_id);
-            }
-        }
+        let 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 = match ccx.maps.write_guard_map.find(key) {
+            None => bcx,
+            Some(_) => self.perform_write_guard(bcx)
+        };
 
         match ty::get(self.ty).sty {
             ty::ty_box(_) | ty::ty_uniq(_) => {
-                return Some(self.box_body(bcx));
+                return (Some(self.box_body(bcx)), bcx);
             }
             ty::ty_ptr(mt) => {
                 if is_auto { // unsafe ptrs are not AUTO-derefable
-                    return None;
+                    return (None, bcx);
                 } else {
-                    return Some(deref_ptr(bcx, &self, mt.ty));
+                    return (Some(deref_ptr(bcx, &self, mt.ty)), bcx);
                 }
             }
             ty::ty_rptr(_, mt) => {
-                return Some(deref_ptr(bcx, &self, mt.ty));
+                return (Some(deref_ptr(bcx, &self, mt.ty)), bcx);
             }
             ty::ty_enum(did, ref substs) => {
                 // Check whether this enum is a newtype enum:
                 let variants = ty::enum_variants(ccx.tcx, did);
                 if (*variants).len() != 1 || variants[0].args.len() != 1 {
-                    return None;
+                    return (None, bcx);
                 }
 
                 let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]);
@@ -655,12 +693,15 @@ impl Datum {
                         // Recast lv.val as a pointer to the newtype
                         // rather than a ptr to the enum type.
                         let llty = T_ptr(type_of::type_of(ccx, ty));
-                        Some(Datum {
-                            val: PointerCast(bcx, self.val, llty),
-                            ty: ty,
-                            mode: ByRef,
-                            source: FromLvalue
-                        })
+                        (
+                            Some(Datum {
+                                val: PointerCast(bcx, self.val, llty),
+                                ty: ty,
+                                mode: ByRef,
+                                source: FromLvalue
+                            }),
+                            bcx
+                        )
                     }
                     ByValue => {
                         // Actually, this case cannot happen right
@@ -672,7 +713,7 @@ impl Datum {
                         // code in place here to do the right
                         // thing if this change ever goes through.
                         assert ty::type_is_immediate(ty);
-                        Some(Datum {ty: ty, ..self})
+                        (Some(Datum {ty: ty, ..self}), bcx)
                     }
                 };
             }
@@ -681,7 +722,7 @@ impl Datum {
                 let fields = ty::struct_fields(ccx.tcx, did, substs);
                 if fields.len() != 1 || fields[0].ident !=
                     special_idents::unnamed_field {
-                    return None;
+                    return (None, bcx);
                 }
 
                 let ty = fields[0].mt.ty;
@@ -691,12 +732,15 @@ impl Datum {
                         // than a pointer to the struct type.
                         // XXX: This isn't correct for structs with
                         // destructors.
-                        Some(Datum {
-                            val: GEPi(bcx, self.val, [0, 0, 0]),
-                            ty: ty,
-                            mode: ByRef,
-                            source: FromLvalue
-                        })
+                        (
+                            Some(Datum {
+                                val: GEPi(bcx, self.val, [0, 0, 0]),
+                                ty: ty,
+                                mode: ByRef,
+                                source: FromLvalue
+                            }),
+                            bcx
+                        )
                     }
                     ByValue => {
                         // Actually, this case cannot happen right now,
@@ -707,12 +751,12 @@ impl Datum {
                         // code in place here to do the right thing if this
                         // change ever goes through.
                         assert ty::type_is_immediate(ty);
-                        Some(Datum {ty: ty, ..self})
+                        (Some(Datum {ty: ty, ..self}), bcx)
                     }
                 }
             }
             _ => { // not derefable.
-                return None;
+                return (None, bcx);
             }
         }
 
@@ -728,10 +772,11 @@ impl Datum {
 
     fn deref(bcx: block,
              expr: @ast::expr,  // the expression whose value is being deref'd
-             derefs: uint) -> Datum {
+             derefs: uint)
+          -> DatumBlock {
         match self.try_deref(bcx, expr.id, derefs, false) {
-            Some(lvres) => lvres,
-            None => {
+            (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres },
+            (None, _) => {
                 bcx.ccx().sess.span_bug(
                     expr.span, ~"Cannot deref this expression");
             }
@@ -740,7 +785,8 @@ impl Datum {
 
     fn autoderef(bcx: block,
                  expr_id: ast::node_id,
-                 max: uint) -> Datum {
+                 max: uint)
+              -> DatumBlock {
         let _icx = bcx.insn_ctxt("autoderef");
 
         debug!("autoderef(expr_id=%d, max=%?, self=%?)",
@@ -749,12 +795,14 @@ impl Datum {
 
         let mut datum = self;
         let mut derefs = 0u;
+        let mut bcx = bcx;
         while derefs < max {
             derefs += 1u;
             match datum.try_deref(bcx, expr_id, derefs, true) {
-                None => break,
-                Some(datum_deref) => {
+                (None, new_bcx) => { bcx = new_bcx; break }
+                (Some(datum_deref), new_bcx) => {
                     datum = datum_deref;
+                    bcx = new_bcx;
                 }
             }
         }
@@ -763,7 +811,7 @@ impl Datum {
         // in which case we should have, or we asked to deref as many
         // times as we can
         assert derefs == max || max == uint::max_value;
-        datum
+        DatumBlock { bcx: bcx, datum: datum }
     }
 
     fn get_base_and_len(bcx: block) -> (ValueRef, ValueRef) {
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 4c8bb91df4c..a6f4b960aa7 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -202,7 +202,10 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
             });
 
             if adj.autoderefs > 0 {
-                datum = datum.autoderef(bcx, expr.id, adj.autoderefs);
+                let DatumBlock { bcx: new_bcx, datum: new_datum } =
+                    datum.autoderef(bcx, expr.id, adj.autoderefs);
+                datum = new_datum;
+                bcx = new_bcx;
             }
 
             datum = match adj.autoref {
@@ -755,8 +758,8 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
     // 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 = {id:expr.id, derefs:0u};
-    for bcx.ccx().maps.root_map.find(root_key).each |scope_id| {
-        unrooted_datum.root(bcx, *scope_id);
+    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};
@@ -779,8 +782,7 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
             }
             ast::expr_unary(ast::deref, base) => {
                 let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-                let derefdatum = basedatum.deref(bcx, base, 0);
-                return DatumBlock {bcx: bcx, datum: derefdatum};
+                return basedatum.deref(bcx, base, 0);
             }
             _ => {
                 bcx.tcx().sess.span_bug(
diff --git a/src/librustc/middle/trans/shape.rs b/src/librustc/middle/trans/shape.rs
index b6f2a00f520..3403df6c1cc 100644
--- a/src/librustc/middle/trans/shape.rs
+++ b/src/librustc/middle/trans/shape.rs
@@ -74,7 +74,7 @@ fn add_u16(dest: &mut ~[u8], val: u16) {
 }
 
 fn add_substr(dest: &mut ~[u8], src: ~[u8]) {
-    add_u16(dest, vec::len(src) as u16);
+    add_u16(&mut *dest, vec::len(src) as u16);
     *dest += src;
 }
 
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index b84fbd8a381..fee2224d3f9 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -2206,8 +2206,15 @@ fn type_kind_ext(cx: ctxt, ty: t, allow_ty_var: bool) -> Kind {
       ty_rptr(re_static, mt) =>
       kind_safe_for_default_mode() | mutable_type_kind(cx, mt),
 
-      // General region pointers are copyable but NOT owned nor sendable
-      ty_rptr(_, _) => kind_safe_for_default_mode(),
+      ty_rptr(_, mt) => {
+        if mt.mutbl == ast::m_mutbl {
+            // Mutable region pointers are noncopyable
+            kind_noncopyable()
+        } else {
+            // General region pointers are copyable but NOT owned nor sendable
+            kind_safe_for_default_mode()
+        }
+      }
 
       // Unique boxes and vecs have the kind of their contained type,
       // but unique boxes can't be implicitly copyable.
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 2205eb77ef0..899c30bddcd 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -2720,9 +2720,9 @@ fn check_enum_variants(ccx: @crate_ctxt,
                              sp,
                              /*bad*/copy vs,
                              id,
-                             disr_vals,
-                             disr_val,
-                             variants);
+                             &mut *disr_vals,
+                             &mut *disr_val,
+                             &mut *variants);
                 }
             }
 
diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs
index c95e9ee08b1..83a6fac04d8 100644
--- a/src/librustc/middle/typeck/coherence.rs
+++ b/src/librustc/middle/typeck/coherence.rs
@@ -721,7 +721,7 @@ impl CoherenceChecker {
                 debug!(
                     "(creating impl) adding provided method `%s` to impl",
                     sess.str_of(provided_method.method_info.ident));
-                vec::push(all_methods, provided_method.method_info);
+                vec::push(&mut *all_methods, provided_method.method_info);
             }
         }
 
diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs
index 54854bed65e..1113029a137 100644
--- a/src/librustc/middle/typeck/infer/region_inference.rs
+++ b/src/librustc/middle/typeck/infer/region_inference.rs
@@ -1278,7 +1278,7 @@ impl RegionVarBindings {
 
         return (move graph);
 
-        fn insert_edge(graph: &mut Graph,
+        fn insert_edge(+graph: &mut Graph,
                        node_id: RegionVid,
                        edge_dir: Direction,
                        edge_idx: uint) {
diff --git a/src/libstd/arc.rs b/src/libstd/arc.rs
index e48dc9acaeb..4d1a8af0b1d 100644
--- a/src/libstd/arc.rs
+++ b/src/libstd/arc.rs
@@ -430,9 +430,9 @@ impl<T: Const Owned> &RWWriteMode<T> {
     /// Access the pre-downgrade RWARC in write mode.
     fn write<U>(blk: fn(x: &mut T) -> U) -> U {
         match *self {
-            RWWriteMode((data, ref token, _)) => {
+            RWWriteMode((ref data, ref token, _)) => {
                 do token.write {
-                    blk(data)
+                    blk(&mut **data)
                 }
             }
         }
@@ -440,12 +440,14 @@ impl<T: Const Owned> &RWWriteMode<T> {
     /// Access the pre-downgrade RWARC in write mode with a condvar.
     fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/Condvar) -> U) -> U {
         match *self {
-            RWWriteMode((data, ref token, ref poison)) => {
+            RWWriteMode((ref data, ref token, ref poison)) => {
                 do token.write_cond |cond| {
                     let cvar = Condvar {
-                        is_mutex: false, failed: poison.failed,
-                        cond: cond };
-                    blk(data, &cvar)
+                        is_mutex: false,
+                        failed: &mut *poison.failed,
+                        cond: cond
+                    };
+                    blk(&mut **data, &cvar)
                 }
             }
         }
diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs
index 153f3956884..d478970ca9f 100644
--- a/src/libstd/rope.rs
+++ b/src/libstd/rope.rs
@@ -1322,7 +1322,12 @@ mod tests {
     fn of_string2() {
         let buf = @ mut ~"1234567890";
         let mut i = 0;
-        while i < 10 { *buf = *buf + *buf; i+=1;}
+        while i < 10 {
+            let a = *buf;
+            let b = *buf;
+            *buf = a + b;
+            i+=1;
+        }
         let sample = @*buf;
         let r      = of_str(sample);
         assert char_len(r) == str::char_len(*sample);
@@ -1353,7 +1358,12 @@ mod tests {
     fn iter1() {
         let buf = @ mut ~"1234567890";
         let mut i = 0;
-        while i < 10 { *buf = *buf + *buf; i+=1;}
+        while i < 10 {
+            let a = *buf;
+            let b = *buf;
+            *buf = a + b;
+            i+=1;
+        }
         let sample = @*buf;
         let r      = of_str(sample);
 
@@ -1374,7 +1384,12 @@ mod tests {
         let init = @~"1234567890";
         let buf  = @mut * init;
         let mut i = 0;
-        while i < 8 { *buf = *buf + *buf; i+=1;}
+        while i < 8 {
+            let a = *buf;
+            let b = *buf;
+            *buf = a + b;
+            i+=1;
+        }
         let sample = @*buf;
         let r1     = of_str(sample);
         let mut r2 = of_str(init);
diff --git a/src/libstd/time.rs b/src/libstd/time.rs
index 8cc6d0245a1..d77e02f37b9 100644
--- a/src/libstd/time.rs
+++ b/src/libstd/time.rs
@@ -402,22 +402,22 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
             None => Err(~"Invalid year")
           },
           'c' => {
-            parse_type(s, pos, 'a', tm)
+            parse_type(s, pos, 'a', &mut *tm)
                 .chain(|pos| parse_char(s, pos, ' '))
-                .chain(|pos| parse_type(s, pos, 'b', tm))
+                .chain(|pos| parse_type(s, pos, 'b', &mut *tm))
                 .chain(|pos| parse_char(s, pos, ' '))
-                .chain(|pos| parse_type(s, pos, 'e', tm))
+                .chain(|pos| parse_type(s, pos, 'e', &mut *tm))
                 .chain(|pos| parse_char(s, pos, ' '))
-                .chain(|pos| parse_type(s, pos, 'T', tm))
+                .chain(|pos| parse_type(s, pos, 'T', &mut *tm))
                 .chain(|pos| parse_char(s, pos, ' '))
-                .chain(|pos| parse_type(s, pos, 'Y', tm))
+                .chain(|pos| parse_type(s, pos, 'Y', &mut *tm))
           }
           'D' | 'x' => {
-            parse_type(s, pos, 'm', tm)
+            parse_type(s, pos, 'm', &mut *tm)
                 .chain(|pos| parse_char(s, pos, '/'))
-                .chain(|pos| parse_type(s, pos, 'd', tm))
+                .chain(|pos| parse_type(s, pos, 'd', &mut *tm))
                 .chain(|pos| parse_char(s, pos, '/'))
-                .chain(|pos| parse_type(s, pos, 'y', tm))
+                .chain(|pos| parse_type(s, pos, 'y', &mut *tm))
           }
           'd' => match match_digits(s, pos, 2u, false) {
             Some(item) => { let (v, pos) = item; tm.tm_mday = v; Ok(pos) }
@@ -428,11 +428,11 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
             None => Err(~"Invalid day of the month")
           },
           'F' => {
-            parse_type(s, pos, 'Y', tm)
+            parse_type(s, pos, 'Y', &mut *tm)
                 .chain(|pos| parse_char(s, pos, '-'))
-                .chain(|pos| parse_type(s, pos, 'm', tm))
+                .chain(|pos| parse_type(s, pos, 'm', &mut *tm))
                 .chain(|pos| parse_char(s, pos, '-'))
-                .chain(|pos| parse_type(s, pos, 'd', tm))
+                .chain(|pos| parse_type(s, pos, 'd', &mut *tm))
           }
           'H' => {
             // FIXME (#2350): range check.
@@ -513,18 +513,18 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
             None => Err(~"Invalid hour")
           },
           'R' => {
-            parse_type(s, pos, 'H', tm)
+            parse_type(s, pos, 'H', &mut *tm)
                 .chain(|pos| parse_char(s, pos, ':'))
-                .chain(|pos| parse_type(s, pos, 'M', tm))
+                .chain(|pos| parse_type(s, pos, 'M', &mut *tm))
           }
           'r' => {
-            parse_type(s, pos, 'I', tm)
+            parse_type(s, pos, 'I', &mut *tm)
                 .chain(|pos| parse_char(s, pos, ':'))
-                .chain(|pos| parse_type(s, pos, 'M', tm))
+                .chain(|pos| parse_type(s, pos, 'M', &mut *tm))
                 .chain(|pos| parse_char(s, pos, ':'))
-                .chain(|pos| parse_type(s, pos, 'S', tm))
+                .chain(|pos| parse_type(s, pos, 'S', &mut *tm))
                 .chain(|pos| parse_char(s, pos, ' '))
-                .chain(|pos| parse_type(s, pos, 'p', tm))
+                .chain(|pos| parse_type(s, pos, 'p', &mut *tm))
           }
           'S' => {
             // FIXME (#2350): range check.
@@ -539,11 +539,11 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
           }
           //'s' {}
           'T' | 'X' => {
-            parse_type(s, pos, 'H', tm)
+            parse_type(s, pos, 'H', &mut *tm)
                 .chain(|pos| parse_char(s, pos, ':'))
-                .chain(|pos| parse_type(s, pos, 'M', tm))
+                .chain(|pos| parse_type(s, pos, 'M', &mut *tm))
                 .chain(|pos| parse_char(s, pos, ':'))
-                .chain(|pos| parse_type(s, pos, 'S', tm))
+                .chain(|pos| parse_type(s, pos, 'S', &mut *tm))
           }
           't' => parse_char(s, pos, '\t'),
           'u' => {
@@ -558,11 +558,11 @@ priv fn do_strptime(s: &str, format: &str) -> Result<Tm, ~str> {
             }
           }
           'v' => {
-            parse_type(s, pos, 'e', tm)
+            parse_type(s, pos, 'e', &mut *tm)
                 .chain(|pos|  parse_char(s, pos, '-'))
-                .chain(|pos| parse_type(s, pos, 'b', tm))
+                .chain(|pos| parse_type(s, pos, 'b', &mut *tm))
                 .chain(|pos| parse_char(s, pos, '-'))
-                .chain(|pos| parse_type(s, pos, 'Y', tm))
+                .chain(|pos| parse_type(s, pos, 'Y', &mut *tm))
           }
           //'W' {}
           'w' => {
diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs
index 0e101d54ba3..66c3111b309 100644
--- a/src/libsyntax/parse/comments.rs
+++ b/src/libsyntax/parse/comments.rs
@@ -160,7 +160,7 @@ fn consume_whitespace_counting_blank_lines(rdr: string_reader,
                                            comments: &mut ~[cmnt]) {
     while is_whitespace(rdr.curr) && !is_eof(rdr) {
         if rdr.col == CharPos(0u) && rdr.curr == '\n' {
-            push_blank_line_comment(rdr, comments);
+            push_blank_line_comment(rdr, &mut *comments);
         }
         bump(rdr);
     }
diff --git a/src/libuv b/src/libuv
-Subproject 4d392c86feb6389f550d8110d36fa90d66c0925
+Subproject 1170ffba3ac5191930b40c897d4569a9d8a296a
diff --git a/src/test/run-fail/write-guard-fail-2.rs b/src/test/run-fail/write-guard-fail-2.rs
new file mode 100644
index 00000000000..126135772ad
--- /dev/null
+++ b/src/test/run-fail/write-guard-fail-2.rs
@@ -0,0 +1,11 @@
+// error-pattern:borrowed
+
+struct S {
+    x: int
+}
+
+fn main() {
+    let x = @mut S { x: 3 };
+    let y: &S = x;
+    x.x = 5;
+}
diff --git a/src/test/run-fail/write-guard-fail-3.rs b/src/test/run-fail/write-guard-fail-3.rs
new file mode 100644
index 00000000000..ad4c7942121
--- /dev/null
+++ b/src/test/run-fail/write-guard-fail-3.rs
@@ -0,0 +1,8 @@
+// error-pattern:borrowed
+
+fn main() {
+    let x = @mut 3;
+    let y: &mut int = x;
+    *x = 5;
+}
+
diff --git a/src/test/run-fail/write-guard-fail.rs b/src/test/run-fail/write-guard-fail.rs
new file mode 100644
index 00000000000..d393832c6e8
--- /dev/null
+++ b/src/test/run-fail/write-guard-fail.rs
@@ -0,0 +1,13 @@
+// error-pattern:borrowed
+
+fn f(x: &int, y: @mut int) {
+    unsafe {
+        *y = 2;
+    }
+}
+
+fn main() {
+    let x = @mut 3;
+    f(x, x);
+}
+
diff --git a/src/test/run-pass/write-guard.rs b/src/test/run-pass/write-guard.rs
new file mode 100644
index 00000000000..bc370b20d15
--- /dev/null
+++ b/src/test/run-pass/write-guard.rs
@@ -0,0 +1,9 @@
+fn f(x: &int) {
+    io::println(x.to_str());
+}
+
+fn main() {
+    let x = @mut 3;
+    f(x);
+}
+