about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcore/cell.rs4
-rw-r--r--src/libcore/char.rs18
-rw-r--r--src/libcore/core.rc3
-rw-r--r--src/libcore/flate.rs2
-rw-r--r--src/libcore/path.rs2
-rw-r--r--src/libcore/prelude.rs3
-rw-r--r--src/libcore/rand.rs2
-rw-r--r--src/libcore/repr.rs4
-rw-r--r--src/libcore/rt/uv/mod.rs2
-rw-r--r--src/libcore/rt/uv/net.rs2
-rw-r--r--src/libcore/rt/uvio.rs2
-rw-r--r--src/libcore/run.rs2
-rw-r--r--src/libcore/str.rs59
-rw-r--r--src/libcore/str/ascii.rs268
-rw-r--r--src/libcore/task/spawn.rs2
-rw-r--r--src/libcore/to_str.rs6
-rw-r--r--src/libcore/unstable/extfmt.rs4
-rw-r--r--src/libcore/vec.rs4
-rw-r--r--src/librustc/back/link.rs112
-rw-r--r--src/librustc/driver/driver.rs11
-rw-r--r--src/librustc/driver/session.rs2
-rw-r--r--src/librustc/lib/llvm.rs3
-rw-r--r--src/librustc/metadata/encoder.rs2
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs13
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs2
-rw-r--r--src/librustc/middle/borrowck/loan.rs12
-rw-r--r--src/librustc/middle/check_match.rs2
-rw-r--r--src/librustc/middle/lint.rs57
-rw-r--r--src/librustc/middle/liveness.rs43
-rw-r--r--src/librustc/middle/mem_categorization.rs10
-rw-r--r--src/librustc/middle/resolve.rs18
-rw-r--r--src/librustc/middle/trans/_match.rs2
-rw-r--r--src/librustc/middle/trans/adt.rs186
-rw-r--r--src/librustc/middle/trans/base.rs5
-rw-r--r--src/librustc/middle/trans/callee.rs6
-rw-r--r--src/librustc/middle/trans/closure.rs2
-rw-r--r--src/librustc/middle/trans/common.rs39
-rw-r--r--src/librustc/middle/trans/expr.rs6
-rw-r--r--src/librustc/middle/trans/foreign.rs3
-rw-r--r--src/librustc/middle/trans/monomorphize.rs16
-rw-r--r--src/librustc/middle/ty.rs6
-rw-r--r--src/librustc/middle/typeck/check/method.rs2
-rw-r--r--src/librustc/middle/typeck/check/mod.rs9
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs2
-rw-r--r--src/librustc/middle/typeck/rscope.rs2
-rw-r--r--src/libstd/bitv.rs2
-rw-r--r--src/libstd/deque.rs8
-rw-r--r--src/libstd/dlist.rs8
-rw-r--r--src/libstd/num/rational.rs4
-rw-r--r--src/libstd/rope.rs5
-rw-r--r--src/libstd/sha1.rs4
-rw-r--r--src/libstd/sort.rs4
-rw-r--r--src/libstd/test.rs3
-rw-r--r--src/libstd/time.rs4
-rw-r--r--src/libsyntax/diagnostic.rs2
-rw-r--r--src/libsyntax/ext/fmt.rs8
-rw-r--r--src/libsyntax/parse/attr.rs8
-rw-r--r--src/libsyntax/parse/comments.rs2
-rw-r--r--src/libsyntax/parse/parser.rs28
-rw-r--r--src/libsyntax/print/pp.rs6
-rw-r--r--src/libsyntax/print/pprust.rs2
m---------src/llvm0
-rw-r--r--src/rustllvm/RustWrapper.cpp3
-rw-r--r--src/test/compile-fail/unused-mut-variables.rs42
-rw-r--r--src/test/run-pass/nullable-pointer-iotareduction.rs87
-rw-r--r--src/test/run-pass/nullable-pointer-size.rs44
66 files changed, 977 insertions, 259 deletions
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index 1707bddc2b9..27e03d2bf31 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -20,7 +20,7 @@ Similar to a mutable option type, but friendlier.
 */
 
 pub struct Cell<T> {
-    value: Option<T>
+    priv value: Option<T>
 }
 
 impl<T:cmp::Eq> cmp::Eq for Cell<T> {
@@ -121,7 +121,7 @@ fn test_with_ref() {
 #[test]
 fn test_with_mut_ref() {
     let good = ~[1, 2, 3];
-    let mut v = ~[1, 2];
+    let v = ~[1, 2];
     let c = Cell(v);
     do c.with_mut_ref() |v| { v.push(3); }
     let v = c.take();
diff --git a/src/libcore/char.rs b/src/libcore/char.rs
index 6ca33540cee..c07a31490c3 100644
--- a/src/libcore/char.rs
+++ b/src/libcore/char.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -234,6 +234,21 @@ pub fn escape_default(c: char) -> ~str {
     }
 }
 
+/// Returns the amount of bytes this character would need if encoded in utf8
+pub fn len_utf8_bytes(c: char) -> uint {
+    static max_one_b: uint = 128u;
+    static max_two_b: uint = 2048u;
+    static max_three_b: uint = 65536u;
+    static max_four_b: uint = 2097152u;
+
+    let code = c as uint;
+    if code < max_one_b { 1u }
+    else if code < max_two_b { 2u }
+    else if code < max_three_b { 3u }
+    else if code < max_four_b { 4u }
+    else { fail!(~"invalid character!") }
+}
+
 /**
  * Compare two chars
  *
@@ -334,7 +349,6 @@ fn test_escape_default() {
     assert_eq!(escape_default('\U0001d4b6'), ~"\\U0001d4b6");
 }
 
-
 #[test]
 fn test_escape_unicode() {
     assert_eq!(escape_unicode('\x00'), ~"\\x00");
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index fd8813d0c4a..b0a3939db73 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -164,6 +164,9 @@ pub mod vec;
 pub mod at_vec;
 pub mod str;
 
+#[path = "str/ascii.rs"]
+pub mod ascii;
+
 pub mod ptr;
 pub mod owned;
 pub mod managed;
diff --git a/src/libcore/flate.rs b/src/libcore/flate.rs
index a4f12f7da7e..70c96c9c806 100644
--- a/src/libcore/flate.rs
+++ b/src/libcore/flate.rs
@@ -67,7 +67,7 @@ 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 mut outsz : size_t = 0;
+            let outsz : size_t = 0;
             let res =
                 rustrt::tinfl_decompress_mem_to_heap(b as *c_void,
                                                      len as size_t,
diff --git a/src/libcore/path.rs b/src/libcore/path.rs
index 23f7e912f96..8328d42c35e 100644
--- a/src/libcore/path.rs
+++ b/src/libcore/path.rs
@@ -854,7 +854,7 @@ pub mod windows {
             while i < s.len() {
                 if is_sep(s[i]) {
                     let pre = s.slice(2, i).to_owned();
-                    let mut rest = s.slice(i, s.len()).to_owned();
+                    let rest = s.slice(i, s.len()).to_owned();
                     return Some((pre, rest));
                 }
                 i += 1;
diff --git a/src/libcore/prelude.rs b/src/libcore/prelude.rs
index 7d8b3edcab1..15aa6c78ed6 100644
--- a/src/libcore/prelude.rs
+++ b/src/libcore/prelude.rs
@@ -45,9 +45,10 @@ pub use path::Path;
 pub use path::PosixPath;
 pub use path::WindowsPath;
 pub use ptr::Ptr;
+pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr};
 pub use str::{StrSlice, OwnedStr};
 pub use to_bytes::IterBytes;
-pub use to_str::ToStr;
+pub use to_str::{ToStr, ToStrConsume};
 pub use tuple::{CopyableTuple, ImmutableTuple, ExtendedTupleOps};
 pub use vec::{CopyableVector, ImmutableVector};
 pub use vec::{ImmutableEqVector, ImmutableCopyableVector};
diff --git a/src/libcore/rand.rs b/src/libcore/rand.rs
index 1e33f382df5..919c7bbb036 100644
--- a/src/libcore/rand.rs
+++ b/src/libcore/rand.rs
@@ -742,7 +742,7 @@ struct XorShiftState {
 impl Rng for XorShiftState {
     fn next(&self) -> u32 {
         let x = self.x;
-        let mut t = x ^ (x << 11);
+        let t = x ^ (x << 11);
         self.x = self.y;
         self.y = self.z;
         self.z = self.w;
diff --git a/src/libcore/repr.rs b/src/libcore/repr.rs
index ca67e6366b5..03e44e00d88 100644
--- a/src/libcore/repr.rs
+++ b/src/libcore/repr.rs
@@ -210,7 +210,7 @@ pub impl ReprVisitor {
     #[inline(always)]
     fn visit_ptr_inner(&self, ptr: *c_void, inner: *TyDesc) -> bool {
         unsafe {
-            let mut u = ReprVisitor(ptr, self.writer);
+            let u = ReprVisitor(ptr, self.writer);
             let v = reflect::MovePtrAdaptor(u);
             visit_tydesc(inner, @v as @TyVisitor);
             true
@@ -667,7 +667,7 @@ pub fn write_repr<T>(writer: @Writer, object: &T) {
     unsafe {
         let ptr = ptr::to_unsafe_ptr(object) as *c_void;
         let tydesc = intrinsic::get_tydesc::<T>();
-        let mut u = ReprVisitor(ptr, writer);
+        let u = ReprVisitor(ptr, writer);
         let v = reflect::MovePtrAdaptor(u);
         visit_tydesc(tydesc, @v as @TyVisitor)
     }
diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs
index 32757d6376e..4cbc8d70569 100644
--- a/src/libcore/rt/uv/mod.rs
+++ b/src/libcore/rt/uv/mod.rs
@@ -402,7 +402,7 @@ fn loop_smoke_test() {
 fn idle_new_then_close() {
     do run_in_bare_thread {
         let mut loop_ = Loop::new();
-        let mut idle_watcher = { IdleWatcher::new(&mut loop_) };
+        let idle_watcher = { IdleWatcher::new(&mut loop_) };
         idle_watcher.close();
     }
 }
diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs
index b4a08c14928..bcfe8b2cfdf 100644
--- a/src/libcore/rt/uv/net.rs
+++ b/src/libcore/rt/uv/net.rs
@@ -393,7 +393,7 @@ fn connect_read() {
                 let buf = vec_from_uv_buf(buf);
                 rtdebug!("read cb!");
                 if status.is_none() {
-                    let bytes = buf.unwrap();
+                    let _bytes = buf.unwrap();
                     rtdebug!("%s", bytes.slice(0, nread as uint).to_str());
                 } else {
                     rtdebug!("status after read: %s", status.get().to_str());
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index ff539739835..d4e547de383 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -206,7 +206,7 @@ impl TcpListener for UvTcpListener {
                     let mut server_stream_watcher = server_stream_watcher;
                     let mut loop_ = loop_from_watcher(&server_stream_watcher);
                     let mut client_tcp_watcher = TcpWatcher::new(&mut loop_);
-                    let mut client_tcp_watcher = client_tcp_watcher.as_stream();
+                    let client_tcp_watcher = client_tcp_watcher.as_stream();
                     // XXX: Need's to be surfaced in interface
                     server_stream_watcher.accept(client_tcp_watcher);
                     Some(~UvStream::new(client_tcp_watcher))
diff --git a/src/libcore/run.rs b/src/libcore/run.rs
index 087ee6cdf85..f9d7f4a229c 100644
--- a/src/libcore/run.rs
+++ b/src/libcore/run.rs
@@ -343,7 +343,7 @@ pub fn start_program(prog: &str, args: &[~str]) -> @Program {
         fn force_destroy(&mut self) { destroy_repr(&mut self.r, true); }
     }
 
-    let mut repr = ProgRepr {
+    let repr = ProgRepr {
         pid: pid,
         in_fd: pipe_input.out,
         out_file: os::fdopen(pipe_output.in),
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index f1cacc6a348..9590f148e30 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -673,7 +673,7 @@ pub fn levdistance(s: &str, t: &str) -> uint {
 
         for t.each_chari |j, tc| {
 
-            let mut next = dcol[j + 1];
+            let next = dcol[j + 1];
 
             if sc == tc {
                 dcol[j + 1] = current;
@@ -789,16 +789,18 @@ pub fn each_split_within<'a>(ss: &'a str,
 
 /// Convert a string to lowercase. ASCII only
 pub fn to_lower(s: &str) -> ~str {
-    map(s,
-        |c| unsafe{(libc::tolower(c as libc::c_char)) as char}
-    )
+    do map(s) |c| {
+        assert!(char::is_ascii(c));
+        (unsafe{libc::tolower(c as libc::c_char)}) as char
+    }
 }
 
 /// Convert a string to uppercase. ASCII only
 pub fn to_upper(s: &str) -> ~str {
-    map(s,
-        |c| unsafe{(libc::toupper(c as libc::c_char)) as char}
-    )
+    do map(s) |c| {
+        assert!(char::is_ascii(c));
+        (unsafe{libc::toupper(c as libc::c_char)}) as char
+    }
 }
 
 /**
@@ -909,7 +911,7 @@ impl TotalOrd for @str {
 /// Bytewise slice less than
 fn lt(a: &str, b: &str) -> bool {
     let (a_len, b_len) = (a.len(), b.len());
-    let mut end = uint::min(a_len, b_len);
+    let end = uint::min(a_len, b_len);
 
     let mut i = 0;
     while i < end {
@@ -1715,7 +1717,7 @@ pub fn utf16_chars(v: &[u16], f: &fn(char)) {
     let len = vec::len(v);
     let mut i = 0u;
     while (i < len && v[i] != 0u16) {
-        let mut u = v[i];
+        let u = v[i];
 
         if  u <= 0xD7FF_u16 || u >= 0xE000_u16 {
             f(u as char);
@@ -2317,20 +2319,20 @@ pub mod raw {
     }
 
     /// Removes the last byte from a string and returns it. (Not UTF-8 safe).
-    pub fn pop_byte(s: &mut ~str) -> u8 {
+    pub unsafe fn pop_byte(s: &mut ~str) -> u8 {
         let len = len(*s);
         assert!((len > 0u));
         let b = s[len - 1u];
-        unsafe { set_len(s, len - 1u) };
+        set_len(s, len - 1u);
         return b;
     }
 
     /// Removes the first byte from a string and returns it. (Not UTF-8 safe).
-    pub fn shift_byte(s: &mut ~str) -> u8 {
+    pub unsafe fn shift_byte(s: &mut ~str) -> u8 {
         let len = len(*s);
         assert!((len > 0u));
         let b = s[0];
-        *s = unsafe { raw::slice_bytes_owned(*s, 1u, len) };
+        *s = raw::slice_bytes_owned(*s, 1u, len);
         return b;
     }
 
@@ -3096,12 +3098,11 @@ mod tests {
 
     #[test]
     fn test_to_lower() {
-        unsafe {
-            assert!(~"" == map(~"",
-                |c| libc::tolower(c as c_char) as char));
-            assert!(~"ymca" == map(~"YMCA",
-                |c| libc::tolower(c as c_char) as char));
-        }
+        // libc::tolower, and hence str::to_lower
+        // are culturally insensitive: they only work for ASCII
+        // (see Issue #1347)
+        assert!(~"" == to_lower(""));
+        assert!(~"ymca" == to_lower("YMCA"));
     }
 
     #[test]
@@ -3346,7 +3347,7 @@ mod tests {
     #[test]
     fn test_shift_byte() {
         let mut s = ~"ABC";
-        let b = raw::shift_byte(&mut s);
+        let b = unsafe{raw::shift_byte(&mut s)};
         assert!((s == ~"BC"));
         assert!((b == 65u8));
     }
@@ -3354,7 +3355,7 @@ mod tests {
     #[test]
     fn test_pop_byte() {
         let mut s = ~"ABC";
-        let b = raw::pop_byte(&mut s);
+        let b = unsafe{raw::pop_byte(&mut s)};
         assert!((s == ~"AB"));
         assert!((b == 67u8));
     }
@@ -3666,12 +3667,8 @@ mod tests {
 
     #[test]
     fn test_map() {
-        unsafe {
-            assert!(~"" == map(~"", |c|
-                libc::toupper(c as c_char) as char));
-            assert!(~"YMCA" == map(~"ymca",
-                                  |c| libc::toupper(c as c_char) as char));
-        }
+        assert!(~"" == map(~"", |c| unsafe {libc::toupper(c as c_char)} as char));
+        assert!(~"YMCA" == map(~"ymca", |c| unsafe {libc::toupper(c as c_char)} as char));
     }
 
     #[test]
@@ -3685,11 +3682,11 @@ mod tests {
 
     #[test]
     fn test_any() {
-        assert!(false  == any(~"", char::is_uppercase));
+        assert!(false == any(~"", char::is_uppercase));
         assert!(false == any(~"ymca", char::is_uppercase));
         assert!(true  == any(~"YMCA", char::is_uppercase));
-        assert!(true == any(~"yMCA", char::is_uppercase));
-        assert!(true == any(~"Ymcy", char::is_uppercase));
+        assert!(true  == any(~"yMCA", char::is_uppercase));
+        assert!(true  == any(~"Ymcy", char::is_uppercase));
     }
 
     #[test]
diff --git a/src/libcore/str/ascii.rs b/src/libcore/str/ascii.rs
new file mode 100644
index 00000000000..339274ab47e
--- /dev/null
+++ b/src/libcore/str/ascii.rs
@@ -0,0 +1,268 @@
+// Copyright 2013 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 to_str::{ToStr,ToStrConsume};
+use str;
+use cast;
+
+/// Datatype to hold one ascii character. It is 8 bit long.
+#[deriving(Clone, Eq)]
+pub struct Ascii { priv chr: u8 }
+
+pub impl Ascii {
+    /// Converts a ascii character into a `u8`.
+    #[inline(always)]
+    fn to_byte(self) -> u8 {
+        self.chr
+    }
+
+    /// Converts a ascii character into a `char`.
+    #[inline(always)]
+    fn to_char(self) -> char {
+        self.chr as char
+    }
+
+    /// Convert to lowercase.
+    #[inline(always)]
+    fn to_lower(self) -> Ascii {
+        if self.chr >= 65 && self.chr <= 90 {
+            Ascii{chr: self.chr | 0x20 }
+        } else {
+            self
+        }
+    }
+
+    /// Convert to uppercase.
+    #[inline(always)]
+    fn to_upper(self) -> Ascii {
+        if self.chr >= 97 && self.chr <= 122 {
+            Ascii{chr: self.chr & !0x20 }
+        } else {
+            self
+        }
+    }
+
+    // Compares two ascii characters of equality, ignoring case.
+    #[inline(always)]
+    fn eq_ignore_case(self, other: Ascii) -> bool {
+        self.to_lower().chr == other.to_lower().chr
+    }
+}
+
+impl ToStr for Ascii {
+    #[inline(always)]
+    fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) }
+}
+
+/// Trait for converting into an ascii type.
+pub trait AsciiCast<T> {
+    /// Convert to an ascii type
+    fn to_ascii(&self) -> T;
+
+    /// Check if convertible to ascii
+    fn is_ascii(&self) -> bool;
+}
+
+impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] {
+    #[inline(always)]
+    fn to_ascii(&self) -> &'self[Ascii] {
+        assert!(self.is_ascii());
+        unsafe{ cast::transmute(*self) }
+    }
+
+    #[inline(always)]
+    fn is_ascii(&self) -> bool {
+        for self.each |b| {
+            if !b.is_ascii() { return false; }
+        }
+        true
+    }
+}
+
+impl<'self> AsciiCast<&'self[Ascii]> for &'self str {
+    #[inline(always)]
+    fn to_ascii(&self) -> &'self[Ascii] {
+        assert!(self.is_ascii());
+        let (p,len): (*u8, uint) = unsafe{ cast::transmute(*self) };
+        unsafe{ cast::transmute((p, len - 1))}
+    }
+
+    #[inline(always)]
+    fn is_ascii(&self) -> bool {
+        for self.each |b| {
+            if !b.is_ascii() { return false; }
+        }
+        true
+    }
+}
+
+impl AsciiCast<Ascii> for u8 {
+    #[inline(always)]
+    fn to_ascii(&self) -> Ascii {
+        assert!(self.is_ascii());
+        Ascii{ chr: *self }
+    }
+
+    #[inline(always)]
+    fn is_ascii(&self) -> bool {
+        *self & 128 == 0u8
+    }
+}
+
+impl AsciiCast<Ascii> for char {
+    #[inline(always)]
+    fn to_ascii(&self) -> Ascii {
+        assert!(self.is_ascii());
+        Ascii{ chr: *self as u8 }
+    }
+
+    #[inline(always)]
+    fn is_ascii(&self) -> bool {
+        *self - ('\x7F' & *self) == '\x00'
+    }
+}
+
+/// Trait for copyless casting to an ascii vector.
+pub trait OwnedAsciiCast {
+    /// Take ownership and cast to an ascii vector without trailing zero element.
+    fn to_ascii_consume(self) -> ~[Ascii];
+}
+
+impl OwnedAsciiCast for ~[u8] {
+    #[inline(always)]
+    fn to_ascii_consume(self) -> ~[Ascii] {
+        assert!(self.is_ascii());
+        unsafe {cast::transmute(self)}
+    }
+}
+
+impl OwnedAsciiCast for ~str {
+    #[inline(always)]
+    fn to_ascii_consume(self) -> ~[Ascii] {
+        assert!(self.is_ascii());
+        let mut s = self;
+        unsafe {
+            str::raw::pop_byte(&mut s);
+            cast::transmute(s)
+        }
+    }
+}
+
+/// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str`
+pub trait AsciiStr {
+    /// Convert to a string.
+    fn to_str_ascii(&self) -> ~str;
+
+    /// Convert to vector representing a lower cased ascii string.
+    fn to_lower(&self) -> ~[Ascii];
+
+    /// Convert to vector representing a upper cased ascii string.
+    fn to_upper(&self) -> ~[Ascii];
+
+}
+
+impl<'self> AsciiStr for &'self [Ascii] {
+    #[inline(always)]
+    fn to_str_ascii(&self) -> ~str {
+        let mut cpy = self.to_owned();
+        cpy.push(0u8.to_ascii());
+        unsafe {cast::transmute(cpy)}
+    }
+
+    #[inline(always)]
+    fn to_lower(&self) -> ~[Ascii] {
+        self.map(|a| a.to_lower())
+    }
+
+    #[inline(always)]
+    fn to_upper(&self) -> ~[Ascii] {
+        self.map(|a| a.to_upper())
+    }
+}
+
+impl ToStrConsume for ~[Ascii] {
+    #[inline(always)]
+    fn to_str_consume(self) -> ~str {
+        let mut cpy = self;
+        cpy.push(0u8.to_ascii());
+        unsafe {cast::transmute(cpy)}
+    }
+}
+
+mod tests {
+    use super::*;
+
+    macro_rules! v2ascii (
+        ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]);
+        (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]);
+    )
+
+    #[test]
+    fn test_ascii() {
+        assert_eq!(65u8.to_ascii().to_byte(), 65u8);
+        assert_eq!(65u8.to_ascii().to_char(), 'A');
+        assert_eq!('A'.to_ascii().to_char(), 'A');
+        assert_eq!('A'.to_ascii().to_byte(), 65u8);
+
+        assert_eq!('A'.to_ascii().to_lower().to_char(), 'a');
+        assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z');
+        assert_eq!('a'.to_ascii().to_upper().to_char(), 'A');
+        assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z');
+
+        assert_eq!('@'.to_ascii().to_lower().to_char(), '@');
+        assert_eq!('['.to_ascii().to_lower().to_char(), '[');
+        assert_eq!('`'.to_ascii().to_upper().to_char(), '`');
+        assert_eq!('{'.to_ascii().to_upper().to_char(), '{');
+    }
+
+    #[test]
+    fn test_ascii_vec() {
+        assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
+        assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
+        // FIXME: #5475 borrowchk error, owned vectors do not live long enough
+        // if chained-from directly
+        let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
+        let v = ~"( ;";              assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
+
+        assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#");
+        assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#");
+    }
+
+    #[test]
+    fn test_owned_ascii_vec() {
+        // FIXME: #4318 Compiler crashes on moving self
+        //assert_eq!(~"( ;".to_ascii_consume(), v2ascii!(~[40, 32, 59]));
+        //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume(), v2ascii!(~[40, 32, 59]));
+        //assert_eq!(~"( ;".to_ascii_consume_with_null(), v2ascii!(~[40, 32, 59, 0]));
+        //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume_with_null(),
+        //           v2ascii!(~[40, 32, 59, 0]));
+    }
+
+    #[test]
+    fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); }
+
+    #[test]
+    fn test_ascii_to_str_consume() {
+        // FIXME: #4318 Compiler crashes on moving self
+        //assert_eq!(v2ascii!(~[40, 32, 59]).to_str_consume(), ~"( ;");
+    }
+
+    #[test] #[should_fail]
+    fn test_ascii_vec_fail_u8_slice()  { (&[127u8, 128u8, 255u8]).to_ascii(); }
+
+    #[test] #[should_fail]
+    fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); }
+
+    #[test] #[should_fail]
+    fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); }
+
+    #[test] #[should_fail]
+    fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); }
+}
diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs
index 118c4cc2312..d872d38a278 100644
--- a/src/libcore/task/spawn.rs
+++ b/src/libcore/task/spawn.rs
@@ -575,7 +575,7 @@ fn spawn_raw_oldsched(opts: TaskOpts, f: ~fn()) {
             };
             assert!(!new_task.is_null());
             // Getting killed after here would leak the task.
-            let mut notify_chan = if opts.notify_chan.is_none() {
+            let notify_chan = if opts.notify_chan.is_none() {
                 None
             } else {
                 Some(opts.notify_chan.swap_unwrap())
diff --git a/src/libcore/to_str.rs b/src/libcore/to_str.rs
index 980d4b445d0..7f8e6915add 100644
--- a/src/libcore/to_str.rs
+++ b/src/libcore/to_str.rs
@@ -20,6 +20,12 @@ pub trait ToStr {
     fn to_str(&self) -> ~str;
 }
 
+/// Trait for converting a type to a string, consuming it in the process.
+pub trait ToStrConsume {
+    // Cosume and convert to a string.
+    fn to_str_consume(self) -> ~str;
+}
+
 impl ToStr for bool {
     #[inline(always)]
     fn to_str(&self) -> ~str { ::bool::to_str(*self) }
diff --git a/src/libcore/unstable/extfmt.rs b/src/libcore/unstable/extfmt.rs
index ad3dce0a749..ee33e2ed20b 100644
--- a/src/libcore/unstable/extfmt.rs
+++ b/src/libcore/unstable/extfmt.rs
@@ -538,7 +538,7 @@ pub mod rt {
     pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) {
         // For strings, precision is the maximum characters
         // displayed
-        let mut unpadded = match cv.precision {
+        let unpadded = match cv.precision {
           CountImplied => s,
           CountIs(max) => if (max as uint) < str::char_len(s) {
             str::slice(s, 0, max as uint)
@@ -596,7 +596,7 @@ pub mod rt {
     #[deriving(Eq)]
     pub enum PadMode { PadSigned, PadUnsigned, PadNozero, PadFloat }
 
-    pub fn pad(cv: Conv, mut s: &str, head: Option<char>, mode: PadMode,
+    pub fn pad(cv: Conv, s: &str, head: Option<char>, mode: PadMode,
                buf: &mut ~str) {
         let headsize = match head { Some(_) => 1, _ => 0 };
         let uwidth : uint = match cv.width {
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 1ef567e9cef..e478936ff65 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -1755,7 +1755,7 @@ impl<T: TotalOrd> TotalOrd for @[T] {
 
 fn lt<T:Ord>(a: &[T], b: &[T]) -> bool {
     let (a_len, b_len) = (a.len(), b.len());
-    let mut end = uint::min(a_len, b_len);
+    let end = uint::min(a_len, b_len);
 
     let mut i = 0;
     while i < end {
@@ -3897,7 +3897,7 @@ mod tests {
 
     #[test]
     fn reversed_mut() {
-        let mut v2 = reversed::<int>(~[10, 20]);
+        let v2 = reversed::<int>(~[10, 20]);
         assert!(v2[0] == 20);
         assert!(v2[1] == 10);
     }
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 3dfa318826b..99ea0bc0c86 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -61,23 +61,32 @@ pub fn llvm_err(sess: Session, msg: ~str) -> ! {
 
 pub fn WriteOutputFile(sess: Session,
         PM: lib::llvm::PassManagerRef, M: ModuleRef,
-        Triple: *c_char,
+        Triple: &str,
+        Feature: &str,
+        Output: &str,
         // FIXME: When #2334 is fixed, change
         // c_uint to FileType
-        Output: *c_char, FileType: c_uint,
+        FileType: c_uint,
         OptLevel: c_int,
         EnableSegmentedStacks: bool) {
     unsafe {
-        let result = llvm::LLVMRustWriteOutputFile(
-                PM,
-                M,
-                Triple,
-                Output,
-                FileType,
-                OptLevel,
-                EnableSegmentedStacks);
-        if (!result) {
-            llvm_err(sess, ~"Could not write output");
+        do str::as_c_str(Triple) |Triple| {
+            do str::as_c_str(Feature) |Feature| {
+                do str::as_c_str(Output) |Output| {
+                    let result = llvm::LLVMRustWriteOutputFile(
+                            PM,
+                            M,
+                            Triple,
+                            Feature,
+                            Output,
+                            FileType,
+                            OptLevel,
+                            EnableSegmentedStacks);
+                    if (!result) {
+                        llvm_err(sess, ~"Could not write output");
+                    }
+                }
+            }
         }
     }
 }
@@ -273,7 +282,7 @@ pub mod write {
                 let LLVMOptDefault    = 2 as c_int; // -O2, -Os
                 let LLVMOptAggressive = 3 as c_int; // -O3
 
-                let mut CodeGenOptLevel = match opts.optimize {
+                let CodeGenOptLevel = match opts.optimize {
                   session::No => LLVMOptNone,
                   session::Less => LLVMOptLess,
                   session::Default => LLVMOptDefault,
@@ -294,7 +303,7 @@ pub mod write {
                     return;
                 }
 
-                let mut FileType;
+                let FileType;
                 if output_type == output_type_object ||
                        output_type == output_type_exe {
                    FileType = lib::llvm::ObjectFile;
@@ -310,66 +319,49 @@ pub mod write {
                         llvm::LLVMWriteBitcodeToFile(llmod, buf)
                     });
                     pm = mk_pass_manager();
-                    // Save the assembly file if -S is used
 
+                    // Save the assembly file if -S is used
                     if output_type == output_type_assembly {
-                        let _: () = str::as_c_str(
+                        WriteOutputFile(
+                            sess,
+                            pm.llpm,
+                            llmod,
                             sess.targ_cfg.target_strs.target_triple,
-                            |buf_t| {
-                                str::as_c_str(output.to_str(), |buf_o| {
-                                    WriteOutputFile(
-                                        sess,
-                                        pm.llpm,
-                                        llmod,
-                                        buf_t,
-                                        buf_o,
-                                        lib::llvm::AssemblyFile as c_uint,
-                                        CodeGenOptLevel,
-                                        true)
-                                })
-                            });
+                            opts.target_feature,
+                            output.to_str(),
+                            lib::llvm::AssemblyFile as c_uint,
+                            CodeGenOptLevel,
+                            true);
                     }
 
-
                     // Save the object file for -c or --save-temps alone
                     // This .o is needed when an exe is built
                     if output_type == output_type_object ||
                            output_type == output_type_exe {
-                        let _: () = str::as_c_str(
+                        WriteOutputFile(
+                            sess,
+                            pm.llpm,
+                            llmod,
                             sess.targ_cfg.target_strs.target_triple,
-                            |buf_t| {
-                                str::as_c_str(output.to_str(), |buf_o| {
-                                    WriteOutputFile(
-                                        sess,
-                                        pm.llpm,
-                                        llmod,
-                                        buf_t,
-                                        buf_o,
-                                        lib::llvm::ObjectFile as c_uint,
-                                        CodeGenOptLevel,
-                                        true)
-                                })
-                            });
+                            opts.target_feature,
+                            output.to_str(),
+                            lib::llvm::ObjectFile as c_uint,
+                            CodeGenOptLevel,
+                            true);
                     }
                 } else {
                     // If we aren't saving temps then just output the file
                     // type corresponding to the '-c' or '-S' flag used
-
-                    let _: () = str::as_c_str(
+                    WriteOutputFile(
+                        sess,
+                        pm.llpm,
+                        llmod,
                         sess.targ_cfg.target_strs.target_triple,
-                        |buf_t| {
-                            str::as_c_str(output.to_str(), |buf_o| {
-                                WriteOutputFile(
-                                    sess,
-                                    pm.llpm,
-                                    llmod,
-                                    buf_t,
-                                    buf_o,
-                                    FileType as c_uint,
-                                    CodeGenOptLevel,
-                                    true)
-                            })
-                        });
+                        opts.target_feature,
+                        output.to_str(),
+                        FileType as c_uint,
+                        CodeGenOptLevel,
+                        true);
                 }
                 // Clean up and return
 
@@ -820,7 +812,7 @@ pub fn link_binary(sess: Session,
     cc_args.push(output.to_str());
     cc_args.push(obj_filename.to_str());
 
-    let mut lib_cmd;
+    let lib_cmd;
     let os = sess.targ_cfg.os;
     if os == session::os_macos {
         lib_cmd = ~"-dynamiclib";
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index fae73d7faf2..7110382bb55 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -349,7 +349,7 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
                 outputs: Option<@OutputFilenames>)
     -> (@ast::crate, Option<ty::ctxt>) {
     let time_passes = sess.time_passes();
-    let mut crate = time(time_passes, ~"parsing",
+    let crate = time(time_passes, ~"parsing",
                          || parse_input(sess, copy cfg, input) );
     if upto == cu_parse { return (crate, None); }
 
@@ -599,6 +599,7 @@ pub fn build_session_options(binary: @~str,
     let sysroot_opt = getopts::opt_maybe_str(matches, ~"sysroot");
     let sysroot_opt = sysroot_opt.map(|m| Path(*m));
     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.
@@ -637,6 +638,10 @@ pub fn build_session_options(binary: @~str,
             None => host_triple(),
             Some(s) => s
         };
+    let target_feature = match target_feature_opt {
+        None => ~"",
+        Some(s) => s
+    };
 
     let addl_lib_search_paths =
         getopts::opt_strs(matches, ~"L")
@@ -659,6 +664,7 @@ pub fn build_session_options(binary: @~str,
         addl_lib_search_paths: addl_lib_search_paths,
         maybe_sysroot: sysroot_opt,
         target_triple: target,
+        target_feature: target_feature,
         cfg: cfg,
         binary: binary,
         test: test,
@@ -769,6 +775,9 @@ pub fn optgroups() -> ~[getopts::groups::OptGroup] {
                         ~"Target triple cpu-manufacturer-kernel[-os]
                           to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
                           for detail)", ~"TRIPLE"),
+  optopt(~"", ~"target-feature",
+                        ~"Target specific attributes (llc -mattr=help
+                          for detail)", ~"FEATURE"),
   optopt(~"", ~"android-cross-path",
          ~"The path to the Android NDK", "PATH"),
   optmulti(~"W", ~"warn",
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 1912c922524..a2bbbca0c5a 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -126,6 +126,7 @@ pub struct options {
     addl_lib_search_paths: ~[Path],
     maybe_sysroot: Option<Path>,
     target_triple: ~str,
+    target_feature: ~str,
     // User-specified cfg meta items. The compiler itself will add additional
     // items to the crate config, and during parsing the entire crate config
     // will be added to the crate AST node.  This should not be used for
@@ -302,6 +303,7 @@ pub fn basic_options() -> @options {
         addl_lib_search_paths: ~[],
         maybe_sysroot: None,
         target_triple: host_triple(),
+        target_feature: ~"",
         cfg: ~[],
         binary: @~"rustc",
         test: false,
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index 369e6aeb4a5..1ab84c7978c 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -1799,9 +1799,10 @@ pub mod llvm {
         pub unsafe fn LLVMRustWriteOutputFile(PM: PassManagerRef,
                                               M: ModuleRef,
                                               Triple: *c_char,
+                                              Feature: *c_char,
+                                              Output: *c_char,
                                               // FIXME: When #2334 is fixed,
                                               // change c_uint to FileType
-                                              Output: *c_char,
                                               FileType: c_uint,
                                               OptLevel: c_int,
                                               EnableSegmentedStacks: bool)
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index aaafc7c18d6..8515e0c6e9b 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -1341,7 +1341,7 @@ pub static metadata_encoding_version : &'static [u8] =
 
 pub fn encode_metadata(parms: EncodeParams, crate: &crate) -> ~[u8] {
     let wr = @io::BytesWriter();
-    let mut stats = Stats {
+    let stats = Stats {
         inline_bytes: 0,
         attr_bytes: 0,
         dep_bytes: 0,
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 4766fe1fb94..6f3075717ed 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -367,7 +367,18 @@ pub impl CheckLoanCtxt {
             // are only assigned once
         } else {
             match cmt.mutbl {
-                McDeclared | McInherited => { /*ok*/ }
+                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);
+                        }
+                    }
+                }
                 McReadOnly | McImmutable => {
                     self.bccx.span_err(
                         ex.span,
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index 4f2e41dca5c..b8e0bba6b23 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -305,7 +305,7 @@ pub impl GatherLoanCtxt {
                 let mcx = &mem_categorization_ctxt {
                     tcx: self.tcx(),
                     method_map: self.bccx.method_map};
-                let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
+                let cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
                 debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
 
                 match autoref.kind {
diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs
index 15189a552fb..aedd6bb5467 100644
--- a/src/librustc/middle/borrowck/loan.rs
+++ b/src/librustc/middle/borrowck/loan.rs
@@ -274,7 +274,17 @@ pub impl LoanContext {
         if !owns_lent_data ||
             self.bccx.is_subregion_of(self.scope_region, scope_ub)
         {
-            if loan_kind.is_take() && !cmt.mutbl.is_mutable() {
+            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 {
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 1db8f8cc8de..8fc94cf51e2 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -481,7 +481,7 @@ pub fn specialize(cx: @MatchCheckCtxt,
                   left_ty: ty::t)
                -> Option<~[@pat]> {
     // Sad, but I can't get rid of this easily
-    let mut r0 = copy *raw_pat(r[0]);
+    let r0 = copy *raw_pat(r[0]);
     match r0 {
         pat{id: pat_id, node: n, span: pat_span} =>
             match n {
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 9087afe5969..bda97f4f530 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -13,6 +13,7 @@ use core::prelude::*;
 use driver::session::Session;
 use driver::session;
 use middle::ty;
+use middle::pat_util;
 use util::ppaux::{ty_to_str};
 
 use core::hashmap::HashMap;
@@ -86,6 +87,7 @@ pub enum lint {
 
     unused_variable,
     dead_assignment,
+    unused_mut,
 }
 
 pub fn level_to_str(lv: level) -> &'static str {
@@ -277,6 +279,13 @@ pub fn get_lint_dict() -> LintDict {
             desc: "detect assignments that will never be read",
             default: warn
         }),
+
+        (~"unused_mut",
+         LintSpec {
+            lint: unused_mut,
+            desc: "detect mut variables which don't need to be mutable",
+            default: warn
+        }),
     ];
     let mut map = HashMap::new();
     do vec::consume(v) |_, (k, v)| {
@@ -499,6 +508,7 @@ fn check_item(i: @ast::item, cx: ty::ctxt) {
     check_item_deprecated_mutable_fields(cx, i);
     check_item_deprecated_drop(cx, i);
     check_item_unused_unsafe(cx, i);
+    check_item_unused_mut(cx, i);
 }
 
 // Take a visitor, and modify it so that it will not proceed past subitems.
@@ -954,6 +964,53 @@ fn check_item_unused_unsafe(cx: ty::ctxt, it: @ast::item) {
     visit::visit_item(it, (), visit);
 }
 
+fn check_item_unused_mut(tcx: ty::ctxt, it: @ast::item) {
+    let check_pat: @fn(@ast::pat) = |p| {
+        let mut used = false;
+        let mut bindings = 0;
+        do pat_util::pat_bindings(tcx.def_map, p) |_, id, _, _| {
+            used = used || tcx.used_mut_nodes.contains(&id);
+            bindings += 1;
+        }
+        if !used {
+            let msg = if bindings == 1 {
+                ~"variable does not need to be mutable"
+            } else {
+                ~"variables do not need to be mutable"
+            };
+            tcx.sess.span_lint(unused_mut, p.id, it.id, p.span, msg);
+        }
+    };
+
+    let visit_fn_decl: @fn(&ast::fn_decl) = |fd| {
+        for fd.inputs.each |arg| {
+            if arg.is_mutbl {
+                check_pat(arg.pat);
+            }
+        }
+    };
+
+    let visit = item_stopping_visitor(
+        visit::mk_simple_visitor(@visit::SimpleVisitor {
+            visit_local: |l| {
+                if l.node.is_mutbl {
+                    check_pat(l.node.pat);
+                }
+            },
+            visit_fn: |_, fd, _, _, _| visit_fn_decl(fd),
+            visit_ty_method: |tm| visit_fn_decl(&tm.decl),
+            visit_struct_method: |sm| visit_fn_decl(&sm.decl),
+            visit_trait_method: |tm| {
+                match *tm {
+                    ast::required(ref tm) => visit_fn_decl(&tm.decl),
+                    ast::provided(m) => visit_fn_decl(&m.decl),
+                }
+            },
+            .. *visit::default_simple_visitor()
+        }));
+    visit::visit_item(it, (), visit);
+}
+
 fn check_fn(tcx: ty::ctxt, fk: &visit::fn_kind, decl: &ast::fn_decl,
             _body: &ast::blk, span: span, id: ast::node_id) {
     debug!("lint check_fn fk=%? id=%?", fk, id);
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 3136d7bf4e4..2b36ce4ce03 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1516,9 +1516,8 @@ fn check_local(local: @local, self: @Liveness, vt: vt<@Liveness>) {
 
         // Initializer:
         self.warn_about_unused_or_dead_vars_in_pat(local.node.pat);
-        if !local.node.is_mutbl {
-            self.check_for_reassignments_in_pat(local.node.pat);
-        }
+        self.check_for_reassignments_in_pat(local.node.pat,
+                                            local.node.is_mutbl);
       }
       None => {
 
@@ -1702,12 +1701,15 @@ pub impl Liveness {
         match expr.node {
           expr_path(_) => {
             match *self.tcx.def_map.get(&expr.id) {
-              def_local(nid, false) => {
-                // Assignment to an immutable variable or argument:
-                // only legal if there is no later assignment.
+              def_local(nid, mutbl) => {
+                // Assignment to an immutable variable or argument: only legal
+                // if there is no later assignment. If this local is actually
+                // mutable, then check for a reassignment to flag the mutability
+                // as being used.
                 let ln = self.live_node(expr.id, expr.span);
                 let var = self.variable(nid, expr.span);
-                self.check_for_reassignment(ln, var, expr.span);
+                self.check_for_reassignment(ln, var, expr.span,
+                                            if mutbl {Some(nid)} else {None});
                 self.warn_about_dead_assign(expr.span, expr.id, ln, var);
               }
               def => {
@@ -1731,23 +1733,28 @@ pub impl Liveness {
        }
     }
 
-    fn check_for_reassignments_in_pat(@self, pat: @pat) {
-        do self.pat_bindings(pat) |ln, var, sp, _id| {
-            self.check_for_reassignment(ln, var, sp);
+    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,
-                              orig_span: span) {
+                              orig_span: span, mutbl: Option<node_id>) {
         match self.assigned_on_exit(ln, var) {
           Some(ExprNode(span)) => {
-            self.tcx.sess.span_err(
-                span,
-                ~"re-assignment of immutable variable");
-
-            self.tcx.sess.span_note(
-                orig_span,
-                ~"prior assignment occurs here");
+            match mutbl {
+              Some(id) => { self.tcx.used_mut_nodes.insert(id); }
+              None => {
+                self.tcx.sess.span_err(
+                    span,
+                    ~"re-assignment of immutable variable");
+                self.tcx.sess.span_note(
+                    orig_span,
+                    ~"prior assignment occurs here");
+              }
+            }
           }
           Some(lnk) => {
             self.tcx.sess.span_bug(
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 6473cb8e8e0..51e6860432a 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -351,6 +351,16 @@ pub impl MutabilityCategory {
     }
 }
 
+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 cat_expr(&self, expr: @ast::expr) -> cmt {
         match self.tcx.adjustments.find(&expr.id) {
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index d923ecf4bb7..83db3a408ea 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -960,7 +960,7 @@ pub impl Resolver {
         // child name directly. Otherwise, we create or reuse an anonymous
         // module and add the child to that.
 
-        let mut module_;
+        let module_;
         match reduced_graph_parent {
             ModuleReducedGraphParent(parent_module) => {
                 module_ = parent_module;
@@ -1527,7 +1527,7 @@ pub impl Resolver {
                                      block: &blk,
                                      parent: ReducedGraphParent,
                                      visitor: vt<ReducedGraphParent>) {
-        let mut new_parent;
+        let new_parent;
         if self.block_needs_anonymous_module(block) {
             let block_id = block.node.id;
 
@@ -2427,7 +2427,7 @@ pub impl Resolver {
 
         let merge_import_resolution = |ident,
                                        name_bindings: @mut NameBindings| {
-            let mut dest_import_resolution;
+            let dest_import_resolution;
             match module_.import_resolutions.find(ident) {
                 None => {
                     // Create a new import resolution from this child.
@@ -2583,8 +2583,8 @@ pub impl Resolver {
         let module_prefix_result = self.resolve_module_prefix(module_,
                                                               module_path);
 
-        let mut search_module;
-        let mut start_index;
+        let search_module;
+        let start_index;
         match module_prefix_result {
             Failed => {
                 self.session.span_err(span, ~"unresolved name");
@@ -3221,7 +3221,7 @@ pub impl Resolver {
                 allow_capturing_self: AllowCapturingSelfFlag)
              -> Option<def_like> {
         let mut def;
-        let mut is_ty_param;
+        let is_ty_param;
 
         match def_like {
             dl_def(d @ def_local(*)) | dl_def(d @ def_upvar(*)) |
@@ -4530,7 +4530,7 @@ pub impl Resolver {
                                  -> Option<def> {
         let module_path_idents = self.intern_module_part_of_path(path);
 
-        let mut containing_module;
+        let containing_module;
         match self.resolve_module_path_for_import(self.current_module,
                                                   module_path_idents,
                                                   UseLexicalScope,
@@ -4578,7 +4578,7 @@ pub impl Resolver {
 
         let root_module = self.graph_root.get_module();
 
-        let mut containing_module;
+        let containing_module;
         match self.resolve_module_path_from_root(root_module,
                                                  module_path_idents,
                                                  0,
@@ -4622,7 +4622,7 @@ pub impl Resolver {
                                         span: span)
                                      -> Option<def> {
         // Check the local set of ribs.
-        let mut search_result;
+        let search_result;
         match namespace {
             ValueNS => {
                 search_result = self.search_ribs(&mut self.value_ribs, ident,
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 7339003c614..dc59fcecb5a 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -248,7 +248,7 @@ pub enum opt_result {
 pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
     let _icx = bcx.insn_ctxt("match::trans_opt");
     let ccx = bcx.ccx();
-    let mut bcx = bcx;
+    let bcx = bcx;
     match *o {
         lit(ExprLit(lit_expr)) => {
             let datumblock = expr::trans_to_datum(bcx, lit_expr);
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 36958fa706e..7db6e44adcf 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -29,11 +29,6 @@
  *   that might contain one and adjust GEP indices accordingly.  See
  *   issue #4578.
  *
- * - Rendering `Option<&T>` as a possibly-null `*T` instead of using
- *   an extra word (and likewise for `@T` and `~T`).  Can and probably
- *   should also apply to any enum with one empty case and one case
- *   starting with a non-null pointer (e.g., `Result<(), ~str>`).
- *
  * - Using smaller integer types for discriminants.
  *
  * - Store nested enums' discriminants in the same word.  Rather, if
@@ -54,7 +49,8 @@ use core::libc::c_ulonglong;
 use core::option::{Option, Some, None};
 use core::vec;
 
-use lib::llvm::{ValueRef, TypeRef, True};
+use lib::llvm::{ValueRef, TypeRef, True, IntEQ, IntNE};
+use lib::llvm::llvm::LLVMDumpValue;
 use middle::trans::_match;
 use middle::trans::build::*;
 use middle::trans::common::*;
@@ -81,7 +77,20 @@ pub enum Repr {
      * General-case enums: for each case there is a struct, and they
      * all start with a field for the discriminant.
      */
-    General(~[Struct])
+    General(~[Struct]),
+    /**
+     * Two cases distinguished by a nullable pointer: the case with discriminant
+     * `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th
+     * field is known to be nonnull due to its type; if that field is null, then
+     * it represents the other case, which is inhabited by at most one value
+     * (and all other fields are undefined/unused).
+     *
+     * For example, `core::option::Option` instantiated at a safe pointer type
+     * is represented such that `None` is a null pointer and `Some` is the
+     * identity function.
+     */
+    NullablePointer{ nonnull: Struct, nndiscr: int, ptrfield: uint,
+                     nullfields: ~[ty::t] }
 }
 
 /// For structs, and struct-like parts of anything fancier.
@@ -108,9 +117,16 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
         Some(repr) => return *repr,
         None => { }
     }
-    let repr = @match ty::get(t).sty {
+    let repr = @represent_type_uncached(cx, t);
+    debug!("Represented as: %?", repr)
+    cx.adt_reprs.insert(t, repr);
+    return repr;
+}
+
+fn represent_type_uncached(cx: @CrateContext, t: ty::t) -> Repr {
+    match ty::get(t).sty {
         ty::ty_tup(ref elems) => {
-            Univariant(mk_struct(cx, *elems, false), false)
+            return Univariant(mk_struct(cx, *elems, false), false)
         }
         ty::ty_struct(def_id, ref substs) => {
             let fields = ty::lookup_struct_fields(cx.tcx, def_id);
@@ -121,10 +137,18 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
             let dtor = ty::ty_dtor(cx.tcx, def_id).is_present();
             let ftys =
                 if dtor { ftys + [ty::mk_bool(cx.tcx)] } else { ftys };
-            Univariant(mk_struct(cx, ftys, packed), dtor)
+            return Univariant(mk_struct(cx, ftys, packed), dtor)
         }
         ty::ty_enum(def_id, ref substs) => {
             struct Case { discr: int, tys: ~[ty::t] };
+            impl Case {
+                fn is_zerolen(&self, cx: @CrateContext) -> bool {
+                    mk_struct(cx, self.tys, false).size == 0
+                }
+                fn find_ptr(&self) -> Option<uint> {
+                    self.tys.position(|&ty| mono_data_classify(ty) == MonoNonNull)
+                }
+            }
 
             let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
                 let arg_tys = do vi.args.map |&raw_ty| {
@@ -132,34 +156,59 @@ pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
                 };
                 Case { discr: vi.disr_val, tys: arg_tys }
             };
+
             if cases.len() == 0 {
                 // Uninhabitable; represent as unit
-                Univariant(mk_struct(cx, ~[], false), false)
-            } else if cases.all(|c| c.tys.len() == 0) {
+                return Univariant(mk_struct(cx, ~[], false), false);
+            }
+
+            if cases.all(|c| c.tys.len() == 0) {
                 // All bodies empty -> intlike
                 let discrs = cases.map(|c| c.discr);
-                CEnum(discrs.min(), discrs.max())
-            } else if cases.len() == 1 {
+                return CEnum(discrs.min(), discrs.max());
+            }
+
+            if cases.len() == 1 {
                 // Equivalent to a struct/tuple/newtype.
                 assert!(cases[0].discr == 0);
-                Univariant(mk_struct(cx, cases[0].tys, false), false)
-            } else {
-                // The general case.  Since there's at least one
-                // non-empty body, explicit discriminants should have
-                // been rejected by a checker before this point.
-                if !cases.alli(|i,c| c.discr == (i as int)) {
-                    cx.sess.bug(fmt!("non-C-like enum %s with specified \
-                                      discriminants",
-                                     ty::item_path_str(cx.tcx, def_id)))
+                return Univariant(mk_struct(cx, cases[0].tys, false), false)
+            }
+
+            // Since there's at least one
+            // non-empty body, explicit discriminants should have
+            // been rejected by a checker before this point.
+            if !cases.alli(|i,c| c.discr == (i as int)) {
+                cx.sess.bug(fmt!("non-C-like enum %s with specified \
+                                  discriminants",
+                                 ty::item_path_str(cx.tcx, def_id)))
+            }
+
+            if cases.len() == 2 {
+                let mut discr = 0;
+                while discr < 2 {
+                    if cases[1 - discr].is_zerolen(cx) {
+                        match cases[discr].find_ptr() {
+                            Some(ptrfield) => {
+                                return NullablePointer {
+                                    nndiscr: discr,
+                                    nonnull: mk_struct(cx, cases[discr].tys, false),
+                                    ptrfield: ptrfield,
+                                    nullfields: copy cases[1 - discr].tys
+                                }
+                            }
+                            None => { }
+                        }
+                    }
+                    discr += 1;
                 }
-                let discr = ~[ty::mk_int(cx.tcx)];
-                General(cases.map(|c| mk_struct(cx, discr + c.tys, false)))
             }
+
+            // The general case.
+            let discr = ~[ty::mk_int(cx.tcx)];
+            return General(cases.map(|c| mk_struct(cx, discr + c.tys, false)))
         }
         _ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
-    };
-    cx.adt_reprs.insert(t, repr);
-    return repr;
+    }
 }
 
 fn mk_struct(cx: @CrateContext, tys: &[ty::t], packed: bool) -> Struct {
@@ -190,6 +239,7 @@ fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
     match *r {
         CEnum(*) => ~[T_enum_discrim(cx)],
         Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing),
+        NullablePointer{ nonnull: ref st, _ } => struct_llfields(cx, st, sizing),
         General(ref sts) => {
             // To get "the" type of a general enum, we pick the case
             // with the largest alignment (so it will always align
@@ -239,12 +289,17 @@ pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef)
         CEnum(*) | General(*) => {
             (_match::switch, Some(trans_get_discr(bcx, r, scrutinee)))
         }
+        NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
+            (_match::switch, Some(nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee)))
+        }
         Univariant(*) => {
             (_match::single, None)
         }
     }
 }
 
+
+
 /// Obtain the actual discriminant of a value.
 pub fn trans_get_discr(bcx: block, r: &Repr, scrutinee: ValueRef)
     -> ValueRef {
@@ -252,10 +307,22 @@ pub fn trans_get_discr(bcx: block, r: &Repr, scrutinee: ValueRef)
         CEnum(min, max) => load_discr(bcx, scrutinee, min, max),
         Univariant(*) => C_int(bcx.ccx(), 0),
         General(ref cases) => load_discr(bcx, scrutinee, 0,
-                                         (cases.len() - 1) as int)
+                                         (cases.len() - 1) as int),
+        NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
+            ZExt(bcx, nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee),
+                 T_enum_discrim(bcx.ccx()))
+        }
     }
 }
 
+fn nullable_bitdiscr(bcx: block, nonnull: &Struct, nndiscr: int, ptrfield: uint,
+                     scrutinee: ValueRef) -> ValueRef {
+    let cmp = if nndiscr == 0 { IntEQ } else { IntNE };
+    let llptr = Load(bcx, GEPi(bcx, scrutinee, [0, ptrfield]));
+    let llptrty = type_of::type_of(bcx.ccx(), nonnull.fields[ptrfield]);
+    ICmp(bcx, cmp, llptr, C_null(llptrty))
+}
+
 /// Helper for cases where the discriminant is simply loaded.
 fn load_discr(bcx: block, scrutinee: ValueRef, min: int, max: int)
     -> ValueRef {
@@ -286,12 +353,16 @@ pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
         CEnum(*) => {
             _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
         }
-        Univariant(*)=> {
+        Univariant(*) => {
             bcx.ccx().sess.bug(~"no cases for univariants or structs")
         }
         General(*) => {
             _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
         }
+        NullablePointer{ _ } => {
+            assert!(discr == 0 || discr == 1);
+            _match::single_result(rslt(bcx, C_i1(discr != 0)))
+        }
     }
 }
 
@@ -317,6 +388,13 @@ pub fn trans_start_init(bcx: block, r: &Repr, val: ValueRef, discr: int) {
         General(*) => {
             Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
         }
+        NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
+            if discr != nndiscr {
+                let llptrptr = GEPi(bcx, val, [0, ptrfield]);
+                let llptrty = type_of::type_of(bcx.ccx(), nonnull.fields[ptrfield]);
+                Store(bcx, C_null(llptrty), llptrptr)
+            }
+        }
     }
 }
 
@@ -331,7 +409,10 @@ pub fn num_args(r: &Repr, discr: int) -> uint {
             assert!(discr == 0);
             st.fields.len() - (if dtor { 1 } else { 0 })
         }
-        General(ref cases) => cases[discr as uint].fields.len() - 1
+        General(ref cases) => cases[discr as uint].fields.len() - 1,
+        NullablePointer{ nonnull: ref nonnull, nndiscr, _ } => {
+            if discr == nndiscr { nonnull.fields.len() } else { 0 }
+        }
     }
 }
 
@@ -352,6 +433,19 @@ pub fn trans_field_ptr(bcx: block, r: &Repr, val: ValueRef, discr: int,
         General(ref cases) => {
             struct_field_ptr(bcx, &cases[discr as uint], val, ix + 1, true)
         }
+        NullablePointer{ nonnull: ref nonnull, nullfields: ref nullfields, nndiscr, _ } => {
+            if (discr == nndiscr) {
+                struct_field_ptr(bcx, nonnull, val, ix, false)
+            } else {
+                // The unit-like case might have a nonzero number of unit-like fields.
+                // (e.g., Result or Either with () as one side.)
+                let llty = type_of::type_of(bcx.ccx(), nullfields[ix]);
+                assert!(machine::llsize_of_alloc(bcx.ccx(), llty) == 0);
+                // The contents of memory at this pointer can't matter, but use
+                // the value that's "reasonable" in case of pointer comparison.
+                PointerCast(bcx, val, T_ptr(llty))
+            }
+        }
     }
 }
 
@@ -420,6 +514,18 @@ pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
                                               ~[C_int(ccx, discr)] + vals);
             C_struct(contents + [padding(max_sz - case.size)])
         }
+        NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
+            if discr == nndiscr {
+                C_struct(build_const_struct(ccx, nonnull, vals))
+            } else {
+                assert!(vals.len() == 0);
+                let vals = do nonnull.fields.mapi |i, &ty| {
+                    let llty = type_of::sizing_type_of(ccx, ty);
+                    if i == ptrfield { C_null(llty) } else { C_undef(llty) }
+                };
+                C_struct(build_const_struct(ccx, nonnull, vals))
+            }
+        }
     }
 }
 
@@ -451,10 +557,14 @@ fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
             cfields.push(padding(target_offset - offset));
             offset = target_offset;
         }
-        assert!(!is_undef(vals[i]));
-        // If that assert fails, could change it to wrap in a struct?
-        // (See `const_struct_field` for why real fields must not be undef.)
-        cfields.push(vals[i]);
+        let val = if is_undef(vals[i]) {
+            let wrapped = C_struct([vals[i]]);
+            assert!(!is_undef(wrapped));
+            wrapped
+        } else {
+            vals[i]
+        };
+        cfields.push(val);
     }
 
     return cfields;
@@ -475,6 +585,9 @@ pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
         CEnum(*) => const_to_int(val) as int,
         Univariant(*) => 0,
         General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int,
+        NullablePointer{ nndiscr, ptrfield, _ } => {
+            if is_null(const_struct_field(ccx, val, ptrfield)) { 1 - nndiscr } else { nndiscr }
+        }
     }
 }
 
@@ -490,7 +603,8 @@ pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef,
     match *r {
         CEnum(*) => ccx.sess.bug(~"element access in C-like enum const"),
         Univariant(*) => const_struct_field(ccx, val, ix),
-        General(*) => const_struct_field(ccx, val, ix + 1)
+        General(*) => const_struct_field(ccx, val, ix + 1),
+        NullablePointer{ _ } => const_struct_field(ccx, val, ix)
     }
 }
 
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 3ad021b2f51..2d9f834040a 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -2007,6 +2007,11 @@ pub fn trans_enum_variant(ccx: @CrateContext,
     // XXX is there a better way to reconstruct the ty::t?
     let repr = adt::represent_type(ccx, enum_ty);
 
+    debug!("trans_enum_variant: name=%s tps=%s repr=%? enum_ty=%s",
+           unsafe { str::raw::from_c_str(llvm::LLVMGetValueName(llfndecl)) },
+           ~"[" + str::connect(ty_param_substs.map(|&t| ty_to_str(ccx.tcx, t)), ", ") + ~"]",
+           repr, ty_to_str(ccx.tcx, enum_ty));
+
     adt::trans_start_init(bcx, repr, fcx.llretptr.get(), disr);
     for vec::eachi(args) |i, va| {
         let lldestptr = adt::trans_field_ptr(bcx,
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 3301ed62cbe..7a174be1e57 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -292,7 +292,7 @@ pub fn trans_fn_ref_with_vtables(
     }
 
     // Find the actual function pointer.
-    let mut val = {
+    let val = {
         if def_id.crate == ast::local_crate {
             // Internal reference.
             get_item_val(ccx, def_id.node)
@@ -415,7 +415,7 @@ pub fn trans_lang_call_with_type_params(bcx: block,
                                                     type_params,
                                                     None,
                                                     fty);
-                    let mut llfnty = type_of::type_of(callee.bcx.ccx(),
+                    let llfnty = type_of::type_of(callee.bcx.ccx(),
                                                       substituted);
                     new_llval = PointerCast(callee.bcx, fn_data.llfn, llfnty);
                 }
@@ -712,7 +712,7 @@ pub fn trans_arg_expr(bcx: block,
         }
     };
     let mut arg_datum = arg_datumblock.datum;
-    let mut bcx = arg_datumblock.bcx;
+    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 cb815506c39..4fc4cae464d 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -261,7 +261,7 @@ pub fn build_closure(bcx0: block,
                      include_ret_handle: Option<ValueRef>) -> ClosureResult {
     let _icx = bcx0.insn_ctxt("closure::build_closure");
     // If we need to, package up the iterator body to call
-    let mut bcx = bcx0;;
+    let bcx = bcx0;;
     let ccx = bcx.ccx(), tcx = ccx.tcx;
 
     // Package up the captured upvars
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 3180fb5a4c8..83fe135d872 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -1304,6 +1304,12 @@ pub fn is_undef(val: ValueRef) -> bool {
     }
 }
 
+pub fn is_null(val: ValueRef) -> bool {
+    unsafe {
+        llvm::LLVMIsNull(val) != False
+    }
+}
+
 // Used to identify cached monomorphized functions and vtables
 #[deriving(Eq)]
 pub enum mono_param_id {
@@ -1311,11 +1317,36 @@ pub enum mono_param_id {
     mono_any,
     mono_repr(uint /* size */,
               uint /* align */,
-              bool /* is_float */,
+              MonoDataClass,
               datum::DatumMode),
 }
 
 #[deriving(Eq)]
+pub enum MonoDataClass {
+    MonoBits,    // Anything not treated differently from arbitrary integer data
+    MonoNonNull, // Non-null pointers (used for optional-pointer optimization)
+    // FIXME(#3547)---scalars and floats are
+    // treated differently in most ABIs.  But we
+    // should be doing something more detailed
+    // here.
+    MonoFloat
+}
+
+pub fn mono_data_classify(t: ty::t) -> MonoDataClass {
+    match ty::get(t).sty {
+        ty::ty_float(_) => MonoFloat,
+        ty::ty_rptr(*) | ty::ty_uniq(*) |
+        ty::ty_box(*) | ty::ty_opaque_box(*) |
+        ty::ty_estr(ty::vstore_uniq) | ty::ty_evec(_, ty::vstore_uniq) |
+        ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) |
+        ty::ty_bare_fn(*) => MonoNonNull,
+        // Is that everything?  Would closures or slices qualify?
+        _ => MonoBits
+    }
+}
+
+
+#[deriving(Eq)]
 pub struct mono_id_ {
     def: ast::def_id,
     params: ~[mono_param_id],
@@ -1338,6 +1369,12 @@ impl to_bytes::IterBytes for mono_param_id {
     }
 }
 
+impl to_bytes::IterBytes for MonoDataClass {
+    fn iter_bytes(&self, lsb0: bool, f:to_bytes::Cb) {
+        (*self as u8).iter_bytes(lsb0, f)
+    }
+}
+
 impl to_bytes::IterBytes for mono_id_ {
     fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
         to_bytes::iter_bytes_2(&self.def, &self.params, lsb0, f);
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 4b4d0869a3e..8727db27fff 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -192,7 +192,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         }
         Some(&@AutoAddEnv(*)) => {
             let mut bcx = bcx;
-            let mut datum = unpack_datum!(bcx, {
+            let datum = unpack_datum!(bcx, {
                 trans_to_datum_unadjusted(bcx, expr)
             });
             add_env(bcx, expr, datum)
@@ -1187,7 +1187,7 @@ fn trans_rec_or_struct(bcx: block,
                        dest: Dest) -> block
 {
     let _icx = bcx.insn_ctxt("trans_rec");
-    let mut bcx = bcx;
+    let bcx = bcx;
 
     let ty = node_id_type(bcx, id);
     let tcx = bcx.tcx();
@@ -1505,7 +1505,7 @@ fn trans_lazy_binop(bcx: block,
                     b: @ast::expr) -> DatumBlock {
     let _icx = bcx.insn_ctxt("trans_lazy_binop");
     let binop_ty = expr_ty(bcx, binop_expr);
-    let mut bcx = bcx;
+    let bcx = bcx;
 
     let Result {bcx: past_lhs, val: lhs} = {
         do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 956ee3bf144..cb15a2e8c64 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -567,7 +567,8 @@ pub fn trans_intrinsic(ccx: @CrateContext,
         set_fixed_stack_segment(fcx.llfn);
     }
 
-    let mut bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
+    let mut bcx = top_scope_block(fcx, None);
+    let lltop = bcx.llbb;
     match *ccx.sess.str_of(item.ident) {
         ~"atomic_cxchg" => {
             let old = AtomicCmpXchg(bcx,
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 52ca8ec49bb..052bea1b022 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -102,7 +102,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
     }
 
     let tpt = ty::lookup_item_type(ccx.tcx, fn_id);
-    let mut llitem_ty = tpt.ty;
+    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 \
@@ -395,22 +395,14 @@ pub fn make_mono_id(ccx: @CrateContext,
                             let size = machine::llbitsize_of_real(ccx, llty);
                             let align = machine::llalign_of_pref(ccx, llty);
                             let mode = datum::appropriate_mode(subst);
-
-                            // FIXME(#3547)---scalars and floats are
-                            // treated differently in most ABIs.  But we
-                            // should be doing something more detailed
-                            // here.
-                            let is_float = match ty::get(subst).sty {
-                                ty::ty_float(_) => true,
-                                _ => false
-                            };
+                            let data_class = mono_data_classify(subst);
 
                             // Special value for nil to prevent problems
                             // with undef return pointers.
                             if size <= 8u && ty::type_is_nil(subst) {
-                                mono_repr(0u, 0u, is_float, mode)
+                                mono_repr(0u, 0u, data_class, mode)
                             } else {
-                                mono_repr(size, align, is_float, mode)
+                                mono_repr(size, align, data_class, mode)
                             }
                         } else {
                             mono_precise(subst, None)
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 033fbecc08b..7cdd7c8a6f2 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -304,6 +304,11 @@ struct ctxt_ {
     // Set of used unsafe nodes (functions or blocks). Unsafe nodes not
     // present in this set can be warned about.
     used_unsafe: @mut HashSet<ast::node_id>,
+
+    // Set of nodes which mark locals as mutable which end up getting used at
+    // some point. Local variable definitions not in this set can be warned
+    // about.
+    used_mut_nodes: @mut HashSet<ast::node_id>,
 }
 
 pub enum tbox_flag {
@@ -933,6 +938,7 @@ pub fn mk_ctxt(s: session::Session,
         destructors: @mut HashSet::new(),
         trait_impls: @mut HashMap::new(),
         used_unsafe: @mut HashSet::new(),
+        used_mut_nodes: @mut HashSet::new(),
      }
 }
 
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 9d8a1145f22..e778986b2d1 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -180,7 +180,7 @@ pub struct Candidate {
 
 pub impl<'self> LookupContext<'self> {
     fn do_lookup(&self, self_ty: ty::t) -> Option<method_map_entry> {
-        let mut self_ty = structurally_resolved_type(self.fcx,
+        let self_ty = structurally_resolved_type(self.fcx,
                                                      self.self_expr.span,
                                                      self_ty);
 
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index d6f892cac3c..ca9b3602d5d 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -1625,7 +1625,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         // block syntax lambdas; that is, lambdas without explicit
         // sigils.
         let expected_sty = unpack_expected(fcx, expected, |x| Some(copy *x));
-        let mut error_happened = false;
+        let error_happened = false;
         let (expected_sig,
              expected_purity,
              expected_sigil,
@@ -1706,7 +1706,7 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
                    field: ast::ident,
                    tys: &[@ast::Ty]) {
         let tcx = fcx.ccx.tcx;
-        let mut bot = check_expr(fcx, base);
+        let bot = check_expr(fcx, base);
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
         let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t);
@@ -2867,7 +2867,7 @@ pub fn check_decl_local(fcx: @mut FnCtxt, local: @ast::local)  {
 }
 
 pub fn check_stmt(fcx: @mut FnCtxt, stmt: @ast::stmt)  {
-    let mut node_id;
+    let node_id;
     let mut saw_bot = false;
     let mut saw_err = false;
     match stmt.node {
@@ -3124,7 +3124,8 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
     ccx.tcx.enum_var_cache.insert(local_def(id), @variants);
 
     // Check that it is possible to represent this enum:
-    let mut outer = true, did = local_def(id);
+    let mut outer = true;
+    let did = local_def(id);
     if ty::type_structurally_contains(ccx.tcx, rty, |sty| {
         match *sty {
           ty::ty_enum(id, _) if id == did => {
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 39715f295ad..ad173c69560 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -11,7 +11,7 @@
 use core::prelude::*;
 
 use middle::resolve::Impl;
-use middle::ty::{param_ty};
+use middle::ty::param_ty;
 use middle::ty;
 use middle::typeck::check::{FnCtxt, impl_self_ty};
 use middle::typeck::check::{structurally_resolved_type};
diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs
index 62320a1d34d..869825e607e 100644
--- a/src/librustc/middle/typeck/rscope.rs
+++ b/src/librustc/middle/typeck/rscope.rs
@@ -158,7 +158,7 @@ impl MethodRscope {
                variance: Option<ty::region_variance>,
                rcvr_generics: &ast::Generics)
             -> MethodRscope {
-        let mut region_param_names =
+        let region_param_names =
             RegionParamNames::from_generics(rcvr_generics);
         MethodRscope {
             self_ty: self_ty,
diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs
index d89ce4232b1..d1f6bf982a7 100644
--- a/src/libstd/bitv.rs
+++ b/src/libstd/bitv.rs
@@ -891,7 +891,7 @@ mod tests {
     #[test]
     fn test_0_elements() {
         let mut act;
-        let mut exp;
+        let exp;
         act = Bitv::new(0u, false);
         exp = vec::from_elem::<uint>(0u, 0u);
         assert!(act.eq_vec(exp));
diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs
index a88d13fda66..b120e40ec25 100644
--- a/src/libstd/deque.rs
+++ b/src/libstd/deque.rs
@@ -113,7 +113,7 @@ pub impl<T> Deque<T> {
     ///
     /// Fails if the deque is empty
     fn pop_front(&mut self) -> T {
-        let mut result = self.elts[self.lo].swap_unwrap();
+        let result = self.elts[self.lo].swap_unwrap();
         self.lo = (self.lo + 1u) % self.elts.len();
         self.nelts -= 1u;
         result
@@ -126,7 +126,7 @@ pub impl<T> Deque<T> {
         if self.hi == 0u {
             self.hi = self.elts.len() - 1u;
         } else { self.hi -= 1u; }
-        let mut result = self.elts[self.hi].swap_unwrap();
+        let result = self.elts[self.hi].swap_unwrap();
         self.elts[self.hi] = None;
         self.nelts -= 1u;
         result
@@ -204,7 +204,7 @@ pub impl<T> Deque<T> {
     ///
     /// Fails if the deque is empty
     fn pop_front(&mut self) -> T {
-        let mut result = self.elts[self.lo].swap_unwrap();
+        let result = self.elts[self.lo].swap_unwrap();
         self.lo = (self.lo + 1u) % self.elts.len();
         self.nelts -= 1u;
         result
@@ -217,7 +217,7 @@ pub impl<T> Deque<T> {
         if self.hi == 0u {
             self.hi = self.elts.len() - 1u;
         } else { self.hi -= 1u; }
-        let mut result = self.elts[self.hi].swap_unwrap();
+        let result = self.elts[self.hi].swap_unwrap();
         self.elts[self.hi] = None;
         self.nelts -= 1u;
         result
diff --git a/src/libstd/dlist.rs b/src/libstd/dlist.rs
index f9de2e0f58a..e736273a5ee 100644
--- a/src/libstd/dlist.rs
+++ b/src/libstd/dlist.rs
@@ -220,7 +220,7 @@ pub impl<T> DList<T> {
      * node. O(1).
      */
     fn push_head_n(@mut self, data: T) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.add_head(nobe);
         nobe.get()
     }
@@ -233,7 +233,7 @@ pub impl<T> DList<T> {
      * node. O(1).
      */
     fn push_n(@mut self, data: T) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.add_tail(nobe);
         nobe.get()
     }
@@ -263,7 +263,7 @@ pub impl<T> DList<T> {
         data: T,
         neighbour: @mut DListNode<T>
     ) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.insert_left(nobe, neighbour);
         nobe.get()
     }
@@ -293,7 +293,7 @@ pub impl<T> DList<T> {
         data: T,
         neighbour: @mut DListNode<T>
     ) -> @mut DListNode<T> {
-        let mut nobe = DList::new_link(data);
+        let nobe = DList::new_link(data);
         self.insert_right(neighbour, nobe);
         nobe.get()
     }
diff --git a/src/libstd/num/rational.rs b/src/libstd/num/rational.rs
index ee6879e31d9..36652380bff 100644
--- a/src/libstd/num/rational.rs
+++ b/src/libstd/num/rational.rs
@@ -60,7 +60,7 @@ impl<T: Copy + Num + Ord>
 
     /// Put self into lowest terms, with denom > 0.
     fn reduce(&mut self) {
-        let mut g : T = gcd(self.numer, self.denom);
+        let g : T = gcd(self.numer, self.denom);
 
         self.numer /= g;
         self.denom /= g;
@@ -505,4 +505,4 @@ mod test {
             test(s);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs
index 48ecc0fc851..33bc393e470 100644
--- a/src/libstd/rope.rs
+++ b/src/libstd/rope.rs
@@ -838,8 +838,7 @@ pub mod node {
                   option::None => break,
                   option::Some(x) => {
                     //FIXME (#2744): Replace with memcpy or something similar
-                    let mut local_buf: ~[u8] =
-                        cast::transmute(*x.content);
+                    let local_buf: ~[u8] = cast::transmute(*x.content);
                     let mut i = x.byte_offset;
                     while i < x.byte_len {
                         buf[offset] = local_buf[i];
@@ -1156,7 +1155,7 @@ pub mod node {
         }
 
         pub fn empty() -> T {
-            let mut stack : ~[@Node] = ~[];
+            let stack : ~[@Node] = ~[];
             T { stack: stack, stackpos: -1 }
         }
 
diff --git a/src/libstd/sha1.rs b/src/libstd/sha1.rs
index 6aa4d1c54bc..7371250b38a 100644
--- a/src/libstd/sha1.rs
+++ b/src/libstd/sha1.rs
@@ -93,7 +93,7 @@ pub fn sha1() -> @Sha1 {
         assert!((vec::len(st.h) == digest_buf_len));
         assert!((vec::uniq_len(st.work_buf) == work_buf_len));
         let mut t: int; // Loop counter
-        let mut w = st.work_buf;
+        let w = st.work_buf;
 
         // Initialize the first 16 words of the vector w
         t = 0;
@@ -260,7 +260,7 @@ pub fn sha1() -> @Sha1 {
             return s;
         }
     }
-    let mut st = Sha1State {
+    let st = Sha1State {
          h: vec::from_elem(digest_buf_len, 0u32),
          len_low: 0u32,
          len_high: 0u32,
diff --git a/src/libstd/sort.rs b/src/libstd/sort.rs
index 72a888fcc91..febaea637ef 100644
--- a/src/libstd/sort.rs
+++ b/src/libstd/sort.rs
@@ -239,7 +239,7 @@ fn binarysort<T:Copy + Ord>(array: &mut [T], start: uint) {
             }
         }
         assert!(left == right);
-        let mut n = start-left;
+        let n = start-left;
 
         copy_vec(array, left+1, array, left, n);
         array[left] = pivot;
@@ -416,7 +416,7 @@ impl<T:Copy + Ord> MergeState<T> {
     }
 
     fn merge_at(&mut self, n: uint, array: &mut [T]) {
-        let mut size = self.runs.len();
+        let size = self.runs.len();
         assert!(size >= 2);
         assert!(n == size-2 || n == size-3);
 
diff --git a/src/libstd/test.rs b/src/libstd/test.rs
index 4ccbf207170..addc1da6394 100644
--- a/src/libstd/test.rs
+++ b/src/libstd/test.rs
@@ -427,8 +427,7 @@ fn run_tests(opts: &TestOpts,
     let filtered_descs = filtered_tests.map(|t| t.desc);
     callback(TeFiltered(filtered_descs));
 
-    let mut (filtered_tests,
-             filtered_benchs) =
+    let (filtered_tests, filtered_benchs) =
         do vec::partition(filtered_tests) |e| {
         match e.testfn {
             StaticTestFn(_) | DynTestFn(_) => true,
diff --git a/src/libstd/time.rs b/src/libstd/time.rs
index 70dc4d8cfeb..a763aa1592e 100644
--- a/src/libstd/time.rs
+++ b/src/libstd/time.rs
@@ -147,7 +147,7 @@ pub fn empty_tm() -> Tm {
 /// Returns the specified time in UTC
 pub fn at_utc(clock: Timespec) -> Tm {
     unsafe {
-        let mut Timespec { sec, nsec } = clock;
+        let Timespec { sec, nsec } = clock;
         let mut tm = empty_tm();
         rustrt::rust_gmtime(sec, nsec, &mut tm);
         tm
@@ -162,7 +162,7 @@ pub fn now_utc() -> Tm {
 /// Returns the specified time in the local timezone
 pub fn at(clock: Timespec) -> Tm {
     unsafe {
-        let mut Timespec { sec, nsec } = clock;
+        let Timespec { sec, nsec } = clock;
         let mut tm = empty_tm();
         rustrt::rust_localtime(sec, nsec, &mut tm);
         tm
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index 807371490db..c170ee5c119 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/diagnostic.rs
@@ -342,7 +342,7 @@ fn highlight_lines_internal(cm: @codemap::CodeMap,
         while num > 0u { num /= 10u; digits += 1u; }
 
         // indent past |name:## | and the 0-offset column location
-        let mut left = str::len(fm.name) + digits + lo.col.to_uint() + 3u;
+        let left = str::len(fm.name) + digits + lo.col.to_uint() + 3u;
         let mut s = ~"";
         // Skip is the number of characters we need to skip because they are
         // part of the 'filename:line ' part of the previous line.
diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs
index 3ebe844950a..6a877040f48 100644
--- a/src/libsyntax/ext/fmt.rs
+++ b/src/libsyntax/ext/fmt.rs
@@ -96,7 +96,7 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
             }
         }
         fn make_ty(cx: @ext_ctxt, sp: span, t: Ty) -> @ast::expr {
-            let mut rt_type;
+            let rt_type;
             match t {
               TyHex(c) => match c {
                 CaseUpper => rt_type = ~"TyHexUpper",
@@ -272,6 +272,7 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
     /* Translate each piece (portion of the fmt expression) by invoking the
        corresponding function in core::unstable::extfmt. Each function takes a
        buffer to insert data into along with the data being formatted. */
+    let npieces = pieces.len();
     do vec::consume(pieces) |i, pc| {
         match pc {
             /* Raw strings get appended via str::push_str */
@@ -279,9 +280,10 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
                 let portion = mk_uniq_str(cx, fmt_sp, s);
 
                 /* If this is the first portion, then initialize the local
-                   buffer with it directly */
+                   buffer with it directly. If it's actually the only piece,
+                   then there's no need for it to be mutable */
                 if i == 0 {
-                    stms.push(mk_local(cx, fmt_sp, true, ident, portion));
+                    stms.push(mk_local(cx, fmt_sp, npieces > 1, ident, portion));
                 } else {
                     let args = ~[mk_mut_addr_of(cx, fmt_sp, buf()), portion];
                     let call = mk_call_global(cx,
diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs
index bf8f03d4bf6..f851b9781ab 100644
--- a/src/libsyntax/parse/attr.rs
+++ b/src/libsyntax/parse/attr.rs
@@ -73,7 +73,7 @@ impl parser_attr for Parser {
         self.expect(&token::LBRACKET);
         let meta_item = self.parse_meta_item();
         self.expect(&token::RBRACKET);
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         return spanned(lo, hi, ast::attribute_ { style: style,
                                                  value: meta_item,
                                                  is_sugared_doc: false });
@@ -141,16 +141,16 @@ impl parser_attr for Parser {
             token::EQ => {
                 self.bump();
                 let lit = self.parse_lit();
-                let mut hi = self.span.hi;
+                let hi = self.span.hi;
                 @spanned(lo, hi, ast::meta_name_value(name, lit))
             }
             token::LPAREN => {
                 let inner_items = self.parse_meta_seq();
-                let mut hi = self.span.hi;
+                let hi = self.span.hi;
                 @spanned(lo, hi, ast::meta_list(name, inner_items))
             }
             _ => {
-                let mut hi = self.span.hi;
+                let hi = self.span.hi;
                 @spanned(lo, hi, ast::meta_word(name))
             }
         }
diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs
index 2f6bfd4cfc5..b73544e95d6 100644
--- a/src/libsyntax/parse/comments.rs
+++ b/src/libsyntax/parse/comments.rs
@@ -229,7 +229,7 @@ fn read_block_comment(rdr: @mut StringReader,
     debug!(">>> block comment");
     let p = rdr.last_pos;
     let mut lines: ~[~str] = ~[];
-    let mut col: CharPos = rdr.col;
+    let col: CharPos = rdr.col;
     bump(rdr);
     bump(rdr);
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 8cdbf9ac9e2..d5cb1f5ebac 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -810,7 +810,7 @@ pub impl Parser {
     // This version of parse arg doesn't necessarily require
     // identifier names.
     fn parse_arg_general(&self, require_name: bool) -> arg {
-        let mut m;
+        let m;
         let mut is_mutbl = false;
         let pat = if require_name || self.is_named_argument() {
             m = self.parse_arg_mode();
@@ -1154,7 +1154,7 @@ pub impl Parser {
         let lo = self.span.lo;
         let mut hi = self.span.hi;
 
-        let mut ex: expr_;
+        let ex: expr_;
 
         if *self.token == token::LPAREN {
             self.bump();
@@ -1629,9 +1629,9 @@ pub impl Parser {
     // parse a prefix-operator expr
     fn parse_prefix_expr(&self) -> @expr {
         let lo = self.span.lo;
-        let mut hi;
+        let hi;
 
-        let mut ex;
+        let ex;
         match *self.token {
           token::NOT => {
             self.bump();
@@ -1781,7 +1781,7 @@ pub impl Parser {
           token::BINOPEQ(op) => {
               self.bump();
               let rhs = self.parse_expr();
-              let mut aop;
+              let aop;
               match op {
                   token::PLUS => aop = add,
                   token::MINUS => aop = subtract,
@@ -1956,7 +1956,7 @@ pub impl Parser {
         let lo = self.last_span.lo;
         let cond = self.parse_expr();
         let body = self.parse_block_no_value();
-        let mut hi = body.span.hi;
+        let hi = body.span.hi;
         return self.mk_expr(lo, hi, expr_while(cond, body));
     }
 
@@ -1984,7 +1984,7 @@ pub impl Parser {
 
             let lo = self.last_span.lo;
             let body = self.parse_block_no_value();
-            let mut hi = body.span.hi;
+            let hi = body.span.hi;
             return self.mk_expr(lo, hi, expr_loop(body, opt_ident));
         } else {
             // This is a 'continue' expression
@@ -2043,7 +2043,7 @@ pub impl Parser {
 
             arms.push(ast::arm { pats: pats, guard: guard, body: blk });
         }
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         self.bump();
         return self.mk_expr(lo, hi, expr_match(discriminant, arms));
     }
@@ -2162,7 +2162,7 @@ pub impl Parser {
             let hi1 = self.last_span.lo;
             let fieldpath = ast_util::ident_to_path(mk_sp(lo1, hi1),
                                                     fieldname);
-            let mut subpat;
+            let subpat;
             if *self.token == token::COLON {
                 self.bump();
                 subpat = self.parse_pat(refutable);
@@ -2183,7 +2183,7 @@ pub impl Parser {
 
         let lo = self.span.lo;
         let mut hi = self.span.hi;
-        let mut pat;
+        let pat;
         match *self.token {
           token::UNDERSCORE => { self.bump(); pat = pat_wild; }
           token::AT => {
@@ -2534,7 +2534,7 @@ pub impl Parser {
             match self.parse_item_or_view_item(/*bad*/ copy item_attrs,
                                                true, false, false) {
               iovi_item(i) => {
-                let mut hi = i.span.hi;
+                let hi = i.span.hi;
                 let decl = @spanned(lo, hi, decl_item(i));
                 return @spanned(lo, hi, stmt_decl(decl, self.get_id()));
               }
@@ -2704,7 +2704,7 @@ pub impl Parser {
                 }
             }
         }
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         self.bump();
         let bloc = ast::blk_ {
             view_items: view_items,
@@ -3590,7 +3590,7 @@ pub impl Parser {
         let purity = self.parse_fn_purity();
         let (ident, generics) = self.parse_fn_header();
         let decl = self.parse_fn_decl(|p| p.parse_arg());
-        let mut hi = self.span.hi;
+        let hi = self.span.hi;
         self.expect(&token::SEMI);
         @ast::foreign_item { ident: ident,
                              attrs: attrs,
@@ -3798,7 +3798,7 @@ pub impl Parser {
             }
         }
         self.bump();
-        let mut actual_dtor = do the_dtor.map |dtor| {
+        let actual_dtor = do the_dtor.map |dtor| {
             let (d_body, d_attrs, d_s) = copy *dtor;
             codemap::spanned { node: ast::struct_dtor_ { id: self.get_id(),
                                                      attrs: d_attrs,
diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs
index eff7524e194..17add33d673 100644
--- a/src/libsyntax/print/pp.rs
+++ b/src/libsyntax/print/pp.rs
@@ -146,9 +146,9 @@ pub fn mk_printer(out: @io::Writer, linewidth: uint) -> @mut Printer {
     // fall behind.
     let n: uint = 3 * linewidth;
     debug!("mk_printer %u", linewidth);
-    let mut token: ~[token] = vec::from_elem(n, EOF);
-    let mut size: ~[int] = vec::from_elem(n, 0);
-    let mut scan_stack: ~[uint] = vec::from_elem(n, 0u);
+    let token: ~[token] = vec::from_elem(n, EOF);
+    let size: ~[int] = vec::from_elem(n, 0);
+    let scan_stack: ~[uint] = vec::from_elem(n, 0u);
     @mut Printer {
         out: @out,
         buf_len: n,
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 3ea04c8e40b..ab4a8c73588 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1969,7 +1969,7 @@ pub fn print_ty_fn(s: @ps,
 
 pub fn maybe_print_trailing_comment(s: @ps, span: codemap::span,
                                     next_pos: Option<BytePos>) {
-    let mut cm;
+    let cm;
     match s.cm { Some(ccm) => cm = ccm, _ => return }
     match next_comment(s) {
       Some(ref cmnt) => {
diff --git a/src/llvm b/src/llvm
-Subproject 56dd407f4f97a01b8df6554c569170d2fc276fc
+Subproject 2e9f0d21fe321849a4759a01fc28eae82ef196d
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 141276e86f0..451a390876c 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -434,6 +434,7 @@ extern "C" bool
 LLVMRustWriteOutputFile(LLVMPassManagerRef PMR,
                         LLVMModuleRef M,
                         const char *triple,
+                        const char *feature,
                         const char *path,
                         TargetMachine::CodeGenFileType FileType,
                         CodeGenOpt::Level OptLevel,
@@ -461,7 +462,7 @@ LLVMRustWriteOutputFile(LLVMPassManagerRef PMR,
 
   std::string Err;
   std::string Trip(Triple::normalize(triple));
-  std::string FeaturesStr;
+  std::string FeaturesStr(feature);
   std::string CPUStr("generic");
   const Target *TheTarget = TargetRegistry::lookupTarget(Trip, Err);
   TargetMachine *Target =
diff --git a/src/test/compile-fail/unused-mut-variables.rs b/src/test/compile-fail/unused-mut-variables.rs
new file mode 100644
index 00000000000..d1223cd8893
--- /dev/null
+++ b/src/test/compile-fail/unused-mut-variables.rs
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+// Exercise the unused_mut attribute in some positive and negative cases
+
+#[allow(dead_assignment)];
+#[allow(unused_variable)];
+#[deny(unused_mut)];
+
+fn main() {
+    // negative cases
+    let mut a = 3; //~ ERROR: variable does not need to be mutable
+    let mut a = 2, b = 3; //~ ERROR: variable does not need to be mutable
+                          //~^ ERROR: variable does not need to be mutable
+    let mut a = ~[3]; //~ ERROR: variable does not need to be mutable
+
+    // positive cases
+    let mut a = 2;
+    a = 3;
+    let mut a = ~[];
+    a.push(3);
+    let mut a = ~[];
+    do callback {
+        a.push(3);
+    }
+}
+
+fn callback(f: &fn()) {}
+
+// make sure the lint attribute can be turned off
+#[allow(unused_mut)]
+fn foo(mut a: int) {
+    let mut a = 3;
+    let mut b = ~[2];
+}
diff --git a/src/test/run-pass/nullable-pointer-iotareduction.rs b/src/test/run-pass/nullable-pointer-iotareduction.rs
new file mode 100644
index 00000000000..0c4d297403c
--- /dev/null
+++ b/src/test/run-pass/nullable-pointer-iotareduction.rs
@@ -0,0 +1,87 @@
+// Copyright 2013 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 core::{option, cast};
+
+// Iota-reduction is a rule in the Calculus of (Co-)Inductive Constructions,
+// which "says that a destructor applied to an object built from a constructor
+// behaves as expected".  -- http://coq.inria.fr/doc/Reference-Manual006.html
+//
+// It's a little more complicated here, because of pointers and regions and
+// trying to get assert failure messages that at least identify which case
+// failed.
+
+enum E<T> { Thing(int, T), Nothing((), ((), ()), [i8, ..0]) }
+impl<T> E<T> {
+    fn is_none(&self) -> bool { 
+        match *self {
+            Thing(*) => false,
+            Nothing(*) => true
+        }
+    }
+    fn get_ref<'r>(&'r self) -> (int, &'r T) {
+        match *self {
+            Nothing(*) => fail!(fmt!("E::get_ref(Nothing::<%s>)",  stringify!($T))),
+            Thing(x, ref y) => (x, y)
+        }
+    }
+}
+
+macro_rules! check_option {
+    ($e:expr: $T:ty) => {{
+        // FIXME #6000: remove the copy
+        check_option!(copy $e: $T, |ptr| assert!(*ptr == $e));
+    }};
+    ($e:expr: $T:ty, |$v:ident| $chk:expr) => {{
+        assert!(option::None::<$T>.is_none());
+        let s_ = option::Some::<$T>($e);
+        let $v = s_.get_ref();
+        $chk
+    }}
+}
+
+macro_rules! check_fancy {
+    ($e:expr: $T:ty) => {{
+        // FIXME #6000: remove the copy
+        check_fancy!(copy $e: $T, |ptr| assert!(*ptr == $e));
+    }};
+    ($e:expr: $T:ty, |$v:ident| $chk:expr) => {{
+        assert!(Nothing::<$T>((), ((), ()), [23i8, ..0]).is_none());
+        let t_ = Thing::<$T>(23, $e);
+        match t_.get_ref() {
+            (23, $v) => { $chk }
+            _ => fail!(fmt!("Thing::<%s>(23, %s).get_ref() != (23, _)",
+                            stringify!($T), stringify!($e)))
+        }
+    }}
+}
+
+macro_rules! check_type {
+    ($($a:tt)*) => {{
+        check_option!($($a)*);
+        check_fancy!($($a)*);
+    }}
+}
+
+pub fn main() {
+    check_type!(&17: &int);
+    check_type!(~18: ~int);
+    check_type!(@19: @int);
+    check_type!(~"foo": ~str);
+    check_type!(@"bar": @str);
+    check_type!(~[]: ~[int]);
+    check_type!(~[20, 22]: ~[int]);
+    check_type!(@[]: @[int]);
+    check_type!(@[24, 26]: @[int]);
+    let mint: uint = unsafe { cast::transmute(main) };
+    check_type!(main: extern fn(), |pthing| {
+        assert!(mint == unsafe { cast::transmute(*pthing) })
+    });
+}
diff --git a/src/test/run-pass/nullable-pointer-size.rs b/src/test/run-pass/nullable-pointer-size.rs
new file mode 100644
index 00000000000..246fc4e304d
--- /dev/null
+++ b/src/test/run-pass/nullable-pointer-size.rs
@@ -0,0 +1,44 @@
+// Copyright 2013 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.
+
+enum E<T> { Thing(int, T), Nothing((), ((), ()), [i8, ..0]) }
+struct S<T>(int, T);
+
+// These are macros so we get useful assert messages.
+
+macro_rules! check_option {
+    ($T:ty) => {
+        assert!(sys::size_of::<Option<$T>>() == sys::size_of::<$T>());
+    }
+}
+
+macro_rules! check_fancy {
+    ($T:ty) => {
+        assert!(sys::size_of::<E<$T>>() == sys::size_of::<S<$T>>());
+    }
+}
+
+macro_rules! check_type {
+    ($T:ty) => {{
+        check_option!($T);
+        check_fancy!($T);
+    }}
+}
+
+pub fn main() {
+    check_type!(&'static int);
+    check_type!(~int);
+    check_type!(@int);
+    check_type!(~str);
+    check_type!(@str);
+    check_type!(~[int]);
+    check_type!(@[int]);
+    check_type!(extern fn());
+}