From 8a8f200d102294cf1bd90cdacad995abccda7934 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Tue, 11 Sep 2012 21:25:01 -0700
Subject: [PATCH] Introduce auto adjustment table to subsume
 autoderef/autoref/borrowings.

Fixes #3261
Fixes #3443
---
 src/libcore/pipes.rs                          |    4 +-
 src/libcore/unsafe.rs                         |    7 +
 src/libstd/ebml.rs                            |   20 +-
 src/libstd/net_tcp.rs                         |    2 +-
 src/libstd/sync.rs                            |    4 +-
 src/libsyntax/ast.rs                          |   11 +-
 src/libsyntax/ext/auto_serialize.rs           |    2 +-
 src/libsyntax/ext/build.rs                    |   10 +-
 src/libsyntax/parse/parser.rs                 |   27 +-
 src/libsyntax/print/pprust.rs                 |   32 +-
 src/rustc/front/test.rs                       |    4 +-
 src/rustc/metadata/common.rs                  |    2 +-
 src/rustc/metadata/tydecode.rs                |   52 +-
 src/rustc/metadata/tyencode.rs                |    9 +-
 src/rustc/middle/astencode.rs                 |  148 +-
 src/rustc/middle/borrowck.rs                  |   30 +-
 src/rustc/middle/borrowck/check_loans.rs      |   48 +-
 src/rustc/middle/borrowck/gather_loans.rs     |  101 +-
 src/rustc/middle/borrowck/loan.rs             |   49 +-
 src/rustc/middle/borrowck/preserve.rs         |    2 +-
 src/rustc/middle/check_const.rs               |    6 +-
 src/rustc/middle/const_eval.rs                |   12 +-
 src/rustc/middle/kind.rs                      |   18 +-
 src/rustc/middle/mem_categorization.rs        |  157 +-
 src/rustc/middle/trans/alt.rs                 |    2 +-
 src/rustc/middle/trans/callee.rs              |   96 +-
 src/rustc/middle/trans/common.rs              |   14 +-
 src/rustc/middle/trans/consts.rs              |    4 +-
 src/rustc/middle/trans/controlflow.rs         |    8 +-
 src/rustc/middle/trans/datum.rs               |    8 +-
 src/rustc/middle/trans/expr.rs                |  221 ++-
 src/rustc/middle/trans/meth.rs                |   38 +-
 src/rustc/middle/ty.rs                        |  102 +-
 src/rustc/middle/typeck.rs                    |   13 +-
 src/rustc/middle/typeck/check.rs              |  432 ++---
 src/rustc/middle/typeck/check/demand.rs       |    5 +-
 src/rustc/middle/typeck/check/method.rs       | 1484 +++++++++--------
 src/rustc/middle/typeck/check/regionck.rs     |  260 ++-
 src/rustc/middle/typeck/check/vtable.rs       |    3 +
 src/rustc/middle/typeck/check/writeback.rs    |   57 +-
 src/rustc/middle/typeck/infer.rs              |   14 +-
 src/rustc/middle/typeck/infer/assignment.rs   |   29 +-
 src/rustc/util/ppaux.rs                       |   13 +-
 src/test/compile-fail/assign-to-method.rs     |    1 +
 .../compile-fail/borrowck-autoref-3261.rs     |   19 +
 ...borrowck-call-method-from-mut-aliasable.rs |   31 +
 src/test/compile-fail/borrowck-lend-args.rs   |    2 +-
 .../borrowck-loan-rcvr-overloaded-op.rs       |    2 +-
 src/test/compile-fail/borrowck-loan-rcvr.rs   |    2 +-
 .../borrowck-mut-addr-of-imm-var.rs           |    2 +-
 .../compile-fail/borrowck-mut-deref-comp.rs   |    2 +-
 .../borrowck-mut-slice-of-imm-vec.rs          |    8 +
 .../borrowck-mut-vec-as-imm-slice-bad.rs      |    2 +-
 .../borrowck-pure-scope-in-call.rs            |    2 +-
 .../compile-fail/borrowck-uniq-via-box.rs     |   27 +-
 .../compile-fail/borrowck-uniq-via-ref.rs     |   27 +-
 src/test/compile-fail/issue-511.rs            |    3 +-
 src/test/run-pass/auto-ref-newtype.rs         |   14 +
 .../autoderef-and-borrow-method-receiver.rs   |    2 +-
 59 files changed, 2098 insertions(+), 1608 deletions(-)
 create mode 100644 src/test/compile-fail/borrowck-autoref-3261.rs
 create mode 100644 src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
 create mode 100644 src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
 create mode 100644 src/test/run-pass/auto-ref-newtype.rs

diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs
index 1bc706d16d1..570ddfb492a 100644
--- a/src/libcore/pipes.rs
+++ b/src/libcore/pipes.rs
@@ -1177,7 +1177,7 @@ type SharedChan<T: Send> = unsafe::Exclusive<Chan<T>>;
 impl<T: Send> SharedChan<T>: Channel<T> {
     fn send(+x: T) {
         let mut xx = Some(move x);
-        do self.with |chan| {
+        do self.with_imm |chan| {
             let mut x = None;
             x <-> xx;
             chan.send(option::unwrap(move x))
@@ -1186,7 +1186,7 @@ impl<T: Send> SharedChan<T>: Channel<T> {
 
     fn try_send(+x: T) -> bool {
         let mut xx = Some(move x);
-        do self.with |chan| {
+        do self.with_imm |chan| {
             let mut x = None;
             x <-> xx;
             chan.try_send(option::unwrap(move x))
diff --git a/src/libcore/unsafe.rs b/src/libcore/unsafe.rs
index 14205f39e9f..eb3fd3974cd 100644
--- a/src/libcore/unsafe.rs
+++ b/src/libcore/unsafe.rs
@@ -354,6 +354,13 @@ impl<T: Send> Exclusive<T> {
             move result
         }
     }
+
+    #[inline(always)]
+    unsafe fn with_imm<U>(f: fn(x: &T) -> U) -> U {
+        do self.with |x| {
+            f(unsafe::transmute_immut(x))
+        }
+    }
 }
 
 // FIXME(#2585) make this a by-move method on the exclusive
diff --git a/src/libstd/ebml.rs b/src/libstd/ebml.rs
index 1d70bfaf00a..d6b8c2f4908 100644
--- a/src/libstd/ebml.rs
+++ b/src/libstd/ebml.rs
@@ -314,6 +314,8 @@ enum EbmlSerializerTag {
     EsEnum, EsEnumVid, EsEnumBody,
     EsVec, EsVecLen, EsVecElt,
 
+    EsOpaque,
+
     EsLabel // Used only when debugging
 }
 
@@ -340,6 +342,14 @@ impl ebml::Writer: SerializerPriv {
     }
 }
 
+impl ebml::Writer {
+    fn emit_opaque(f: fn()) {
+        do self.wr_tag(EsOpaque as uint) {
+            f()
+        }
+    }
+}
+
 impl ebml::Writer: serialization::Serializer {
     fn emit_nil() {}
 
@@ -397,7 +407,7 @@ impl ebml::Writer: serialization::Serializer {
 }
 
 type EbmlDeserializer_ = {mut parent: ebml::Doc,
-                           mut pos: uint};
+                          mut pos: uint};
 
 enum EbmlDeserializer {
     EbmlDeserializer_(EbmlDeserializer_)
@@ -462,6 +472,14 @@ priv impl EbmlDeserializer {
     }
 }
 
+impl EbmlDeserializer {
+    fn read_opaque<R>(op: fn(ebml::Doc) -> R) -> R {
+        do self.push_doc(self.next_doc(EsOpaque)) {
+            op(copy self.parent)
+        }
+    }
+}
+
 impl EbmlDeserializer: serialization::Deserializer {
     fn read_nil() -> () { () }
 
diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs
index d4eb5ce6998..7622483a64b 100644
--- a/src/libstd/net_tcp.rs
+++ b/src/libstd/net_tcp.rs
@@ -794,7 +794,7 @@ impl TcpSocketBuf: io::Reader {
         count
     }
     fn read_byte() -> int {
-        let bytes = ~[0];
+        let mut bytes = ~[0];
         if self.read(bytes, 1u) == 0 { fail } else { bytes[0] as int }
     }
     fn unread_byte(amt: int) {
diff --git a/src/libstd/sync.rs b/src/libstd/sync.rs
index 9a176013474..25e6d85d1c0 100644
--- a/src/libstd/sync.rs
+++ b/src/libstd/sync.rs
@@ -775,7 +775,7 @@ mod tests {
         let (c,p) = pipes::stream();
         let m = ~Mutex();
         let m2 = ~m.clone();
-        let sharedstate = ~0;
+        let mut sharedstate = ~0;
         let ptr = ptr::addr_of(*sharedstate);
         do task::spawn {
             let sharedstate: &mut int =
@@ -1047,7 +1047,7 @@ mod tests {
         // mutex mutual exclusion test, a ways above.
         let (c,p) = pipes::stream();
         let x2 = ~x.clone();
-        let sharedstate = ~0;
+        let mut sharedstate = ~0;
         let ptr = ptr::addr_of(*sharedstate);
         do task::spawn {
             let sharedstate: &mut int =
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 3d3fd5bfd11..af7d8d83b6b 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -421,6 +421,15 @@ enum vstore {
     vstore_slice(@region)         // &[1,2,3,4](foo)?
 }
 
+#[auto_serialize]
+enum expr_vstore {
+    // FIXME (#2112): Change uint to @expr (actually only constant exprs)
+    expr_vstore_fixed(Option<uint>),   // [1,2,3,4]/_ or 4
+    expr_vstore_uniq,                  // ~[1,2,3,4]
+    expr_vstore_box,                   // @[1,2,3,4]
+    expr_vstore_slice                  // &[1,2,3,4]
+}
+
 pure fn is_blockish(p: ast::proto) -> bool {
     match p {
       proto_block => true,
@@ -662,7 +671,7 @@ enum alt_mode { alt_check, alt_exhaustive, }
 
 #[auto_serialize]
 enum expr_ {
-    expr_vstore(@expr, vstore),
+    expr_vstore(@expr, expr_vstore),
     expr_vec(~[@expr], mutability),
     expr_rec(~[field], Option<@expr>),
     expr_call(@expr, ~[@expr], bool), // True iff last argument is a block
diff --git a/src/libsyntax/ext/auto_serialize.rs b/src/libsyntax/ext/auto_serialize.rs
index 6a31dac2e8f..52357ca4752 100644
--- a/src/libsyntax/ext/auto_serialize.rs
+++ b/src/libsyntax/ext/auto_serialize.rs
@@ -260,7 +260,7 @@ impl ext_ctxt: ext_ctxt_helpers {
                     ast::expr_lit(
                         @{node: ast::lit_str(s),
                           span: span})),
-                ast::vstore_uniq))
+                ast::expr_vstore_uniq))
     }
 
     fn lit_uint(span: span, i: uint) -> @ast::expr {
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 12fbb00e6c6..8574c0c9082 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -65,24 +65,26 @@ fn mk_base_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
     let vecexpr = ast::expr_vec(exprs, ast::m_imm);
     mk_expr(cx, sp, vecexpr)
 }
-fn mk_vstore_e(cx: ext_ctxt, sp: span, expr: @ast::expr, vst: ast::vstore) ->
+fn mk_vstore_e(cx: ext_ctxt, sp: span, expr: @ast::expr,
+               vst: ast::expr_vstore) ->
    @ast::expr {
     mk_expr(cx, sp, ast::expr_vstore(expr, vst))
 }
 fn mk_uniq_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
    @ast::expr {
-    mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::vstore_uniq)
+    mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::expr_vstore_uniq)
 }
 fn mk_fixed_vec_e(cx: ext_ctxt, sp: span, exprs: ~[@ast::expr]) ->
    @ast::expr {
-    mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs), ast::vstore_fixed(None))
+    mk_vstore_e(cx, sp, mk_base_vec_e(cx, sp, exprs),
+                ast::expr_vstore_fixed(None))
 }
 fn mk_base_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr {
     let lit = ast::lit_str(@s);
     return mk_lit(cx, sp, lit);
 }
 fn mk_uniq_str(cx: ext_ctxt, sp: span, s: ~str) -> @ast::expr {
-    mk_vstore_e(cx, sp, mk_base_str(cx, sp, s), ast::vstore_uniq)
+    mk_vstore_e(cx, sp, mk_base_str(cx, sp, s), ast::expr_vstore_uniq)
 }
 
 fn mk_rec_e(cx: ext_ctxt, sp: span,
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ea824ea2fb6..b8d55fb1055 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -64,7 +64,9 @@ use ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
              variant, view_item, view_item_, view_item_export,
              view_item_import, view_item_use, view_path, view_path_glob,
              view_path_list, view_path_simple, visibility, vstore, vstore_box,
-             vstore_fixed, vstore_slice, vstore_uniq};
+             vstore_fixed, vstore_slice, vstore_uniq,
+             expr_vstore_fixed, expr_vstore_slice, expr_vstore_box,
+             expr_vstore_uniq};
 
 export file_type;
 export parser;
@@ -1071,7 +1073,8 @@ impl parser {
             None => (),
             Some(v) => {
                 hi = self.span.hi;
-                ex = expr_vstore(self.mk_expr(lo, hi, ex), vstore_fixed(v));
+                ex = expr_vstore(self.mk_expr(lo, hi, ex),
+                                 expr_vstore_fixed(v));
             }
           },
           _ => ()
@@ -1370,7 +1373,7 @@ impl parser {
                 ex = match e.node {
                   expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
                   if m == m_imm => {
-                    expr_vstore(e, vstore_slice(self.region_from_name(None)))
+                    expr_vstore(e, expr_vstore_slice)
                   }
                   _ => expr_addr_of(m, e)
                 };
@@ -1386,7 +1389,7 @@ impl parser {
             // HACK: turn @[...] into a @-evec
             ex = match e.node {
               expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
-              if m == m_imm => expr_vstore(e, vstore_box),
+              if m == m_imm => expr_vstore(e, expr_vstore_box),
               _ => expr_unary(box(m), e)
             };
           }
@@ -1398,7 +1401,7 @@ impl parser {
             // HACK: turn ~[...] into a ~-evec
             ex = match e.node {
               expr_vec(*) | expr_lit(@{node: lit_str(_), span: _})
-              if m == m_imm => expr_vstore(e, vstore_uniq),
+              if m == m_imm => expr_vstore(e, expr_vstore_uniq),
               _ => expr_unary(uniq(m), e)
             };
           }
@@ -1849,7 +1852,7 @@ impl parser {
                 node: expr_lit(@{node: lit_str(_), span: _}), _
               }) => {
                 let vst = @{id: self.get_id(), callee_id: self.get_id(),
-                            node: expr_vstore(e, vstore_box),
+                            node: expr_vstore(e, expr_vstore_box),
                             span: mk_sp(lo, hi)};
                 pat_lit(vst)
               }
@@ -1866,7 +1869,7 @@ impl parser {
                 node: expr_lit(@{node: lit_str(_), span: _}), _
               }) => {
                 let vst = @{id: self.get_id(), callee_id: self.get_id(),
-                            node: expr_vstore(e, vstore_uniq),
+                            node: expr_vstore(e, expr_vstore_uniq),
                             span: mk_sp(lo, hi)};
                 pat_lit(vst)
               }
@@ -1884,10 +1887,12 @@ impl parser {
                   pat_lit(e@@{
                       node: expr_lit(@{node: lit_str(_), span: _}), _
                   }) => {
-                      let vst = @{id: self.get_id(), callee_id: self.get_id(),
-                                  node: expr_vstore(e,
-                                  vstore_slice(self.region_from_name(None))),
-                                  span: mk_sp(lo, hi)};
+                      let vst = @{
+                          id: self.get_id(),
+                          callee_id: self.get_id(),
+                          node: expr_vstore(e, expr_vstore_slice),
+                          span: mk_sp(lo, hi)
+                      };
                       pat_lit(vst)
                   }
               _ => pat_region(sub)
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 46fbaed8b5d..49133242d54 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -976,6 +976,16 @@ fn print_vstore(s: ps, t: ast::vstore) {
     }
 }
 
+fn print_expr_vstore(s: ps, t: ast::expr_vstore) {
+    match t {
+      ast::expr_vstore_fixed(Some(i)) => word(s.s, fmt!("%u", i)),
+      ast::expr_vstore_fixed(None) => word(s.s, ~"_"),
+      ast::expr_vstore_uniq => word(s.s, ~"~"),
+      ast::expr_vstore_box => word(s.s, ~"@"),
+      ast::expr_vstore_slice => word(s.s, ~"&"),
+    }
+}
+
 fn print_expr(s: ps, &&expr: @ast::expr) {
     fn print_field(s: ps, field: ast::field) {
         ibox(s, indent_unit);
@@ -992,17 +1002,17 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
     let ann_node = node_expr(s, expr);
     s.ann.pre(ann_node);
     match expr.node {
-      ast::expr_vstore(e, v) => match v {
-        ast::vstore_fixed(_) => {
-            print_expr(s, e);
-              word(s.s, ~"/");
-              print_vstore(s, v);
-          }
-        _ => {
-            print_vstore(s, v);
-              print_expr(s, e);
-          }
-      },
+        ast::expr_vstore(e, v) => match v {
+            ast::expr_vstore_fixed(_) => {
+                print_expr(s, e);
+                word(s.s, ~"/");
+                print_expr_vstore(s, v);
+            }
+            _ => {
+                print_expr_vstore(s, v);
+                print_expr(s, e);
+            }
+        },
       ast::expr_vec(exprs, mutbl) => {
         ibox(s, indent_unit);
         word(s.s, ~"[");
diff --git a/src/rustc/front/test.rs b/src/rustc/front/test.rs
index 5ac62379e76..60f14f4ca63 100644
--- a/src/rustc/front/test.rs
+++ b/src/rustc/front/test.rs
@@ -294,7 +294,7 @@ fn mk_test_desc_vec(cx: test_ctxt) -> @ast::expr {
                        span: dummy_sp()};
     return @{id: cx.sess.next_node_id(),
           callee_id: cx.sess.next_node_id(),
-          node: ast::expr_vstore(inner_expr, ast::vstore_uniq),
+          node: ast::expr_vstore(inner_expr, ast::expr_vstore_uniq),
           span: dummy_sp()};
 }
 
@@ -316,7 +316,7 @@ fn mk_test_desc_rec(cx: test_ctxt, test: test) -> @ast::expr {
     let name_expr = {id: cx.sess.next_node_id(),
                      callee_id: cx.sess.next_node_id(),
                      node: ast::expr_vstore(name_expr_inner,
-                                            ast::vstore_uniq),
+                                            ast::expr_vstore_uniq),
                      span: dummy_sp()};
 
 
diff --git a/src/rustc/metadata/common.rs b/src/rustc/metadata/common.rs
index 15e15a8a54c..a95862a978b 100644
--- a/src/rustc/metadata/common.rs
+++ b/src/rustc/metadata/common.rs
@@ -120,7 +120,7 @@ enum astencode_tag { // Reserves 0x50 -- 0x6f
     tag_table_spill = 0x5f,
     tag_table_method_map = 0x60,
     tag_table_vtable_map = 0x61,
-    tag_table_borrowings = 0x62
+    tag_table_adjustments = 0x62
 }
 
 // djb's cdb hashes.
diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs
index 0c94213c08c..9b723c5d9eb 100644
--- a/src/rustc/metadata/tydecode.rs
+++ b/src/rustc/metadata/tydecode.rs
@@ -11,8 +11,10 @@ use middle::ty;
 use std::map::HashMap;
 use ty::{FnTyBase, FnMeta, FnSig};
 
-export parse_ty_data, parse_def_id, parse_ident;
+export parse_state_from_data;
+export parse_arg_data, parse_ty_data, parse_def_id, parse_ident;
 export parse_bounds_data;
+export pstate;
 
 // Compact string representation for ty::t values. API ty_str &
 // parse_from_str. Extra parameters are for converting to/from def_ids in the
@@ -53,13 +55,25 @@ fn parse_ident_(st: @pstate, is_last: fn@(char) -> bool) ->
     return st.tcx.sess.ident_of(rslt);
 }
 
+fn parse_state_from_data(data: @~[u8], crate_num: int,
+                         pos: uint, tcx: ty::ctxt)
+    -> @pstate
+{
+    @{data: data, crate: crate_num, mut pos: pos, tcx: tcx}
+}
 
 fn parse_ty_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
                  conv: conv_did) -> ty::t {
-    let st = @{data: data, crate: crate_num, mut pos: pos, tcx: tcx};
+    let st = parse_state_from_data(data, crate_num, pos, tcx);
     parse_ty(st, conv)
 }
 
+fn parse_arg_data(data: @~[u8], crate_num: int, pos: uint, tcx: ty::ctxt,
+                  conv: conv_did) -> ty::arg {
+    let st = parse_state_from_data(data, crate_num, pos, tcx);
+    parse_arg(st, conv)
+}
+
 fn parse_ret_ty(st: @pstate, conv: conv_did) -> (ast::ret_style, ty::t) {
     match peek(st) {
       '!' => { next(st); (ast::noreturn, ty::mk_bot(st.tcx)) }
@@ -373,6 +387,23 @@ fn parse_purity(c: char) -> purity {
     }
 }
 
+fn parse_arg(st: @pstate, conv: conv_did) -> ty::arg {
+    {mode: parse_mode(st),
+     ty: parse_ty(st, conv)}
+}
+
+fn parse_mode(st: @pstate) -> ast::mode {
+    let m = ast::expl(match next(st) {
+        '&' => ast::by_mutbl_ref,
+        '-' => ast::by_move,
+        '+' => ast::by_copy,
+        '=' => ast::by_ref,
+        '#' => ast::by_val,
+        _ => fail ~"bad mode"
+    });
+    return m;
+}
+
 fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
     let proto = parse_proto(st);
     let purity = parse_purity(next(st));
@@ -380,16 +411,8 @@ fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::FnTy {
     assert (next(st) == '[');
     let mut inputs: ~[ty::arg] = ~[];
     while peek(st) != ']' {
-        let mode = match peek(st) {
-          '&' => ast::by_mutbl_ref,
-          '-' => ast::by_move,
-          '+' => ast::by_copy,
-          '=' => ast::by_ref,
-          '#' => ast::by_val,
-          _ => fail ~"bad mode"
-        };
-        st.pos += 1u;
-        vec::push(inputs, {mode: ast::expl(mode), ty: parse_ty(st, conv)});
+        let mode = parse_mode(st);
+        vec::push(inputs, {mode: mode, ty: parse_ty(st, conv)});
     }
     st.pos += 1u; // eat the ']'
     let (ret_style, ret_ty) = parse_ret_ty(st, conv);
@@ -432,8 +455,9 @@ fn parse_def_id(buf: &[u8]) -> ast::def_id {
 
 fn parse_bounds_data(data: @~[u8], start: uint,
                      crate_num: int, tcx: ty::ctxt, conv: conv_did)
-    -> @~[ty::param_bound] {
-    let st = @{data: data, crate: crate_num, mut pos: start, tcx: tcx};
+    -> @~[ty::param_bound]
+{
+    let st = parse_state_from_data(data, crate_num, start, tcx);
     parse_bounds(st, conv)
 }
 
diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs
index 175691613a7..2c8df582a55 100644
--- a/src/rustc/metadata/tyencode.rs
+++ b/src/rustc/metadata/tyencode.rs
@@ -15,6 +15,7 @@ export ac_use_abbrevs;
 export enc_ty;
 export enc_bounds;
 export enc_mode;
+export enc_arg;
 
 type ctxt = {
     diag: span_handler,
@@ -323,6 +324,11 @@ fn enc_proto(w: io::Writer, cx: @ctxt, proto: ty::fn_proto) {
     }
 }
 
+fn enc_arg(w: io::Writer, cx: @ctxt, arg: ty::arg) {
+    enc_mode(w, cx, arg.mode);
+    enc_ty(w, cx, arg.ty);
+}
+
 fn enc_mode(w: io::Writer, cx: @ctxt, m: mode) {
     match ty::resolved_mode(cx.tcx, m) {
       by_mutbl_ref => w.write_char('&'),
@@ -348,8 +354,7 @@ fn enc_ty_fn(w: io::Writer, cx: @ctxt, ft: ty::FnTy) {
     enc_bounds(w, cx, ft.meta.bounds);
     w.write_char('[');
     for ft.sig.inputs.each |arg| {
-        enc_mode(w, cx, arg.mode);
-        enc_ty(w, cx, arg.ty);
+        enc_arg(w, cx, arg);
     }
     w.write_char(']');
     match ft.meta.ret_style {
diff --git a/src/rustc/middle/astencode.rs b/src/rustc/middle/astencode.rs
index 85e4a5f2e18..3172704c971 100644
--- a/src/rustc/middle/astencode.rs
+++ b/src/rustc/middle/astencode.rs
@@ -18,10 +18,8 @@ use std::serialization::DeserializerHelpers;
 use std::prettyprint::Serializer;
 use middle::{ty, typeck};
 use middle::typeck::{method_origin, method_map_entry,
-                        serialize_method_map_entry,
-                        deserialize_method_map_entry,
-                        vtable_res,
-                        vtable_origin};
+                     vtable_res,
+                     vtable_origin};
 use driver::session::session;
 use middle::freevars::{freevar_entry,
                           serialize_freevar_entry,
@@ -385,6 +383,45 @@ impl ast::def: tr {
     }
 }
 
+// ______________________________________________________________________
+// Encoding and decoding of adjustment information
+
+impl ty::AutoAdjustment: tr {
+    fn tr(xcx: extended_decode_ctxt) -> ty::AutoAdjustment {
+        {autoderefs: self.autoderefs,
+         autoref: self.autoref.map(|ar| ar.tr(xcx))}
+    }
+}
+
+impl ty::AutoRef: tr {
+    fn tr(xcx: extended_decode_ctxt) -> ty::AutoRef {
+        {kind: self.kind,
+         region: self.region.tr(xcx),
+         mutbl: self.mutbl}
+    }
+}
+
+impl ty::region: tr {
+    fn tr(xcx: extended_decode_ctxt) -> ty::region {
+        match self {
+            ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
+            ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
+            ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
+            ty::re_static | ty::re_var(*) => self,
+        }
+    }
+}
+
+impl ty::bound_region: tr {
+    fn tr(xcx: extended_decode_ctxt) -> ty::bound_region {
+        match self {
+            ty::br_anon(_) | ty::br_named(_) | ty::br_self => self,
+            ty::br_cap_avoid(id, br) => ty::br_cap_avoid(xcx.tr_id(id),
+                                                         @br.tr(xcx))
+        }
+    }
+}
+
 // ______________________________________________________________________
 // Encoding and decoding of freevar information
 
@@ -416,12 +453,31 @@ trait read_method_map_entry_helper {
     fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry;
 }
 
+fn serialize_method_map_entry(ecx: @e::encode_ctxt,
+                              ebml_w: ebml::Writer,
+                              mme: method_map_entry) {
+    do ebml_w.emit_rec {
+        do ebml_w.emit_rec_field(~"self_arg", 0u) {
+            ebml_w.emit_arg(ecx, mme.self_arg);
+        }
+        do ebml_w.emit_rec_field(~"origin", 1u) {
+            typeck::serialize_method_origin(ebml_w, mme.origin);
+        }
+    }
+}
+
 impl ebml::EbmlDeserializer: read_method_map_entry_helper {
     fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry {
-        let mme = deserialize_method_map_entry(self);
-        {derefs: mme.derefs,
-         self_mode: mme.self_mode,
-         origin: mme.origin.tr(xcx)}
+        do self.read_rec {
+            {self_arg:
+                 self.read_rec_field(~"self_arg", 0u, || {
+                     self.read_arg(xcx)
+                 }),
+             origin:
+                 self.read_rec_field(~"origin", 1u, || {
+                     typeck::deserialize_method_origin(self).tr(xcx)
+                 })}
+        }
     }
 }
 
@@ -445,8 +501,8 @@ impl method_origin: tr {
 // Encoding and decoding vtable_res
 
 fn encode_vtable_res(ecx: @e::encode_ctxt,
-                   ebml_w: ebml::Writer,
-                   dr: typeck::vtable_res) {
+                     ebml_w: ebml::Writer,
+                     dr: typeck::vtable_res) {
     // can't autogenerate this code because automatic serialization of
     // ty::t doesn't work, and there is no way (atm) to have
     // hand-written serialization routines combine with auto-generated
@@ -573,6 +629,7 @@ impl @e::encode_ctxt: get_ty_str_ctxt {
 }
 
 trait ebml_writer_helpers {
+    fn emit_arg(ecx: @e::encode_ctxt, arg: ty::arg);
     fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t);
     fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]);
     fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds);
@@ -581,17 +638,27 @@ trait ebml_writer_helpers {
 
 impl ebml::Writer: ebml_writer_helpers {
     fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t) {
-        e::write_type(ecx, self, ty)
-    }
-
-    fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
-        do self.emit_from_vec(tys) |ty| {
+        do self.emit_opaque {
             e::write_type(ecx, self, ty)
         }
     }
 
+    fn emit_arg(ecx: @e::encode_ctxt, arg: ty::arg) {
+        do self.emit_opaque {
+            tyencode::enc_arg(self.writer, ecx.ty_str_ctxt(), arg);
+        }
+    }
+
+    fn emit_tys(ecx: @e::encode_ctxt, tys: ~[ty::t]) {
+        do self.emit_from_vec(tys) |ty| {
+            self.emit_ty(ecx, ty)
+        }
+    }
+
     fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds) {
-        tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
+        do self.emit_opaque {
+            tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs)
+        }
     }
 
     fn emit_tpbt(ecx: @e::encode_ctxt, tpbt: ty::ty_param_bounds_and_ty) {
@@ -664,7 +731,7 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
         do ebml_w.tag(c::tag_table_node_type) {
             ebml_w.id(id);
             do ebml_w.tag(c::tag_table_val) {
-                e::write_type(ecx, ebml_w, ty)
+                ebml_w.emit_ty(ecx, ty);
             }
         }
     }
@@ -743,7 +810,7 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
         do ebml_w.tag(c::tag_table_method_map) {
             ebml_w.id(id);
             do ebml_w.tag(c::tag_table_val) {
-                serialize_method_map_entry(ebml_w, mme)
+                serialize_method_map_entry(ecx, ebml_w, mme)
             }
         }
     }
@@ -757,13 +824,11 @@ fn encode_side_tables_for_id(ecx: @e::encode_ctxt,
         }
     }
 
-    do option::iter(tcx.borrowings.find(id)) |_borrow| {
-        do ebml_w.tag(c::tag_table_borrowings) {
+    do option::iter(tcx.adjustments.find(id)) |adj| {
+        do ebml_w.tag(c::tag_table_adjustments) {
             ebml_w.id(id);
             do ebml_w.tag(c::tag_table_val) {
-                // N.B. We don't actually serialize borrows as, in
-                // trans, we only care whether a value is borrowed or
-                // not.
+                ty::serialize_AutoAdjustment(ebml_w, *adj)
             }
         }
     }
@@ -782,6 +847,7 @@ impl ebml::Doc: doc_decoder_helpers {
 }
 
 trait ebml_deserializer_decoder_helpers {
+    fn read_arg(xcx: extended_decode_ctxt) -> ty::arg;
     fn read_ty(xcx: extended_decode_ctxt) -> ty::t;
     fn read_tys(xcx: extended_decode_ctxt) -> ~[ty::t];
     fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound];
@@ -791,15 +857,25 @@ trait ebml_deserializer_decoder_helpers {
 
 impl ebml::EbmlDeserializer: ebml_deserializer_decoder_helpers {
 
+    fn read_arg(xcx: extended_decode_ctxt) -> ty::arg {
+        do self.read_opaque |doc| {
+            tydecode::parse_arg_data(
+                doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
+                |a| xcx.tr_def_id(a))
+        }
+    }
+
     fn read_ty(xcx: extended_decode_ctxt) -> ty::t {
         // Note: regions types embed local node ids.  In principle, we
         // should translate these node ids into the new decode
         // context.  However, we do not bother, because region types
         // are not used during trans.
 
-        tydecode::parse_ty_data(
-            self.parent.data, xcx.dcx.cdata.cnum, self.pos, xcx.dcx.tcx,
-            |a| xcx.tr_def_id(a) )
+        do self.read_opaque |doc| {
+            tydecode::parse_ty_data(
+                doc.data, xcx.dcx.cdata.cnum, doc.start, xcx.dcx.tcx,
+                |a| xcx.tr_def_id(a))
+        }
     }
 
     fn read_tys(xcx: extended_decode_ctxt) -> ~[ty::t] {
@@ -807,13 +883,16 @@ impl ebml::EbmlDeserializer: ebml_deserializer_decoder_helpers {
     }
 
     fn read_bounds(xcx: extended_decode_ctxt) -> @~[ty::param_bound] {
-        tydecode::parse_bounds_data(
-            self.parent.data, self.pos, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
-            |a| xcx.tr_def_id(a) )
+        do self.read_opaque |doc| {
+            tydecode::parse_bounds_data(
+                doc.data, doc.start, xcx.dcx.cdata.cnum, xcx.dcx.tcx,
+                |a| xcx.tr_def_id(a))
+        }
     }
 
     fn read_ty_param_bounds_and_ty(xcx: extended_decode_ctxt)
-        -> ty::ty_param_bounds_and_ty {
+        -> ty::ty_param_bounds_and_ty
+    {
         do self.read_rec {
             {
                 bounds: self.read_rec_field(~"bounds", 0u, || {
@@ -881,12 +960,9 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
             } else if tag == (c::tag_table_vtable_map as uint) {
                 dcx.maps.vtable_map.insert(id,
                                            val_dsr.read_vtable_res(xcx));
-            } else if tag == (c::tag_table_borrowings as uint) {
-                // N.B.: we don't actually *serialize* borrows because, in
-                // trans, the only thing we care about is whether a value was
-                // borrowed or not.
-                let borrow = {region: ty::re_static, mutbl: ast::m_imm};
-                dcx.tcx.borrowings.insert(id, borrow);
+            } else if tag == (c::tag_table_adjustments as uint) {
+                let adj = @ty::deserialize_AutoAdjustment(val_dsr).tr(xcx);
+                dcx.tcx.adjustments.insert(id, adj);
             } else {
                 xcx.dcx.tcx.sess.bug(
                     fmt!("unknown tag found in side tables: %x", tag));
diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs
index bc237bab315..ea5bc48445e 100644
--- a/src/rustc/middle/borrowck.rs
+++ b/src/rustc/middle/borrowck.rs
@@ -221,7 +221,7 @@ use syntax::ast_util;
 use syntax::ast_map;
 use syntax::codemap::span;
 use util::ppaux::{ty_to_str, region_to_str, explain_region,
-                  note_and_explain_region};
+                  expr_repr, note_and_explain_region};
 use std::map::{int_hash, HashMap, Set};
 use std::list;
 use std::list::{List, Cons, Nil};
@@ -318,7 +318,7 @@ enum bckerr_code {
     err_mut_uniq,
     err_mut_variant,
     err_root_not_permitted,
-    err_mutbl(ast::mutability, ast::mutability),
+    err_mutbl(ast::mutability),
     err_out_of_root_scope(ty::region, ty::region), // superscope, subscope
     err_out_of_scope(ty::region, ty::region) // superscope, subscope
 }
@@ -344,9 +344,9 @@ impl bckerr_code : cmp::Eq {
                     _ => false
                 }
             }
-            err_mutbl(e0a, e1a) => {
+            err_mutbl(e0a) => {
                 match other {
-                    err_mutbl(e0b, e1b) => e0a == e0b && e1a == e1b,
+                    err_mutbl(e0b) => e0a == e0b,
                     _ => false
                 }
             }
@@ -444,10 +444,6 @@ impl borrowck_ctxt {
         cat_expr(self.tcx, self.method_map, expr)
     }
 
-    fn cat_borrow_of_expr(expr: @ast::expr) -> cmt {
-        cat_borrow_of_expr(self.tcx, self.method_map, expr)
-    }
-
     fn cat_def(id: ast::node_id,
                span: span,
                ty: ty::t,
@@ -482,8 +478,8 @@ impl borrowck_ctxt {
         self.span_err(
             err.cmt.span,
             fmt!("illegal borrow: %s",
-                 self.bckerr_code_to_str(err.code)));
-        self.note_and_explain_bckerr(err.code);
+                 self.bckerr_to_str(err)));
+        self.note_and_explain_bckerr(err);
     }
 
     fn span_err(s: span, m: ~str) {
@@ -506,11 +502,12 @@ impl borrowck_ctxt {
         }
     }
 
-    fn bckerr_code_to_str(code: bckerr_code) -> ~str {
-        match code {
-            err_mutbl(req, act) => {
-                fmt!("creating %s alias to aliasable, %s memory",
-                     self.mut_to_str(req), self.mut_to_str(act))
+    fn bckerr_to_str(err: bckerr) -> ~str {
+        match err.code {
+            err_mutbl(req) => {
+                fmt!("creating %s alias to %s",
+                     self.mut_to_str(req),
+                     self.cmt_to_str(err.cmt))
             }
             err_mut_uniq => {
                 ~"unique value in aliasable, mutable location"
@@ -533,7 +530,8 @@ impl borrowck_ctxt {
         }
     }
 
-    fn note_and_explain_bckerr(code: bckerr_code) {
+    fn note_and_explain_bckerr(err: bckerr) {
+        let code = err.code;
         match code {
             err_mutbl(*) | err_mut_uniq | err_mut_variant |
             err_root_not_permitted => {}
diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs
index e2cbb68e95a..ff7a5210007 100644
--- a/src/rustc/middle/borrowck/check_loans.rs
+++ b/src/rustc/middle/borrowck/check_loans.rs
@@ -75,8 +75,7 @@ fn check_loans(bccx: borrowck_ctxt,
 
 enum assignment_type {
     at_straight_up,
-    at_swap,
-    at_mutbl_ref,
+    at_swap
 }
 
 impl assignment_type : cmp::Eq {
@@ -92,15 +91,13 @@ impl assignment_type {
         // are only assigned once; but it doesn't consider &mut
         match self {
           at_straight_up => true,
-          at_swap => true,
-          at_mutbl_ref => false
+          at_swap => true
         }
     }
     fn ing_form(desc: ~str) -> ~str {
         match self {
           at_straight_up => ~"assigning to " + desc,
-          at_swap => ~"swapping to and from " + desc,
-          at_mutbl_ref => ~"taking mut reference to " + desc
+          at_swap => ~"swapping to and from " + desc
         }
     }
 }
@@ -369,11 +366,9 @@ impl check_loan_ctxt {
         // taking a mutable ref.  that will create a loan of its own
         // which will be checked for compat separately in
         // check_for_conflicting_loans()
-        if at != at_mutbl_ref {
-            for cmt.lp.each |lp| {
-                self.check_for_loan_conflicting_with_assignment(
-                    at, ex, cmt, lp);
-            }
+        for cmt.lp.each |lp| {
+            self.check_for_loan_conflicting_with_assignment(
+                at, ex, cmt, lp);
         }
 
         self.bccx.add_to_mutbl_map(cmt);
@@ -430,8 +425,8 @@ impl check_loan_ctxt {
                 self.tcx().sess.span_err(
                     e.cmt.span,
                     fmt!("illegal borrow unless pure: %s",
-                         self.bccx.bckerr_code_to_str(e.code)));
-                self.bccx.note_and_explain_bckerr(e.code);
+                         self.bccx.bckerr_to_str(e)));
+                self.bccx.note_and_explain_bckerr(e);
                 self.tcx().sess.span_note(
                     sp,
                     fmt!("impure due to %s", msg));
@@ -531,14 +526,12 @@ impl check_loan_ctxt {
                 ty::node_id_to_type(self.tcx(), callee_id));
         do vec::iter2(args, arg_tys) |arg, arg_ty| {
             match ty::resolved_mode(self.tcx(), arg_ty.mode) {
-              ast::by_move => {
-                self.check_move_out(arg);
-              }
-              ast::by_mutbl_ref => {
-                self.check_assignment(at_mutbl_ref, arg);
-              }
-              ast::by_ref | ast::by_copy | ast::by_val => {
-              }
+                ast::by_move => {
+                    self.check_move_out(arg);
+                }
+                ast::by_mutbl_ref | ast::by_ref |
+                ast::by_copy | ast::by_val => {
+                }
             }
         }
     }
@@ -645,19 +638,6 @@ fn check_loans_in_expr(expr: @ast::expr,
             }
         }
       }
-      ast::expr_addr_of(mutbl, base) => {
-        match mutbl {
-          m_const => { /*all memory is const*/ }
-          m_mutbl => {
-            // If we are taking an &mut ptr, make sure the memory
-            // being pointed at is assignable in the first place:
-            self.check_assignment(at_mutbl_ref, base);
-          }
-          m_imm => {
-            // XXX explain why no check is req'd here
-          }
-        }
-      }
       ast::expr_call(f, args, _) => {
         self.check_call(expr, Some(f), f.id, f.span, args);
       }
diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs
index 18e27e79fd1..449913e92b7 100644
--- a/src/rustc/middle/borrowck/gather_loans.rs
+++ b/src/rustc/middle/borrowck/gather_loans.rs
@@ -6,9 +6,9 @@
 // their associated scopes.  In phase two, checking loans, we will then make
 // sure that all of these loans are honored.
 
-use mem_categorization::{opt_deref_kind};
+use mem_categorization::{mem_categorization_ctxt, opt_deref_kind};
 use preserve::{preserve_condition, pc_ok, pc_if_pure};
-use ty::ty_region;
+use ty::{ty_region};
 
 export gather_loans;
 
@@ -94,9 +94,8 @@ fn req_loans_in_expr(ex: @ast::expr,
            ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
 
     // If this expression is borrowed, have to ensure it remains valid:
-    for tcx.borrowings.find(ex.id).each |borrow| {
-        let cmt = self.bccx.cat_borrow_of_expr(ex);
-        self.guarantee_valid(cmt, borrow.mutbl, borrow.region);
+    for tcx.adjustments.find(ex.id).each |adjustments| {
+        self.guarantee_adjustments(ex, adjustments);
     }
 
     // Special checks for various kinds of expressions:
@@ -125,45 +124,9 @@ fn req_loans_in_expr(ex: @ast::expr,
                 self.guarantee_valid(arg_cmt, m_imm,  scope_r);
               }
               ast::by_val => {
-                // Rust's by-val does not actually give ownership to
-                // the callee.  This means that if a pointer type is
-                // passed, it is effectively a borrow, and so the
-                // caller must guarantee that the data remains valid.
-                //
-                // Subtle: we only guarantee that the pointer is valid
-                // and const.  Technically, we ought to pass in the
-                // mutability that the caller expects (e.g., if the
-                // formal argument has type @mut, we should guarantee
-                // validity and mutability, not validity and const).
-                // However, the type system already guarantees that
-                // the caller's mutability is compatible with the
-                // callee, so this is not necessary.  (Note that with
-                // actual borrows, typeck is more liberal and allows
-                // the pointer to be borrowed as immutable even if it
-                // is mutable in the caller's frame, thus effectively
-                // passing the buck onto us to enforce this)
-                //
-                // FIXME (#2493): this handling is not really adequate.
-                // For example, if there is a type like, {f: ~[int]}, we
-                // will ignore it, but we ought to be requiring it to be
-                // immutable (whereas something like {f:int} would be
-                // fine).
-                //
-                match opt_deref_kind(arg_ty.ty) {
-                  Some(deref_ptr(region_ptr(_))) |
-                  Some(deref_ptr(unsafe_ptr)) => {
-                    /* region pointers are (by induction) guaranteed */
-                    /* unsafe pointers are the user's problem */
-                  }
-                  Some(deref_comp(_)) |
-                  None => {
-                    /* not a pointer, no worries */
-                  }
-                  Some(deref_ptr(_)) => {
-                    let arg_cmt = self.bccx.cat_borrow_of_expr(arg);
-                    self.guarantee_valid(arg_cmt, m_const, scope_r);
-                  }
-                }
+                // FIXME (#2493): safety checks would be required here,
+                // but the correct set is really hard to get right,
+                // and modes are going away anyhow.
               }
               ast::by_move | ast::by_copy => {}
             }
@@ -261,6 +224,42 @@ fn req_loans_in_expr(ex: @ast::expr,
 impl gather_loan_ctxt {
     fn tcx() -> ty::ctxt { self.bccx.tcx }
 
+    fn guarantee_adjustments(expr: @ast::expr,
+                             adjustment: &ty::AutoAdjustment) {
+        debug!("guarantee_adjustments(expr=%s, adjustment=%?)",
+               expr_repr(self.tcx(), expr), adjustment);
+        let _i = indenter();
+
+        match adjustment.autoref {
+            None => {
+                debug!("no autoref");
+                return;
+            }
+
+            Some(ref autoref) => {
+                let mcx = &mem_categorization_ctxt {
+                    tcx: self.tcx(),
+                    method_map: self.bccx.method_map};
+                let mut cmt = mcx.cat_expr_autoderefd(expr, adjustment);
+                debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
+
+                match autoref.kind {
+                    ty::AutoPtr => {
+                        self.guarantee_valid(cmt,
+                                             autoref.mutbl,
+                                             autoref.region)
+                    }
+                    ty::AutoSlice => {
+                        let cmt_index = mcx.cat_index(expr, cmt);
+                        self.guarantee_valid(cmt_index,
+                                             autoref.mutbl,
+                                             autoref.region)
+                    }
+                }
+            }
+        }
+    }
+
     // guarantees that addr_of(cmt) will be valid for the duration of
     // `static_scope_r`, or reports an error.  This may entail taking
     // out loans, which will be added to the `req_loan_map`.  This can
@@ -387,25 +386,17 @@ impl gather_loan_ctxt {
     // mutable memory.
     fn check_mutbl(req_mutbl: ast::mutability,
                    cmt: cmt) -> bckres<preserve_condition> {
-        match (req_mutbl, cmt.mutbl) {
-          (m_const, _) |
-          (m_imm, m_imm) |
-          (m_mutbl, m_mutbl) => {
+        if req_mutbl == m_const || req_mutbl == cmt.mutbl {
             Ok(pc_ok)
-          }
-
-          (_, m_const) |
-          (m_imm, m_mutbl) |
-          (m_mutbl, m_imm) => {
+        } else {
             let e = {cmt: cmt,
-                     code: err_mutbl(req_mutbl, cmt.mutbl)};
+                     code: err_mutbl(req_mutbl)};
             if req_mutbl == m_imm {
                 // you can treat mutable things as imm if you are pure
                 Ok(pc_if_pure(e))
             } else {
                 Err(e)
             }
-          }
         }
     }
 
diff --git a/src/rustc/middle/borrowck/loan.rs b/src/rustc/middle/borrowck/loan.rs
index 353022214c5..8d9d7a5796a 100644
--- a/src/rustc/middle/borrowck/loan.rs
+++ b/src/rustc/middle/borrowck/loan.rs
@@ -36,23 +36,40 @@ enum loan_ctxt {
 impl loan_ctxt {
     fn tcx() -> ty::ctxt { self.bccx.tcx }
 
-    fn ok_with_loan_of(cmt: cmt,
-                       scope_ub: ty::region,
-                       mutbl: ast::mutability) -> bckres<()> {
+    fn issue_loan(cmt: cmt,
+                  scope_ub: ty::region,
+                  req_mutbl: ast::mutability) -> bckres<()> {
         if self.bccx.is_subregion_of(self.scope_region, scope_ub) {
-            // Note: all cmt's that we deal with will have a non-none
-            // lp, because the entry point into this routine,
-            // `borrowck_ctxt::loan()`, rejects any cmt with a
-            // none-lp.
-            (*self.loans).push({lp: option::get(cmt.lp),
-                                cmt: cmt,
-                                mutbl: mutbl});
-            Ok(())
+            match req_mutbl {
+                m_mutbl => {
+                    // We do not allow non-mutable data to be loaned
+                    // out as mutable under any circumstances.
+                    if cmt.mutbl != m_mutbl {
+                        return Err({cmt:cmt,
+                                    code:err_mutbl(req_mutbl)});
+                    }
+                }
+                m_const | m_imm => {
+                    // However, mutable data can be loaned out as
+                    // immutable (and any data as const).  The
+                    // `check_loans` pass will then guarantee that no
+                    // writes occur for the duration of the loan.
+                }
+            }
+
+            (*self.loans).push({
+                // Note: cmt.lp must be Some(_) because otherwise this
+                // loan process does not apply at all.
+                lp: cmt.lp.get(),
+                cmt: cmt,
+                mutbl: req_mutbl});
+            return Ok(());
         } else {
             // The loan being requested lives longer than the data
             // being loaned out!
-            Err({cmt:cmt, code:err_out_of_scope(scope_ub,
-                                                self.scope_region)})
+            return Err({cmt:cmt,
+                        code:err_out_of_scope(scope_ub,
+                                              self.scope_region)});
         }
     }
 
@@ -78,7 +95,7 @@ impl loan_ctxt {
           }
           cat_local(local_id) | cat_arg(local_id) => {
             let local_scope_id = self.tcx().region_map.get(local_id);
-            self.ok_with_loan_of(cmt, ty::re_scope(local_scope_id), req_mutbl)
+            self.issue_loan(cmt, ty::re_scope(local_scope_id), req_mutbl)
           }
           cat_stack_upvar(cmt) => {
             self.loan(cmt, req_mutbl) // NDM correct?
@@ -138,7 +155,7 @@ impl loan_ctxt {
         do self.loan(cmt_base, base_mutbl).chain |_ok| {
             // can use static for the scope because the base
             // determines the lifetime, ultimately
-            self.ok_with_loan_of(cmt, ty::re_static, req_mutbl)
+            self.issue_loan(cmt, ty::re_static, req_mutbl)
         }
     }
 
@@ -153,7 +170,7 @@ impl loan_ctxt {
         // could change.
         do self.loan(cmt_base, m_imm).chain |_ok| {
             // can use static, as in loan_stable_comp()
-            self.ok_with_loan_of(cmt, ty::re_static, req_mutbl)
+            self.issue_loan(cmt, ty::re_static, req_mutbl)
         }
     }
 }
diff --git a/src/rustc/middle/borrowck/preserve.rs b/src/rustc/middle/borrowck/preserve.rs
index d1c7e36a27a..6bfb0d091a4 100644
--- a/src/rustc/middle/borrowck/preserve.rs
+++ b/src/rustc/middle/borrowck/preserve.rs
@@ -169,7 +169,7 @@ priv impl &preserve_ctxt {
                   }
                   Err(e) => {
                     debug!("must root @T, err: %s",
-                           self.bccx.bckerr_code_to_str(e.code));
+                           self.bccx.bckerr_to_str(e));
                     self.attempt_root(cmt, base, derefs)
                   }
                 }
diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs
index d7cbd2969a3..0a1de0d11a4 100644
--- a/src/rustc/middle/check_const.rs
+++ b/src/rustc/middle/check_const.rs
@@ -40,7 +40,7 @@ fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) {
     fn is_str(e: @expr) -> bool {
         match e.node {
           expr_vstore(@{node: expr_lit(@{node: lit_str(_), _}), _},
-                      vstore_uniq) => true,
+                      expr_vstore_uniq) => true,
           _ => false
         }
     }
@@ -98,8 +98,8 @@ fn check_expr(sess: session, def_map: resolve::DefMap,
               }
             }
           }
-          expr_vstore(_, vstore_slice(_)) |
-          expr_vstore(_, vstore_fixed(_)) |
+          expr_vstore(_, expr_vstore_slice) |
+          expr_vstore(_, expr_vstore_fixed(_)) |
           expr_vec(_, m_imm) |
           expr_addr_of(m_imm, _) |
           expr_field(*) |
diff --git a/src/rustc/middle/const_eval.rs b/src/rustc/middle/const_eval.rs
index 41bfe9ae026..6c9617d3310 100644
--- a/src/rustc/middle/const_eval.rs
+++ b/src/rustc/middle/const_eval.rs
@@ -87,12 +87,12 @@ fn classify(e: @expr,
               }
 
               ast::expr_vstore(e, vstore) => {
-                match vstore {
-                  ast::vstore_fixed(_) |
-                  ast::vstore_slice(_) => classify(e, def_map, tcx),
-                  ast::vstore_uniq |
-                  ast::vstore_box => non_const
-                }
+                  match vstore {
+                      ast::expr_vstore_fixed(_) |
+                      ast::expr_vstore_slice => classify(e, def_map, tcx),
+                      ast::expr_vstore_uniq |
+                      ast::expr_vstore_box => non_const
+                  }
               }
 
               ast::expr_struct(_, fs, None) |
diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs
index 4e1d67ea365..e81c8f967e0 100644
--- a/src/rustc/middle/kind.rs
+++ b/src/rustc/middle/kind.rs
@@ -332,8 +332,13 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
         // If this is a method call with a by-val argument, we need
         // to check the copy
         match cx.method_map.find(e.id) {
-          Some({self_mode: by_copy, _}) => maybe_copy(cx, lhs, None),
-          _ => ()
+            Some(ref mme) => {
+                match ty::arg_mode(cx.tcx, mme.self_arg) {
+                    by_copy => maybe_copy(cx, lhs, None),
+                    by_ref | by_val | by_mutbl_ref | by_move => ()
+                }
+            }
+            _ => ()
         }
       }
       expr_repeat(element, count_expr, _) => {
@@ -437,11 +442,18 @@ fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool,
         !is_nullary_variant(cx, ex) &&
 
         // borrowed unique value isn't really a copy
-        !cx.tcx.borrowings.contains_key(ex.id)
+        !is_autorefd(cx, ex)
     {
         let ty = ty::expr_ty(cx.tcx, ex);
         check_copy(cx, ex.id, ty, ex.span, implicit_copy, why);
     }
+
+    fn is_autorefd(cx: ctx, ex: @expr) -> bool {
+        match cx.tcx.adjustments.find(ex.id) {
+            None => false,
+            Some(ref adj) => adj.autoref.is_some()
+        }
+    }
 }
 
 fn check_imm_free_var(cx: ctx, def: def, sp: span) {
diff --git a/src/rustc/middle/mem_categorization.rs b/src/rustc/middle/mem_categorization.rs
index c97431eed23..e6d27a7d09e 100644
--- a/src/rustc/middle/mem_categorization.rs
+++ b/src/rustc/middle/mem_categorization.rs
@@ -340,17 +340,6 @@ fn deref_kind(tcx: ty::ctxt, t: ty::t) -> deref_kind {
     }
 }
 
-fn cat_borrow_of_expr(
-    tcx: ty::ctxt,
-    method_map: typeck::method_map,
-    expr: @ast::expr) -> cmt {
-
-    let mcx = &mem_categorization_ctxt {
-        tcx: tcx, method_map: method_map
-    };
-    return mcx.cat_borrow_of_expr(expr);
-}
-
 fn cat_expr(
     tcx: ty::ctxt,
     method_map: typeck::method_map,
@@ -420,33 +409,40 @@ struct mem_categorization_ctxt {
 }
 
 impl &mem_categorization_ctxt {
-    fn cat_borrow_of_expr(expr: @ast::expr) -> cmt {
-        // Any expression can be borrowed (to account for auto-ref on method
-        // receivers), but @, ~, @vec, and ~vec are handled specially.
-        let expr_ty = ty::expr_ty(self.tcx, expr);
-        match ty::get(expr_ty).sty {
-          ty::ty_evec(*) | ty::ty_estr(*) => {
-            self.cat_index(expr, expr)
-          }
+    fn cat_expr(expr: @ast::expr) -> cmt {
+        match self.tcx.adjustments.find(expr.id) {
+            None => {
+                // No adjustments.
+                self.cat_expr_unadjusted(expr)
+            }
 
-          ty::ty_uniq(*) | ty::ty_box(*) | ty::ty_rptr(*) => {
-            let cmt = self.cat_expr(expr);
-            self.cat_deref(expr, cmt, 0u, true).get()
-          }
-
-          /*
-          ty::ty_fn({proto, _}) {
-            self.cat_call(expr, expr, proto)
-          }
-          */
-
-          _ => {
-            self.cat_rvalue(expr, expr_ty)
-          }
+            Some(adjustment) => {
+                match adjustment.autoref {
+                    Some(_) => {
+                        // Equivalent to &*expr or something similar.
+                        // This is an rvalue, effectively.
+                        let expr_ty = ty::expr_ty(self.tcx, expr);
+                        self.cat_rvalue(expr, expr_ty)
+                    }
+                    None => {
+                        // Equivalent to *expr or something similar.
+                        self.cat_expr_autoderefd(expr, adjustment)
+                    }
+                }
+            }
         }
     }
 
-    fn cat_expr(expr: @ast::expr) -> cmt {
+    fn cat_expr_autoderefd(expr: @ast::expr,
+                           adjustment: &ty::AutoAdjustment) -> cmt {
+        let mut cmt = self.cat_expr_unadjusted(expr);
+        for uint::range(1, adjustment.autoderefs+1) |deref| {
+            cmt = self.cat_deref(expr, cmt, deref);
+        }
+        return cmt;
+    }
+
+    fn cat_expr_unadjusted(expr: @ast::expr) -> cmt {
         debug!("cat_expr: id=%d expr=%s",
                expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr()));
 
@@ -459,15 +455,7 @@ impl &mem_categorization_ctxt {
             }
 
             let base_cmt = self.cat_expr(e_base);
-            match self.cat_deref(expr, base_cmt, 0u, true) {
-              Some(cmt) => return cmt,
-              None => {
-                tcx.sess.span_bug(
-                    e_base.span,
-                    fmt!("Explicit deref of non-derefable type `%s`",
-                         ty_to_str(tcx, tcx.ty(e_base))));
-              }
-            }
+            self.cat_deref(expr, base_cmt, 0)
           }
 
           ast::expr_field(base, f_name, _) => {
@@ -475,7 +463,7 @@ impl &mem_categorization_ctxt {
                 return self.cat_method_ref(expr, expr_ty);
             }
 
-            let base_cmt = self.cat_autoderef(base);
+            let base_cmt = self.cat_expr(base);
             self.cat_field(expr, base_cmt, f_name)
           }
 
@@ -484,7 +472,8 @@ impl &mem_categorization_ctxt {
                 return self.cat_rvalue(expr, expr_ty);
             }
 
-            self.cat_index(expr, base)
+            let base_cmt = self.cat_expr(base);
+            self.cat_index(expr, base_cmt)
           }
 
           ast::expr_path(_) => {
@@ -666,11 +655,21 @@ impl &mem_categorization_ctxt {
           mutbl: m, ty: self.tcx.ty(node)}
     }
 
-    fn cat_deref<N:ast_node>(node: N, base_cmt: cmt, derefs: uint,
-                             expl: bool) -> Option<cmt> {
-        do ty::deref(self.tcx, base_cmt.ty, expl).map |mt| {
-            match deref_kind(self.tcx, base_cmt.ty) {
-              deref_ptr(ptr) => {
+    fn cat_deref<N:ast_node>(node: N,
+                             base_cmt: cmt,
+                             deref_cnt: uint) -> cmt {
+        let mt = match ty::deref(self.tcx, base_cmt.ty, true) {
+            Some(mt) => mt,
+            None => {
+                self.tcx.sess.span_bug(
+                    node.span(),
+                    fmt!("Explicit deref of non-derefable type: %s",
+                         ty_to_str(self.tcx, base_cmt.ty)));
+            }
+        };
+
+        match deref_kind(self.tcx, base_cmt.ty) {
+            deref_ptr(ptr) => {
                 let lp = do base_cmt.lp.chain |l| {
                     // Given that the ptr itself is loanable, we can
                     // loan out deref'd uniq ptrs as the data they are
@@ -678,41 +677,38 @@ impl &mem_categorization_ctxt {
                     // Other ptr types admit aliases and are therefore
                     // not loanable.
                     match ptr {
-                      uniq_ptr => {Some(@lp_deref(l, ptr))}
-                      gc_ptr | region_ptr(_) | unsafe_ptr => {None}
+                        uniq_ptr => {Some(@lp_deref(l, ptr))}
+                        gc_ptr | region_ptr(_) | unsafe_ptr => {None}
                     }
                 };
 
                 // for unique ptrs, we inherit mutability from the
                 // owning reference.
                 let m = match ptr {
-                  uniq_ptr => {
-                    self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
-                  }
-                  gc_ptr | region_ptr(_) | unsafe_ptr => {
-                    mt.mutbl
-                  }
+                    uniq_ptr => {
+                        self.inherited_mutability(base_cmt.mutbl, mt.mutbl)
+                    }
+                    gc_ptr | region_ptr(_) | unsafe_ptr => {
+                        mt.mutbl
+                    }
                 };
 
                 @{id:node.id(), span:node.span(),
-                  cat:cat_deref(base_cmt, derefs, ptr), lp:lp,
+                  cat:cat_deref(base_cmt, deref_cnt, ptr), lp:lp,
                   mutbl:m, ty:mt.ty}
-              }
+            }
 
-              deref_comp(comp) => {
+            deref_comp(comp) => {
                 let lp = base_cmt.lp.map(|l| @lp_comp(l, comp) );
                 let m = self.inherited_mutability(base_cmt.mutbl, mt.mutbl);
                 @{id:node.id(), span:node.span(),
                   cat:cat_comp(base_cmt, comp), lp:lp,
                   mutbl:m, ty:mt.ty}
-              }
             }
         }
     }
 
-    fn cat_index(expr: @ast::expr, base: @ast::expr) -> cmt {
-        let base_cmt = self.cat_autoderef(base);
-
+    fn cat_index(expr: @ast::expr, base_cmt: cmt) -> cmt {
         let mt = match ty::index(self.tcx, base_cmt.ty) {
           Some(mt) => mt,
           None => {
@@ -781,25 +777,6 @@ impl &mem_categorization_ctxt {
           mutbl:m_imm, ty:expr_ty}
     }
 
-    fn cat_autoderef(base: @ast::expr) -> cmt {
-        // Creates a string of implicit derefences so long as base is
-        // dereferencable.  n.b., it is important that these dereferences are
-        // associated with the field/index that caused the autoderef (expr).
-        // This is used later to adjust ref counts and so forth in trans.
-
-        // Given something like base.f where base has type @m1 @m2 T, we want
-        // to yield the equivalent categories to (**base).f.
-        let mut cmt = self.cat_expr(base);
-        let mut ctr = 0u;
-        loop {
-            ctr += 1u;
-            match self.cat_deref(base, cmt, ctr, false) {
-              None => return cmt,
-              Some(cmt1) => cmt = cmt1
-            }
-        }
-    }
-
     fn cat_pattern(cmt: cmt, pat: @ast::pat, op: fn(cmt, @ast::pat)) {
 
         op(cmt, pat);
@@ -900,15 +877,9 @@ impl &mem_categorization_ctxt {
 
           ast::pat_box(subpat) | ast::pat_uniq(subpat) |
           ast::pat_region(subpat) => {
-            // @p1, ~p1, &p1
-            match self.cat_deref(subpat, cmt, 0u, true) {
-              Some(subcmt) => {
-                self.cat_pattern(subcmt, subpat, op);
-              }
-              None => {
-                tcx.sess.span_bug(pat.span, ~"Non derefable type");
-              }
-            }
+            // @p1, ~p1
+            let subcmt = self.cat_deref(subpat, cmt, 0);
+            self.cat_pattern(subcmt, subpat, op);
           }
 
           ast::pat_lit(_) | ast::pat_range(_, _) => { /*always ok*/ }
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index 421d7923234..725848592b1 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -500,7 +500,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
 
             let Result {bcx: guard_cx, val} = {
                 do with_scope_result(bcx, e.info(), ~"guard") |bcx| {
-                    expr::trans_to_appropriate_llval(bcx, e)
+                    expr::trans_to_datum(bcx, e).to_result()
                 }
             };
 
diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs
index 3f1fc9aa3d5..4c33bd2b432 100644
--- a/src/rustc/middle/trans/callee.rs
+++ b/src/rustc/middle/trans/callee.rs
@@ -453,8 +453,7 @@ fn trans_args(cx: block, llenv: ValueRef, args: CallArgs, fn_ty: ty::t,
         do vec::iteri(arg_exprs) |i, arg_expr| {
             let arg_val = unpack_result!(bcx, {
                 trans_arg_expr(bcx, arg_tys[i], arg_expr, &mut temp_cleanups,
-                               if i == last { ret_flag } else { None },
-                               0u)
+                               if i == last { ret_flag } else { None })
             });
             vec::push(llargs, arg_val);
         }
@@ -480,18 +479,17 @@ fn trans_arg_expr(bcx: block,
                   formal_ty: ty::arg,
                   arg_expr: @ast::expr,
                   temp_cleanups: &mut ~[ValueRef],
-                  ret_flag: Option<ValueRef>,
-                  derefs: uint)
+                  ret_flag: Option<ValueRef>)
     -> Result
 {
     let _icx = bcx.insn_ctxt("trans_arg_expr");
     let ccx = bcx.ccx();
 
     debug!("trans_arg_expr(formal_ty=(%?,%s), arg_expr=%s, \
-            ret_flag=%?, derefs=%?)",
+            ret_flag=%?)",
            formal_ty.mode, bcx.ty_to_str(formal_ty.ty),
            bcx.expr_to_str(arg_expr),
-           ret_flag.map(|v| bcx.val_str(v)), derefs);
+           ret_flag.map(|v| bcx.val_str(v)));
     let _indenter = indenter();
 
     // translate the arg expr to a datum
@@ -528,20 +526,7 @@ fn trans_arg_expr(bcx: block,
     let mut arg_datum = arg_datumblock.datum;
     let mut bcx = arg_datumblock.bcx;
 
-    debug!("   initial value: %s", arg_datum.to_str(bcx.ccx()));
-
-    // auto-deref value as required (this only applies to method
-    // call receivers) of method
-    if derefs != 0 {
-        arg_datum = arg_datum.autoderef(bcx, arg_expr.id, derefs);
-        debug!("   deref'd value: %s", arg_datum.to_str(bcx.ccx()));
-    };
-
-    // borrow value (convert from @T to &T and so forth)
-    let arg_datum = unpack_datum!(bcx, {
-        adapt_borrowed_value(bcx, arg_datum, arg_expr)
-    });
-    debug!("   borrowed value: %s", arg_datum.to_str(bcx.ccx()));
+    debug!("   arg datum: %s", arg_datum.to_str(bcx.ccx()));
 
     // finally, deal with the various modes
     let arg_mode = ty::resolved_mode(ccx.tcx, formal_ty.mode);
@@ -601,74 +586,3 @@ fn trans_arg_expr(bcx: block,
     return rslt(bcx, val);
 }
 
-// when invoking a method, an argument of type @T or ~T can be implicltly
-// converted to an argument of type &T. Similarly, ~[T] can be converted to
-// &[T] and so on.  If such a conversion (called borrowing) is necessary,
-// then the borrowings table will have an appropriate entry inserted.  This
-// routine consults this table and performs these adaptations.  It returns a
-// new location for the borrowed result as well as a new type for the argument
-// that reflects the borrowed value and not the original.
-fn adapt_borrowed_value(bcx: block,
-                        datum: Datum,
-                        expr: @ast::expr) -> DatumBlock
-{
-    if !expr_is_borrowed(bcx, expr) {
-        return DatumBlock {bcx: bcx, datum: datum};
-    }
-
-    debug!("adapt_borrowed_value(datum=%s, expr=%s)",
-           datum.to_str(bcx.ccx()),
-           bcx.expr_to_str(expr));
-
-    match ty::get(datum.ty).sty {
-        ty::ty_uniq(_) | ty::ty_box(_) => {
-            let body_datum = datum.box_body(bcx);
-            let rptr_datum = body_datum.to_rptr(bcx);
-            return DatumBlock {bcx: bcx, datum: rptr_datum};
-        }
-
-        ty::ty_estr(_) | ty::ty_evec(_, _) => {
-            let ccx = bcx.ccx();
-            let val = datum.to_appropriate_llval(bcx);
-
-            let unit_ty = ty::sequence_element_type(ccx.tcx, datum.ty);
-            let llunit_ty = type_of::type_of(ccx, unit_ty);
-            let (base, len) = datum.get_base_and_len(bcx);
-            let p = alloca(bcx, T_struct(~[T_ptr(llunit_ty), ccx.int_type]));
-
-            debug!("adapt_borrowed_value: adapting %s to %s",
-                   val_str(bcx.ccx().tn, val),
-                   val_str(bcx.ccx().tn, p));
-
-            Store(bcx, base, GEPi(bcx, p, [0u, abi::slice_elt_base]));
-            Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len]));
-
-            // this isn't necessarily the type that rust would assign
-            // but it's close enough for trans purposes, as it will
-            // have the same runtime representation
-            let slice_ty = ty::mk_evec(bcx.tcx(),
-                                       {ty: unit_ty, mutbl: ast::m_imm},
-                                       ty::vstore_slice(ty::re_static));
-
-            return DatumBlock {bcx: bcx,
-                               datum: Datum {val: p,
-                                             mode: ByRef,
-                                             ty: slice_ty,
-                                             source: FromRvalue}};
-        }
-
-        _ => {
-            // Just take a reference. This is basically like trans_addr_of.
-            //
-            // NDM---this code is almost certainly wrong.  I presume its
-            // purpose is auto-ref? What if an @T is autoref'd? No good.
-            let rptr_datum = datum.to_rptr(bcx);
-            return DatumBlock {bcx: bcx, datum: rptr_datum};
-        }
-    }
-
-    fn expr_is_borrowed(bcx: block, e: @ast::expr) -> bool {
-        bcx.tcx().borrowings.contains_key(e.id)
-    }
-}
-
diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs
index b7b84367410..87e27bcf6d7 100644
--- a/src/rustc/middle/trans/common.rs
+++ b/src/rustc/middle/trans/common.rs
@@ -604,7 +604,7 @@ impl block {
     }
 
     fn expr_to_str(e: @ast::expr) -> ~str {
-        fmt!("expr(%d: %s)", e.id, expr_to_str(e, self.sess().intr()))
+        util::ppaux::expr_repr(self.tcx(), e)
     }
 
     fn expr_is_lval(e: @ast::expr) -> bool {
@@ -1180,13 +1180,17 @@ fn path_str(sess: session::session, p: path) -> ~str {
     r
 }
 
+fn monomorphize_type(bcx: block, t: ty::t) -> ty::t {
+    match bcx.fcx.param_substs {
+        Some(substs) => ty::subst_tps(bcx.tcx(), substs.tys, t),
+        _ => { assert !ty::type_has_params(t); t }
+    }
+}
+
 fn node_id_type(bcx: block, id: ast::node_id) -> ty::t {
     let tcx = bcx.tcx();
     let t = ty::node_id_to_type(tcx, id);
-    match bcx.fcx.param_substs {
-      Some(substs) => ty::subst_tps(tcx, substs.tys, t),
-      _ => { assert !ty::type_has_params(t); t }
-    }
+    monomorphize_type(bcx, t)
 }
 
 fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t {
diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs
index 24fd916823a..68d87288326 100644
--- a/src/rustc/middle/trans/consts.rs
+++ b/src/rustc/middle/trans/consts.rs
@@ -312,10 +312,10 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
         let (v, _, _) = const_vec(cx, e, es);
         v
       }
-      ast::expr_vstore(e, ast::vstore_fixed(_)) => {
+      ast::expr_vstore(e, ast::expr_vstore_fixed(_)) => {
         const_expr(cx, e)
       }
-      ast::expr_vstore(sub, ast::vstore_slice(_)) => {
+      ast::expr_vstore(sub, ast::expr_vstore_slice) => {
         match sub.node {
           ast::expr_lit(lit) => {
             match lit.node {
diff --git a/src/rustc/middle/trans/controlflow.rs b/src/rustc/middle/trans/controlflow.rs
index 83c5eb5f5f3..7e81b066693 100644
--- a/src/rustc/middle/trans/controlflow.rs
+++ b/src/rustc/middle/trans/controlflow.rs
@@ -41,7 +41,7 @@ fn trans_if(bcx: block,
 
     let _icx = bcx.insn_ctxt("trans_if");
     let Result {bcx, val: cond_val} =
-        expr::trans_to_appropriate_llval(bcx, cond);
+        expr::trans_to_datum(bcx, cond).to_result();
 
     let then_bcx_in = scope_block(bcx, thn.info(), ~"then");
     let else_bcx_in = scope_block(bcx, els.info(), ~"else");
@@ -121,7 +121,7 @@ fn trans_while(bcx: block, cond: @ast::expr, body: ast::blk)
 
     // compile the condition
     let Result {bcx: cond_bcx_out, val: cond_val} =
-        expr::trans_to_appropriate_llval(cond_bcx_in, cond);
+        expr::trans_to_datum(cond_bcx_in, cond).to_result();
     let cond_bcx_out =
         trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
     CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
@@ -179,7 +179,7 @@ fn trans_log(log_ex: @ast::expr,
     let current_level = Load(bcx, global);
     let level = unpack_result!(bcx, {
         do with_scope_result(bcx, lvl.info(), ~"level") |bcx| {
-            expr::trans_to_appropriate_llval(bcx, lvl)
+            expr::trans_to_datum(bcx, lvl).to_result()
         }
     });
 
@@ -278,7 +278,7 @@ fn trans_check_expr(bcx: block, chk_expr: @ast::expr,
         + ~" failed";
     let Result {bcx, val} = {
         do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| {
-            expr::trans_to_appropriate_llval(bcx, pred_expr)
+            expr::trans_to_datum(bcx, pred_expr).to_result()
         }
     };
     do with_cond(bcx, Not(bcx, val)) |bcx| {
diff --git a/src/rustc/middle/trans/datum.rs b/src/rustc/middle/trans/datum.rs
index 31db9c96bea..791cebad641 100644
--- a/src/rustc/middle/trans/datum.rs
+++ b/src/rustc/middle/trans/datum.rs
@@ -174,8 +174,12 @@ fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum {
     /*!
      *
      * Allocates temporary space on the stack using alloca() and
-     * returns a by-ref Datum pointing to it.  You must arrange
-     * any cleanups etc yourself! */
+     * returns a by-ref Datum pointing to it.  If `zero` is true, the
+     * space will be zeroed when it is allocated; this is normally not
+     * necessary, but in the case of automatic rooting in match
+     * statements it is possible to have temporaries that may not get
+     * initialized if a certain arm is not taken, so we must zero
+     * them. You must arrange any cleanups etc yourself! */
 
     let llty = type_of::type_of(bcx.ccx(), ty);
     let scratch = alloca_maybe_zeroed(bcx, llty, zero);
diff --git a/src/rustc/middle/trans/expr.rs b/src/rustc/middle/trans/expr.rs
index 96a30f62240..303e10ce818 100644
--- a/src/rustc/middle/trans/expr.rs
+++ b/src/rustc/middle/trans/expr.rs
@@ -35,9 +35,6 @@ The two functions above are the most general and can handle any
 situation, but there are a few other functions that are useful
 in specific scenarios:
 
-- `trans_to_appropriate_llval()` can be used when you just want
-  an LLVM ValueRef.  It will return by value if the value in
-  question is immediate, or by ref otherwise.
 - `trans_lvalue()` is exactly like `trans_to_datum()` but it only
   works on lvalues.  This is mostly used as an assertion for those
   places where only an lvalue is expected.  It also guarantees that
@@ -57,10 +54,10 @@ If you invoke `trans_into()`, no cleanup is scheduled for you.  The
 value is written into the given destination and is assumed to be owned
 by that destination.
 
-When you invoke `trans_to_datum()` or `trans_to_appropriate_llval()`
-on an rvalue, the resulting datum/value will have an appropriate
-cleanup scheduled for the innermost cleanup scope.  If you later use
-`move_to()` or `drop_val()`, this cleanup will be canceled.
+When you invoke `trans_to_datum()` on an rvalue, the resulting
+datum/value will have an appropriate cleanup scheduled for the
+innermost cleanup scope.  If you later use `move_to()` or
+`drop_val()`, this cleanup will be canceled.
 
 During the evaluation of an expression, temporary cleanups are created
 and later canceled.  These represent intermediate or partial results
@@ -112,21 +109,22 @@ use base::*;
 use syntax::print::pprust::{expr_to_str};
 use util::ppaux::ty_to_str;
 use util::common::indenter;
+use ty::{AutoPtr, AutoSlice};
 
 // The primary two functions for translating expressions:
 export trans_to_datum, trans_into;
+
+// More specific variants than trans_to_datum/trans_into that are useful
+// in some scenarios:
+export trans_local_var;
+
+// Other helpers, types, and so forth:
+export with_field_tys;
 export Dest, SaveIn, Ignore;
 export cast_type_kind;
 export cast_kind, cast_pointer, cast_integral, cast_float;
 export cast_enum, cast_other;
 
-// More specific variants than trans_to_datum/trans_into that are useful
-// in some scenarios:
-export trans_to_appropriate_llval, trans_lvalue, trans_local_var;
-
-// Other helpers:
-export with_field_tys;
-
 // Destinations
 
 // These are passed around by the code generating functions to track the
@@ -160,15 +158,98 @@ impl Dest : cmp::Eq {
     pure fn ne(&&other: Dest) -> bool { !self.eq(other) }
 }
 
-fn trans_to_appropriate_llval(bcx: block,
-                              expr: @ast::expr) -> common::Result {
-    let mut bcx = bcx;
-    let datum = unpack_datum!(bcx, trans_to_datum(bcx, expr));
-    debug!("trans_to_appropriate_llval(): datum=%s", datum.to_str(bcx.ccx()));
-    rslt(bcx, datum.to_appropriate_llval(bcx))
+fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
+    debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
+
+    return match bcx.tcx().adjustments.find(expr.id) {
+        None => {
+            trans_to_datum_unadjusted(bcx, expr)
+        }
+        Some(adj) => {
+            let mut bcx = bcx;
+            let mut datum = unpack_datum!(bcx, {
+                trans_to_datum_unadjusted(bcx, expr)
+            });
+
+            if adj.autoderefs > 0 {
+                datum = datum.autoderef(bcx, expr.id, adj.autoderefs);
+            }
+
+            datum = match adj.autoref {
+                None => datum,
+                Some(ref autoref) => {
+                    match autoref.kind {
+                        AutoPtr => {
+                            unpack_datum!(bcx, auto_ref(bcx, datum))
+                        }
+                        AutoSlice => {
+                            unpack_datum!(bcx, auto_slice(bcx, datum))
+                        }
+                    }
+                }
+            };
+
+            debug!("after adjustments, datum=%s", datum.to_str(bcx.ccx()));
+
+            return DatumBlock {bcx: bcx, datum: datum};
+        }
+    };
+
+    fn auto_ref(bcx: block, datum: Datum) -> DatumBlock {
+        DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)}
+    }
+
+    fn auto_slice(bcx: block, datum: Datum) -> DatumBlock {
+        // This is not the most efficient thing possible; since slices
+        // are two words it'd be better if this were compiled in
+        // 'dest' mode, but I can't find a nice way to structure the
+        // code and keep it DRY that accommodates that use case at the
+        // moment.
+
+        let tcx = bcx.tcx();
+        let unit_ty = ty::sequence_element_type(tcx, datum.ty);
+        let (base, len) = datum.get_base_and_len(bcx);
+
+        // this type may have a different region/mutability than the
+        // real one, but it will have the same runtime representation
+        let slice_ty = ty::mk_evec(tcx, {ty: unit_ty, mutbl: ast::m_imm},
+                                   ty::vstore_slice(ty::re_static));
+
+        let scratch = scratch_datum(bcx, slice_ty, false);
+        Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
+        Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
+        DatumBlock {bcx: bcx, datum: scratch}
+    }
 }
 
-fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
+fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
+    return match bcx.tcx().adjustments.find(expr.id) {
+        None => trans_into_unadjusted(bcx, expr, dest),
+        Some(_) => {
+            // use trans_to_datum, which is mildly less efficient but
+            // which will perform the adjustments:
+            let datumblock = trans_to_datum(bcx, expr);
+            match dest {
+                Ignore => datumblock.bcx,
+                SaveIn(lldest) => datumblock.store_to(INIT, lldest)
+            }
+        }
+    }
+}
+
+fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
+    return match bcx.tcx().adjustments.find(expr.id) {
+        None => trans_lvalue_unadjusted(bcx, expr),
+        Some(_) => {
+            bcx.sess().span_bug(
+                expr.span,
+                fmt!("trans_lvalue() called on an expression \
+                      with adjustments"));
+        }
+    };
+}
+
+fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
     /*!
      *
      * Translates an expression into a datum.  If this expression
@@ -178,35 +259,38 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
 
     let mut bcx = bcx;
 
-    debug!("trans_to_datum(expr=%s)", bcx.expr_to_str(expr));
+    debug!("trans_to_datum_unadjusted(expr=%s)", bcx.expr_to_str(expr));
     let _indenter = indenter();
 
     debuginfo::update_source_pos(bcx, expr.span);
 
     match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
         ty::LvalueExpr => {
-            return trans_lvalue(bcx, expr);
+            return trans_lvalue_unadjusted(bcx, expr);
         }
 
         ty::RvalueDatumExpr => {
-            let datum = unpack_datum!(bcx, trans_rvalue_datum(bcx, expr));
+            let datum = unpack_datum!(bcx, {
+                trans_rvalue_datum_unadjusted(bcx, expr)
+            });
             datum.add_clean(bcx);
             return DatumBlock {bcx: bcx, datum: datum};
         }
 
         ty::RvalueStmtExpr => {
-            bcx = trans_rvalue_stmt(bcx, expr);
+            bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
             return nil(bcx, expr_ty(bcx, expr));
         }
 
         ty::RvalueDpsExpr => {
             let ty = expr_ty(bcx, expr);
             if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
-                bcx = trans_rvalue_dps(bcx, expr, Ignore);
+                bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
                 return nil(bcx, ty);
             } else {
                 let scratch = scratch_datum(bcx, ty, false);
-                bcx = trans_rvalue_dps(bcx, expr, SaveIn(scratch.val));
+                bcx = trans_rvalue_dps_unadjusted(
+                    bcx, expr, SaveIn(scratch.val));
 
                 // Note: this is not obviously a good idea.  It causes
                 // immediate values to be loaded immediately after a
@@ -231,10 +315,10 @@ fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
     }
 }
 
-fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
+fn trans_into_unadjusted(bcx: block, expr: @ast::expr, dest: Dest) -> block {
     let ty = expr_ty(bcx, expr);
 
-    debug!("trans_into(expr=%s, dest=%s)",
+    debug!("trans_into_unadjusted(expr=%s, dest=%s)",
            bcx.expr_to_str(expr),
            dest.to_str(bcx.ccx()));
     let _indenter = indenter();
@@ -253,39 +337,39 @@ fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
     debug!("expr kind = %?", kind);
     match kind {
         ty::LvalueExpr => {
-            let datumblock = trans_lvalue(bcx, expr);
+            let datumblock = trans_lvalue_unadjusted(bcx, expr);
             match dest {
                 Ignore => datumblock.bcx,
                 SaveIn(lldest) => datumblock.store_to(INIT, lldest)
             }
         }
         ty::RvalueDatumExpr => {
-            let datumblock = trans_rvalue_datum(bcx, expr);
+            let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
             match dest {
                 Ignore => datumblock.drop_val(),
                 SaveIn(lldest) => datumblock.store_to(INIT, lldest)
             }
         }
         ty::RvalueDpsExpr => {
-            return trans_rvalue_dps(bcx, expr, dest);
+            return trans_rvalue_dps_unadjusted(bcx, expr, dest);
         }
         ty::RvalueStmtExpr => {
-            return trans_rvalue_stmt(bcx, expr);
+            return trans_rvalue_stmt_unadjusted(bcx, expr);
         }
     }
 }
 
-fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
-    let _icx = bcx.insn_ctxt("trans_rvalue_datum");
+fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
+    let _icx = bcx.insn_ctxt("trans_rvalue_datum_unadjusted");
 
     trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
 
     match expr.node {
-        ast::expr_vstore(contents, ast::vstore_box) => {
+        ast::expr_vstore(contents, ast::expr_vstore_box) => {
             return tvec::trans_uniq_or_managed_vstore(bcx, heap_shared,
                                                       expr, contents);
         }
-        ast::expr_vstore(contents, ast::vstore_uniq) => {
+        ast::expr_vstore(contents, ast::expr_vstore_uniq) => {
             return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
                                                       expr, contents);
         }
@@ -310,13 +394,14 @@ fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         _ => {
             bcx.tcx().sess.span_bug(
                 expr.span,
-                fmt!("trans_rvalue_datum reached fall-through case: %?",
+                fmt!("trans_rvalue_datum_unadjusted reached \
+                      fall-through case: %?",
                      expr.node));
         }
     }
 }
 
-fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
+fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
     let mut bcx = bcx;
     let _icx = bcx.insn_ctxt("trans_rvalue_stmt");
 
@@ -378,22 +463,25 @@ fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
         _ => {
             bcx.tcx().sess.span_bug(
                 expr.span,
-                fmt!("trans_rvalue_stmt reached fall-through case: %?",
+                fmt!("trans_rvalue_stmt_unadjusted reached \
+                      fall-through case: %?",
                      expr.node));
         }
     };
 }
 
-fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
+fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
+                               dest: Dest) -> block {
     let mut bcx = bcx;
-    let _icx = bcx.insn_ctxt("trans_rvalue_dps");
+    let _icx = bcx.insn_ctxt("trans_rvalue_dps_unadjusted");
     let tcx = bcx.tcx();
 
     trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
 
     match expr.node {
         ast::expr_path(_) => {
-            return trans_def_dps(bcx, expr, bcx.def(expr.id), dest);
+            return trans_def_dps_unadjusted(bcx, expr,
+                                            bcx.def(expr.id), dest);
         }
         ast::expr_if(cond, thn, els) => {
             return controlflow::trans_if(bcx, cond, thn, els, dest);
@@ -416,10 +504,10 @@ fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
         ast::expr_lit(@{node: ast::lit_str(s), _}) => {
             return tvec::trans_lit_str(bcx, expr, s, dest);
         }
-        ast::expr_vstore(contents, ast::vstore_slice(_)) => {
+        ast::expr_vstore(contents, ast::expr_vstore_slice) => {
             return tvec::trans_slice_vstore(bcx, expr, contents, dest);
         }
-        ast::expr_vstore(contents, ast::vstore_fixed(_)) => {
+        ast::expr_vstore(contents, ast::expr_vstore_fixed(_)) => {
             return tvec::trans_fixed_vstore(bcx, expr, contents, dest);
         }
         ast::expr_vec(*) | ast::expr_repeat(*) => {
@@ -527,15 +615,16 @@ fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
         _ => {
             bcx.tcx().sess.span_bug(
                 expr.span,
-                fmt!("trans_rvalue_dps reached fall-through case: %?",
+                fmt!("trans_rvalue_dps_unadjusted reached \
+                      fall-through case: %?",
                      expr.node));
         }
     }
 }
 
-fn trans_def_dps(bcx: block, ref_expr: @ast::expr,
-                 def: ast::def, dest: Dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_def_dps");
+fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
+                            def: ast::def, dest: Dest) -> block {
+    let _icx = bcx.insn_ctxt("trans_def_dps_unadjusted");
     let ccx = bcx.ccx();
 
     let lldest = match dest {
@@ -575,13 +664,13 @@ fn trans_def_dps(bcx: block, ref_expr: @ast::expr,
     }
 }
 
-fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
-    //!
-    //
-    // Translates an lvalue expression, always yielding a by-ref
-    // datum.  Generally speaking you should call trans_to_datum()
-    // instead, but sometimes we call trans_lvalue() directly as a
-    // means of asserting that a particular expression is an lvalue.
+fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
+    /*!
+     *
+     * Translates an lvalue expression, always yielding a by-ref
+     * datum.  Generally speaking you should call trans_to_datum()
+     * instead, but sometimes we call trans_lvalue() directly as a
+     * means of asserting that a particular expression is an lvalue. */
 
     let _icx = bcx.insn_ctxt("trans_lval");
     let mut bcx = bcx;
@@ -798,12 +887,7 @@ fn trans_rec_field(bcx: block,
     let mut bcx = bcx;
     let _icx = bcx.insn_ctxt("trans_rec_field");
 
-    // Translate and autoderef the base expression.  We should have a
-    // record or a struct when we're done, both of which are currently
-    // non-immediate and hence always tracked by reference.
     let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-    let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value);
-
     do with_field_tys(bcx.tcx(), base_datum.ty) |_has_dtor, field_tys| {
         let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
         DatumBlock {
@@ -822,14 +906,11 @@ fn trans_index(bcx: block,
     let base_ty = expr_ty(bcx, base);
     let mut bcx = bcx;
 
-    // Translate and autoderef the base expression.  We should have some sort
-    // of vector (@[], &[], ~[], []/_, etc) when we're done.
     let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-    let base_datum = base_datum.autoderef(bcx, base.id, uint::max_value);
 
     // Translate index expression and cast to a suitable LLVM integer.
     // Rust is less strict than LLVM in this regard.
-    let Result {bcx, val: ix_val} = trans_to_appropriate_llval(bcx, idx);
+    let Result {bcx, val: ix_val} = trans_to_datum(bcx, idx).to_result();
     let ix_size = shape::llsize_of_real(bcx.ccx(), val_ty(ix_val));
     let int_size = shape::llsize_of_real(bcx.ccx(), ccx.int_type);
     let ix_val = {
@@ -989,11 +1070,11 @@ fn trans_unary_datum(bcx: block,
 
     return match op {
         ast::not => {
-            let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
+            let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
             immediate_rvalue_bcx(bcx, Not(bcx, val), un_ty)
         }
         ast::neg => {
-            let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
+            let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
             let llneg = {
                 if ty::type_is_fp(un_ty) {
                     FNeg(bcx, val)
@@ -1153,7 +1234,7 @@ fn trans_lazy_binop(bcx: block,
 
     let Result {bcx: past_lhs, val: lhs} = {
         do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
-            trans_to_appropriate_llval(bcx, a)
+            trans_to_datum(bcx, a).to_result()
         }
     };
 
@@ -1170,7 +1251,7 @@ fn trans_lazy_binop(bcx: block,
     }
     let Result {bcx: past_rhs, val: rhs} = {
         do base::with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| {
-            trans_to_appropriate_llval(bcx, b)
+            trans_to_datum(bcx, b).to_result()
         }
     };
 
@@ -1299,7 +1380,7 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr,
     let t_out = node_id_type(bcx, id);
 
     let mut bcx = bcx;
-    let llexpr = unpack_result!(bcx, trans_to_appropriate_llval(bcx, expr));
+    let llexpr = unpack_result!(bcx, trans_to_datum(bcx, expr).to_result());
     let ll_t_in = val_ty(llexpr);
     let t_in = expr_ty(bcx, expr);
     let ll_t_out = type_of::type_of(ccx, t_out);
diff --git a/src/rustc/middle/trans/meth.rs b/src/rustc/middle/trans/meth.rs
index b923e35765a..a87ae02f8d2 100644
--- a/src/rustc/middle/trans/meth.rs
+++ b/src/rustc/middle/trans/meth.rs
@@ -95,18 +95,17 @@ fn trans_method(ccx: @crate_ctxt,
 fn trans_self_arg(bcx: block, base: @ast::expr,
                   mentry: typeck::method_map_entry) -> Result {
     let _icx = bcx.insn_ctxt("impl::trans_self_arg");
-    let basety = expr_ty(bcx, base);
-    let mode = ast::expl(mentry.self_mode);
     let mut temp_cleanups = ~[];
-    let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, base,
-                                &mut temp_cleanups, None, mentry.derefs);
+    let self_arg = {mode: mentry.self_arg.mode,
+                    ty: monomorphize_type(bcx, mentry.self_arg.ty)};
+    let result = trans_arg_expr(bcx, self_arg, base,
+                                &mut temp_cleanups, None);
 
-    // by-ref self argument should not require cleanup in the case of
-    // other arguments failing:
-    //assert temp_cleanups == ~[];
-    //do vec::iter(temp_cleanups) |c| {
-    //    revoke_clean(bcx, c)
-    //}
+    // FIXME(#3446)---this is wrong, actually.  The temp_cleanups
+    // should be revoked only after all arguments have been passed.
+    for temp_cleanups.each |c| {
+        revoke_clean(bcx, c)
+    }
 
     return result;
 }
@@ -120,14 +119,14 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
         typeck::method_static(did) => {
             let callee_fn = callee::trans_fn_ref(bcx, did, callee_id);
             let Result {bcx, val} = trans_self_arg(bcx, self, mentry);
-
+            let tcx = bcx.tcx();
             Callee {
                 bcx: bcx,
                 data: Method(MethodData {
                     llfn: callee_fn.llfn,
                     llself: val,
                     self_ty: node_id_type(bcx, self.id),
-                    self_mode: mentry.self_mode
+                    self_mode: ty::resolved_mode(tcx, mentry.self_arg.mode)
                 })
             }
         }
@@ -144,7 +143,7 @@ fn trans_method_callee(bcx: block, callee_id: ast::node_id,
             }
         }
         typeck::method_trait(_, off) => {
-            trans_trait_callee(bcx, callee_id, off, self, mentry.derefs)
+            trans_trait_callee(bcx, callee_id, off, self)
         }
     }
 }
@@ -176,7 +175,9 @@ fn trans_static_method_callee(bcx: block,
     let vtbls = resolve_vtables_in_fn_ctxt(
         bcx.fcx, ccx.maps.vtable_map.get(callee_id));
 
-    match vtbls[0] { // is index 0 always the one we want?
+    // FIXME(#3446) -- I am pretty sure index 0 is not the right one,
+    // if the static method is implemented on a generic type. (NDM)
+    match vtbls[0] {
         typeck::vtable_static(impl_did, rcvr_substs, rcvr_origins) => {
 
             let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
@@ -276,18 +277,19 @@ fn trans_monomorphized_callee(bcx: block,
           let llfn_val = PointerCast(bcx, callee.llfn, llfn_ty);
 
           // combine the self environment with the rest
+          let tcx = bcx.tcx();
           Callee {
               bcx: bcx,
               data: Method(MethodData {
                   llfn: llfn_val,
                   llself: llself_val,
                   self_ty: node_id_type(bcx, base.id),
-                  self_mode: mentry.self_mode
+                  self_mode: ty::resolved_mode(tcx, mentry.self_arg.mode)
               })
           }
       }
       typeck::vtable_trait(*) => {
-          trans_trait_callee(bcx, callee_id, n_method, base, mentry.derefs)
+          trans_trait_callee(bcx, callee_id, n_method, base)
       }
       typeck::vtable_param(*) => {
           fail ~"vtable_param left in monomorphized function's vtable substs";
@@ -388,8 +390,7 @@ fn combine_impl_and_methods_origins(bcx: block,
 fn trans_trait_callee(bcx: block,
                       callee_id: ast::node_id,
                       n_method: uint,
-                      self_expr: @ast::expr,
-                      autoderefs: uint)
+                      self_expr: @ast::expr)
     -> Callee
 {
     //!
@@ -404,7 +405,6 @@ fn trans_trait_callee(bcx: block,
     let _icx = bcx.insn_ctxt("impl::trans_trait_callee");
     let mut bcx = bcx;
     let self_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr));
-    let self_datum = self_datum.autoderef(bcx, self_expr.id, autoderefs);
     let llpair = self_datum.to_ref_llval(bcx);
     let callee_ty = node_id_type(bcx, callee_id);
     trans_trait_callee_from_llval(bcx, callee_ty, n_method, llpair)
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 2ad5412e758..2edf0178e32 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -19,7 +19,9 @@ use syntax::ast::*;
 use syntax::print::pprust::*;
 use util::ppaux::{ty_to_str, proto_ty_to_str, tys_to_str};
 use std::serialization::{serialize_Option,
-                            deserialize_Option};
+                         deserialize_Option,
+                         serialize_uint,
+                         deserialize_uint};
 
 export TyVid, IntVid, FnVid, RegionVid, vid;
 export br_hashmap;
@@ -155,6 +157,7 @@ export closure_kind;
 export ck_block;
 export ck_box;
 export ck_uniq;
+export param_ty;
 export param_bound, param_bounds, bound_copy, bound_owned;
 export param_bounds_to_str, param_bound_to_str;
 export bound_send, bound_trait;
@@ -192,6 +195,8 @@ export opt_region_variance;
 export serialize_opt_region_variance, deserialize_opt_region_variance;
 export determine_inherited_purity;
 export provided_trait_methods;
+export AutoAdjustment, serialize_AutoAdjustment, deserialize_AutoAdjustment;
+export AutoRef, AutoRefKind, AutoSlice, AutoPtr;
 
 // Data types
 
@@ -287,19 +292,26 @@ impl region_variance: cmp::Eq {
     pure fn ne(&&other: region_variance) -> bool { !self.eq(other) }
 }
 
-// N.B.: Borrows from inlined content are not accurately deserialized.  This
-// is because we don't need the details in trans, we only care if there is an
-// entry in the table or not.
-type borrow = {
-    region: ty::region,
+#[auto_serialize]
+type AutoAdjustment = {
+    autoderefs: uint,
+    autoref: Option<AutoRef>
+};
+
+#[auto_serialize]
+type AutoRef = {
+    kind: AutoRefKind,
+    region: region,
     mutbl: ast::mutability
 };
 
-impl borrow : cmp::Eq {
-    pure fn eq(&&other: borrow) -> bool {
-        self.region == other.region && self.mutbl == other.mutbl
-    }
-    pure fn ne(&&other: borrow) -> bool { !self.eq(other) }
+#[auto_serialize]
+enum AutoRefKind {
+    /// Convert from @[]/~[] to &[] (or str)
+    AutoSlice,
+
+    /// Convert from T to &T
+    AutoPtr
 }
 
 type ctxt =
@@ -340,8 +352,7 @@ type ctxt =
       trait_method_cache: HashMap<def_id, @~[method]>,
       ty_param_bounds: HashMap<ast::node_id, param_bounds>,
       inferred_modes: HashMap<ast::node_id, ast::mode>,
-      // maps the id of borrowed expr to scope of borrowed ptr
-      borrowings: HashMap<ast::node_id, borrow>,
+      adjustments: HashMap<ast::node_id, @AutoAdjustment>,
       normalized_cache: HashMap<t, t>,
       lang_items: middle::lang_items::LanguageItems};
 
@@ -496,6 +507,7 @@ impl param_ty : to_bytes::IterBytes {
 
 
 /// Representation of regions:
+#[auto_serialize]
 enum region {
     /// Bound regions are found (primarily) in function types.  They indicate
     /// region parameters that have yet to be replaced with actual regions
@@ -523,6 +535,7 @@ enum region {
     re_var(RegionVid)
 }
 
+#[auto_serialize]
 enum bound_region {
     /// The self region for classes, impls (&T in a type defn or &self/T)
     br_self,
@@ -533,35 +546,37 @@ enum bound_region {
     /// Named region parameters for functions (a in &a/T)
     br_named(ast::ident),
 
-    /// Handles capture-avoiding substitution in a rather subtle case.  If you
-    /// have a closure whose argument types are being inferred based on the
-    /// expected type, and the expected type includes bound regions, then we
-    /// will wrap those bound regions in a br_cap_avoid() with the id of the
-    /// fn expression.  This ensures that the names are not "captured" by the
-    /// enclosing scope, which may define the same names.  For an example of
-    /// where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs
-    /// and regions-ret-borrowed-1.rs.
+    /**
+     * Handles capture-avoiding substitution in a rather subtle case.  If you
+     * have a closure whose argument types are being inferred based on the
+     * expected type, and the expected type includes bound regions, then we
+     * will wrap those bound regions in a br_cap_avoid() with the id of the
+     * fn expression.  This ensures that the names are not "captured" by the
+     * enclosing scope, which may define the same names.  For an example of
+     * where this comes up, see src/test/compile-fail/regions-ret-borrowed.rs
+     * and regions-ret-borrowed-1.rs. */
     br_cap_avoid(ast::node_id, @bound_region),
 }
 
 type opt_region = Option<region>;
 
-/// The type substs represents the kinds of things that can be substituted to
-/// convert a polytype into a monotype.  Note however that substituting bound
-/// regions other than `self` is done through a different mechanism.
-///
-/// `tps` represents the type parameters in scope.  They are indexed according
-/// to the order in which they were declared.
-///
-/// `self_r` indicates the region parameter `self` that is present on nominal
-/// types (enums, classes) declared as having a region parameter.  `self_r`
-/// should always be none for types that are not region-parameterized and
-/// Some(_) for types that are.  The only bound region parameter that should
-/// appear within a region-parameterized type is `self`.
-///
-/// `self_ty` is the type to which `self` should be remapped, if any.  The
-/// `self` type is rather funny in that it can only appear on traits and
-/// is always substituted away to the implementing type for a trait.
+/**
+ * The type substs represents the kinds of things that can be substituted to
+ * convert a polytype into a monotype.  Note however that substituting bound
+ * regions other than `self` is done through a different mechanism:
+ *
+ * - `tps` represents the type parameters in scope.  They are indexed
+ *   according to the order in which they were declared.
+ *
+ * - `self_r` indicates the region parameter `self` that is present on nominal
+ *   types (enums, classes) declared as having a region parameter.  `self_r`
+ *   should always be none for types that are not region-parameterized and
+ *   Some(_) for types that are.  The only bound region parameter that should
+ *   appear within a region-parameterized type is `self`.
+ *
+ * - `self_ty` is the type to which `self` should be remapped, if any.  The
+ *   `self` type is rather funny in that it can only appear on traits and is
+ *   always substituted away to the implementing type for a trait. */
 type substs = {
     self_r: opt_region,
     self_ty: Option<ty::t>,
@@ -650,6 +665,7 @@ enum param_bound {
 enum TyVid = uint;
 enum IntVid = uint;
 enum FnVid = uint;
+#[auto_serialize]
 enum RegionVid = uint;
 
 enum InferTy {
@@ -842,7 +858,7 @@ fn mk_ctxt(s: session::session,
       trait_method_cache: new_def_hash(),
       ty_param_bounds: map::int_hash(),
       inferred_modes: map::int_hash(),
-      borrowings: map::int_hash(),
+      adjustments: map::int_hash(),
       normalized_cache: new_ty_hash(),
       lang_items: move lang_items}
 }
@@ -1339,7 +1355,7 @@ fn substs_to_str(cx: ctxt, substs: &substs) -> ~str {
     fmt!("substs(self_r=%s, self_ty=%s, tps=%?)",
          substs.self_r.map_default(~"none", |r| region_to_str(cx, r)),
          substs.self_ty.map_default(~"none", |t| ty_to_str(cx, t)),
-         substs.tps.map(|t| ty_to_str(cx, t)))
+         tys_to_str(cx, substs.tps))
 }
 
 fn param_bound_to_str(cx: ctxt, pb: &param_bound) -> ~str {
@@ -2939,8 +2955,8 @@ fn expr_kind(tcx: ctxt,
         ast::expr_unary_move(*) |
         ast::expr_repeat(*) |
         ast::expr_lit(@{node: lit_str(_), _}) |
-        ast::expr_vstore(_, ast::vstore_slice(_)) |
-        ast::expr_vstore(_, ast::vstore_fixed(_)) |
+        ast::expr_vstore(_, ast::expr_vstore_slice) |
+        ast::expr_vstore(_, ast::expr_vstore_fixed(_)) |
         ast::expr_vec(*) => {
             RvalueDpsExpr
         }
@@ -2990,8 +3006,8 @@ fn expr_kind(tcx: ctxt,
         ast::expr_unary(*) |
         ast::expr_addr_of(*) |
         ast::expr_binary(*) |
-        ast::expr_vstore(_, ast::vstore_box) |
-        ast::expr_vstore(_, ast::vstore_uniq) => {
+        ast::expr_vstore(_, ast::expr_vstore_box) |
+        ast::expr_vstore(_, ast::expr_vstore_uniq) => {
             RvalueDatumExpr
         }
 
diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs
index 439c0052259..4b146d89cf8 100644
--- a/src/rustc/middle/typeck.rs
+++ b/src/rustc/middle/typeck.rs
@@ -60,7 +60,7 @@ use std::serialization::{serialize_uint, deserialize_uint};
 use vec::each;
 use syntax::print::pprust::*;
 use util::ppaux::{ty_to_str, tys_to_str, region_to_str,
-                     bound_region_to_str, vstore_to_str};
+                  bound_region_to_str, vstore_to_str, expr_repr};
 use util::common::{indent, indenter};
 use std::list;
 use list::{List, Nil, Cons};
@@ -86,7 +86,7 @@ enum method_origin {
     // method invoked on a type parameter with a bounded trait
     method_param(method_param),
 
-    // method invoked on a boxed trait
+    // method invoked on a trait instance
     method_trait(ast::def_id, uint),
 }
 
@@ -108,13 +108,10 @@ type method_param = {
     bound_num: uint
 };
 
-#[auto_serialize]
 type method_map_entry = {
-    // number of derefs that are required on the receiver
-    derefs: uint,
-
-    // the mode by which the self parameter needs to be passed
-    self_mode: ast::rmode,
+    // the type and mode of the self parameter, which is not reflected
+    // in the fn type (FIXME #3446)
+    self_arg: ty::arg,
 
     // method details being invoked
     origin: method_origin
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index de803402a13..9af764511d3 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -102,7 +102,7 @@ struct inherited {
     locals: HashMap<ast::node_id, TyVid>,
     node_types: HashMap<ast::node_id, ty::t>,
     node_type_substs: HashMap<ast::node_id, ty::substs>,
-    borrowings: HashMap<ast::node_id, ty::borrow>,
+    adjustments: HashMap<ast::node_id, @ty::AutoAdjustment>
 }
 
 struct fn_ctxt {
@@ -143,7 +143,7 @@ fn blank_inherited(ccx: @crate_ctxt) -> @inherited {
         locals: int_hash(),
         node_types: map::int_hash(),
         node_type_substs: map::int_hash(),
-        borrowings: map::int_hash()
+        adjustments: map::int_hash()
     }
 }
 
@@ -604,6 +604,7 @@ impl @fn_ctxt {
                node_id, ty_to_str(self.tcx(), ty), self.tag());
         self.inh.node_types.insert(node_id, ty);
     }
+
     fn write_substs(node_id: ast::node_id, +substs: ty::substs) {
         if !ty::substs_is_noop(&substs) {
             debug!("write_substs(%d, %s) in fcx %s",
@@ -613,12 +614,24 @@ impl @fn_ctxt {
             self.inh.node_type_substs.insert(node_id, substs);
         }
     }
+
     fn write_ty_substs(node_id: ast::node_id, ty: ty::t,
                        +substs: ty::substs) {
         let ty = ty::subst(self.tcx(), &substs, ty);
         self.write_ty(node_id, ty);
         self.write_substs(node_id, substs);
     }
+
+    fn write_autoderef_adjustment(node_id: ast::node_id, derefs: uint) {
+        if derefs == 0 { return; }
+        self.write_adjustment(node_id, @{autoderefs: derefs, autoref: None});
+    }
+
+    fn write_adjustment(node_id: ast::node_id, adj: @ty::AutoAdjustment) {
+        debug!("write_adjustment(node_id=%?, adj=%?)", node_id, adj);
+        self.inh.adjustments.insert(node_id, adj);
+    }
+
     fn write_nil(node_id: ast::node_id) {
         self.write_ty(node_id, ty::mk_nil(self.tcx()));
     }
@@ -630,14 +643,17 @@ impl @fn_ctxt {
         ast_ty_to_ty(self, self, ast_t)
     }
 
+    fn expr_to_str(expr: @ast::expr) -> ~str {
+        expr_repr(self.tcx(), expr)
+    }
+
     fn expr_ty(ex: @ast::expr) -> ty::t {
         match self.inh.node_types.find(ex.id) {
             Some(t) => t,
             None => {
                 self.tcx().sess.bug(
-                    fmt!("no type for expr %d (%s) in fcx %s",
-                         ex.id, expr_to_str(ex, self.ccx.tcx.sess.intr()),
-                         self.tag()));
+                    fmt!("no type for %s in fcx %s",
+                         self.expr_to_str(ex), self.tag()));
             }
         }
     }
@@ -691,22 +707,15 @@ impl @fn_ctxt {
         infer::can_mk_subty(self.infcx(), sub, sup)
     }
 
-    fn mk_assignty(expr: @ast::expr, borrow_lb: ast::node_id,
-                   sub: ty::t, sup: ty::t) -> Result<(), ty::type_err> {
+    fn mk_assignty(expr: @ast::expr, sub: ty::t, sup: ty::t)
+        -> Result<(), ty::type_err>
+    {
         match infer::mk_assignty(self.infcx(), false, expr.span, sub, sup) {
             Ok(None) => result::Ok(()),
             Err(e) => result::Err(e),
-            Ok(Some(borrow)) => {
-                match self.mk_subr(true, expr.span,
-                                   ty::re_scope(borrow_lb), borrow.region) {
-                    Err(e) => Err(e),
-                    Ok(()) => {
-                        debug!("inserting borrowing of expr %?: %?",
-                               expr.id, borrow);
-                        self.inh.borrowings.insert(expr.id, borrow);
-                        Ok(())
-                    }
-                }
+            Ok(Some(adjustment)) => {
+                self.write_adjustment(expr.id, adjustment);
+                Ok(())
             }
         }
     }
@@ -753,9 +762,18 @@ impl @fn_ctxt {
     }
 }
 
-fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
+fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> (ty::t, uint) {
+    /*!
+     *
+     * Autoderefs the type `t` as many times as possible, returning
+     * a new type and a counter for how many times the type was
+     * deref'd.  If the counter is non-zero, the receiver is responsible
+     * for inserting an AutoAdjustment record into `tcx.adjustments`
+     * so that trans/borrowck/etc know about this autoderef. */
+
     let mut t1 = t;
     let mut enum_dids = ~[];
+    let mut autoderefs = 0;
     loop {
         let sty = structure_of(fcx, sp, t1);
 
@@ -773,13 +791,14 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
             }
             ty::ty_enum(did, _) => {
                 // Watch out for a type like `enum t = @t`.  Such a
-                // type would otherwise infinitely auto-deref.  This
-                // is the only autoderef loop that needs to be
+                // type would otherwise infinitely auto-deref.  Only
+                // autoderef loops during typeck (basically, this one
+                // and the loops in typeck::check::method) need to be
                 // concerned with this, as an error will be reported
                 // on the enum definition as well because the enum is
                 // not instantiable.
                 if vec::contains(enum_dids, did) {
-                    return t1;
+                    return (t1, autoderefs);
                 }
                 vec::push(enum_dids, did);
             }
@@ -788,8 +807,13 @@ fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t {
 
         // Otherwise, deref if type is derefable:
         match ty::deref_sty(fcx.ccx.tcx, &sty, false) {
-            None => return t1,
-            Some(mt) => t1 = mt.ty
+            None => {
+                return (t1, autoderefs);
+            }
+            Some(mt) => {
+                autoderefs += 1;
+                t1 = mt.ty
+            }
         }
     };
 }
@@ -904,16 +928,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                            expected: Option<ty::t>,
                            unifier: fn()) -> bool {
 
-    debug!(
-        ">> typechecking expr %d (%s)",
-        expr.id, syntax::print::pprust::expr_to_str(expr,
-                                                    fcx.ccx.tcx.sess.intr()));
+    debug!(">> typechecking %s", fcx.expr_to_str(expr));
 
     // A generic function to factor out common logic from call and
     // overloaded operations
     fn check_call_inner(
-        fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
+        fcx: @fn_ctxt,
+        sp: span,
+        call_expr_id: ast::node_id,
+        in_fty: ty::t,
         callee_expr: @ast::expr,
+        check_args: bool,
         args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
 
         let mut bot = false;
@@ -956,7 +981,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // Grab the argument types, supplying fresh type variables
         // if the wrong number of arguments were supplied
         let expected_arg_count = vec::len(fn_ty.sig.inputs);
-        let arg_tys = if expected_arg_count == supplied_arg_count {
+        let formal_tys = if expected_arg_count == supplied_arg_count {
             fn_ty.sig.inputs.map(|a| a.ty)
         } else {
             fcx.ccx.tcx.sess.span_err(
@@ -983,6 +1008,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // of arguments when we typecheck the functions. This isn't really the
         // right way to do this.
         for [false, true]/_.each |check_blocks| {
+            debug!("check_blocks=%b", check_blocks);
+
             // More awful hacks: before we check the blocks, try to do
             // an "opportunistic" vtable resolution of any trait
             // bounds on the call.
@@ -990,19 +1017,26 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
                 vtable::early_resolve_expr(callee_expr, fcx, true);
             }
 
-            for args.eachi |i, a| {
-                let is_block = match a.node {
-                  ast::expr_fn_block(*) | ast::expr_loop_body(*) |
-                  ast::expr_do_body(*) => true,
-                  _ => false
+            for args.eachi |i, arg| {
+                let is_block = match arg.node {
+                    ast::expr_fn_block(*) | ast::expr_loop_body(*) |
+                    ast::expr_do_body(*) => true,
+                    _ => false
                 };
+
                 if is_block == check_blocks {
-                    let arg_ty = arg_tys[i];
-                    bot |= check_expr_with_unifier(
-                        fcx, a, Some(arg_ty),
-                        || demand::assign(fcx, a.span, call_expr_id,
-                                          arg_ty, a)
+                    debug!("checking the argument");
+                    let formal_ty = formal_tys[i];
+
+                    if check_args {
+                        bot |= check_expr_with_unifier(
+                            fcx, arg, Some(formal_ty),
+                            || demand::assign(fcx, arg.span, formal_ty, arg)
                         );
+                    } else {
+                        demand::assign(fcx, arg.span, formal_ty, arg);
+                        bot |= ty::type_is_bot(fcx.expr_ty(arg));
+                    }
                 }
             }
         }
@@ -1036,7 +1070,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         // Call the generic checker.
         let fty = {
             let r = check_call_inner(fcx, sp, call_expr_id,
-                                     fn_ty, f, args);
+                                     fn_ty, f, true, args);
             bot |= r.bot;
             r.fty
         };
@@ -1092,16 +1126,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
     fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
                         self_ex: @ast::expr, self_t: ty::t,
-                        opname: ast::ident, args: ~[@ast::expr])
-        -> Option<(ty::t, bool)> {
-        let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id,
-                     op_ex.callee_id, opname, self_t, ~[], false);
-        match lkup.method() {
+                        opname: ast::ident, check_args: bool,
+                        args: ~[@ast::expr])
+        -> Option<(ty::t, bool)>
+    {
+        match method::lookup(fcx, op_ex, self_ex,
+                             op_ex.callee_id, opname, self_t, ~[]) {
           Some(origin) => {
             let {fty: method_ty, bot: bot} = {
                 let method_ty = fcx.node_ty(op_ex.callee_id);
                 check_call_inner(fcx, op_ex.span, op_ex.id,
-                                 method_ty, op_ex, args)
+                                 method_ty, op_ex, check_args, args)
             };
             fcx.ccx.method_map.insert(op_ex.id, origin);
             Some((ty::ty_fn_ret(method_ty), bot))
@@ -1109,71 +1144,95 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
           _ => None
         }
     }
+
+    fn check_rel_op(fcx: @fn_ctxt,
+                    expr: @ast::expr,
+                    op: ast::binop,
+                    lhs: @ast::expr,
+                    rhs: @ast::expr) -> bool
+    {
+        // We know that only things of equal type can be compared, so
+        // go ahead and unify the two types before we do anything else
+        // (with other operators, we must be much more careful not to
+        // make assumptions, due to the possibility of operator
+        // overloading; but overloaded == still enforces the
+        // requirement that only equal types are compared).
+        let tcx = fcx.ccx.tcx;
+        let lhs_bot = check_expr(fcx, lhs, None);
+        let lhs_t = fcx.expr_ty(lhs);
+        let rhs_bot = check_expr_with(fcx, rhs, lhs_t);
+
+        let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
+        if ty::is_binopable(tcx, lhs_t, op) {
+            let result_t = ty::mk_bool(tcx);
+            fcx.write_ty(expr.id, result_t);
+            return lhs_bot | rhs_bot;
+        }
+
+        let (result, rhs_bot) =
+            check_user_binop(fcx, expr, lhs, lhs_t, op, false, rhs);
+        fcx.write_ty(expr.id, result);
+        return lhs_bot | rhs_bot;
+    }
+
     // could be either a expr_binop or an expr_assign_binop
     fn check_binop(fcx: @fn_ctxt, expr: @ast::expr,
                    op: ast::binop,
                    lhs: @ast::expr,
                    rhs: @ast::expr) -> bool {
         let tcx = fcx.ccx.tcx;
-        let lhs_bot = check_expr(fcx, lhs, None);
-        let lhs_t = fcx.expr_ty(lhs);
 
-        // Hack: Unify the two sides if this is a relational operator.
+        // Relational operators are different for type inferencing
+        // reasons.
         match op {
             ast::eq | ast::ne | ast::lt | ast::le | ast::ge | ast::gt => {
-                check_expr_with(fcx, rhs, lhs_t);
+                return check_rel_op(fcx, expr, op, lhs, rhs);
             }
             _ => {}
         }
 
+        let lhs_bot = check_expr(fcx, lhs, None);
+        let lhs_t = fcx.expr_ty(lhs);
         let lhs_t = structurally_resolved_type(fcx, lhs.span, lhs_t);
-        return match (op, ty::get(lhs_t).sty) {
-          (_, _) if ty::type_is_integral(lhs_t) &&
-          ast_util::is_shift_binop(op) => {
+
+        if ty::type_is_integral(lhs_t) && ast_util::is_shift_binop(op) {
             // Shift is a special case: rhs can be any integral type
             let rhs_bot = check_expr(fcx, rhs, None);
             let rhs_t = fcx.expr_ty(rhs);
             require_integral(fcx, rhs.span, rhs_t);
             fcx.write_ty(expr.id, lhs_t);
-            lhs_bot | rhs_bot
-          }
+            return lhs_bot | rhs_bot;
+        }
 
-          (_, _) if ty::is_binopable(tcx, lhs_t, op) => {
+        if ty::is_binopable(tcx, lhs_t, op) {
             let tvar = fcx.infcx().next_ty_var();
             demand::suptype(fcx, expr.span, tvar, lhs_t);
             let rhs_bot = check_expr_with(fcx, rhs, tvar);
-            let result_t = match op {
-              ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
-              ast::gt => {
-                if !ty::type_is_scalar(lhs_t) {
-                    fcx.ccx.tcx.sess.span_bug(expr.span,
-                                              ~"non-scalar compare");
-                }
-                ty::mk_bool(fcx.ccx.tcx)
-              }
-              _ => lhs_t
-            };
+            let result_t = lhs_t;
             fcx.write_ty(expr.id, result_t);
-            if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
-            else { lhs_bot }
-          }
+            return {
+                if !ast_util::lazy_binop(op) { lhs_bot | rhs_bot }
+                else { lhs_bot }
+            };
+        }
 
-          (_, _) => {
-            let (result, rhs_bot) =
-                check_user_binop(fcx, expr, lhs, lhs_t, op, rhs);
-            fcx.write_ty(expr.id, result);
-            lhs_bot | rhs_bot
-          }
-        };
+        let (result, rhs_bot) =
+            check_user_binop(fcx, expr, lhs, lhs_t, op, true, rhs);
+        fcx.write_ty(expr.id, result);
+        return lhs_bot | rhs_bot;
     }
+
     fn check_user_binop(fcx: @fn_ctxt, ex: @ast::expr,
                         lhs_expr: @ast::expr, lhs_resolved_t: ty::t,
-                        op: ast::binop, rhs: @ast::expr) -> (ty::t, bool) {
+                        op: ast::binop, check_rhs: bool,
+                        rhs: @ast::expr) -> (ty::t, bool)
+    {
         let tcx = fcx.ccx.tcx;
         match ast_util::binop_to_method_name(op) {
           Some(name) => {
             match lookup_op_method(fcx, ex, lhs_expr, lhs_resolved_t,
-                                   fcx.tcx().sess.ident_of(name), ~[rhs]) {
+                                   fcx.tcx().sess.ident_of(name),
+                                   check_rhs, ~[rhs]) {
               Some(pair) => return pair,
               _ => ()
             }
@@ -1202,11 +1261,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
 
         (lhs_resolved_t, false)
     }
+
     fn check_user_unop(fcx: @fn_ctxt, op_str: ~str, mname: ~str,
                        ex: @ast::expr,
                        rhs_expr: @ast::expr, rhs_t: ty::t) -> ty::t {
         match lookup_op_method(fcx, ex, rhs_expr, rhs_t,
-                               fcx.tcx().sess.ident_of(mname), ~[]) {
+                               fcx.tcx().sess.ident_of(mname), true, ~[]) {
           Some((ret_ty, _)) => ret_ty,
           _ => {
             fcx.ccx.tcx.sess.span_err(
@@ -1308,87 +1368,84 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
     // Check field access expressions
     fn check_field(fcx: @fn_ctxt, expr: @ast::expr, is_callee: bool,
                    base: @ast::expr, field: ast::ident, tys: ~[@ast::ty])
-        -> bool {
+        -> bool
+    {
         let tcx = fcx.ccx.tcx;
         let bot = check_expr(fcx, base, None);
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
-        let base_t = do_autoderef(fcx, expr.span, expr_t);
-        let mut handled = false;
+        let (base_t, derefs) = do_autoderef(fcx, expr.span, expr_t);
         let n_tys = vec::len(tys);
         match structure_of(fcx, expr.span, base_t) {
-          ty::ty_rec(fields) => {
-            match ty::field_idx(field, fields) {
-              Some(ix) => {
-                if n_tys > 0u {
-                    tcx.sess.span_err(expr.span,
-                                      ~"can't provide type parameters \
-                                       to a field access");
-                }
-                fcx.write_ty(expr.id, fields[ix].mt.ty);
-                handled = true;
-              }
-              _ => ()
-            }
-          }
-          ty::ty_class(base_id, substs) => {
-              // This is just for fields -- the same code handles
-              // methods in both classes and traits
-
-              // (1) verify that the class id actually has a field called
-              // field
-              debug!("class named %s", ty_to_str(tcx, base_t));
-              let cls_items = ty::lookup_class_fields(tcx, base_id);
-              match lookup_field_ty(tcx, base_id, cls_items, field, &substs) {
-                 Some(field_ty) => {
-                    // (2) look up what field's type is, and return it
-                     fcx.write_ty(expr.id, field_ty);
-                     handled = true;
-                 }
-                 None => ()
-              }
-          }
-          _ => ()
-        }
-        if !handled {
-            let tps = vec::map(tys, |ty| fcx.to_ty(ty));
-            let is_self_ref = self_ref(fcx, base.id);
-
-            // this will be the call or block that immediately
-            // encloses the method call
-            let borrow_lb = fcx.tcx().region_map.get(expr.id);
-
-            let lkup = method::lookup(fcx, expr, base, borrow_lb,
-                                      expr.id, field, expr_t, tps,
-                                      is_self_ref);
-            match lkup.method() {
-                Some(entry) => {
-                    fcx.ccx.method_map.insert(expr.id, entry);
-
-                    // If we have resolved to a method but this is not in
-                    // a callee position, error
-                    if !is_callee {
-                        tcx.sess.span_err(
-                            expr.span,
-                            ~"attempted to take value of method \
-                              (try writing an anonymous function)");
+            ty::ty_rec(fields) => {
+                match ty::field_idx(field, fields) {
+                    Some(ix) => {
+                        if n_tys > 0u {
+                            tcx.sess.span_err(
+                                expr.span,
+                                ~"can't provide type parameters \
+                                  to a field access");
+                        }
+                        fcx.write_ty(expr.id, fields[ix].mt.ty);
+                        fcx.write_autoderef_adjustment(base.id, derefs);
+                        return bot;
                     }
-                }
-                None => {
-                    let t_err =
-                        fcx.infcx().resolve_type_vars_if_possible(expr_t);
-                    let msg =
-                        fmt!(
-                            "attempted access of field `%s` on type `%s`, \
-                             but no field or method with that name was found",
-                            tcx.sess.str_of(field),
-                            fcx.infcx().ty_to_str(t_err));
-                    tcx.sess.span_err(expr.span, msg);
-                    // NB: Add bogus type to allow typechecking to continue
-                    fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
+                    _ => ()
                 }
             }
+            ty::ty_class(base_id, substs) => {
+                // This is just for fields -- the same code handles
+                // methods in both classes and traits
+
+                // (1) verify that the class id actually has a field called
+                // field
+                debug!("class named %s", ty_to_str(tcx, base_t));
+                let cls_items = ty::lookup_class_fields(tcx, base_id);
+                match lookup_field_ty(tcx, base_id, cls_items,
+                                      field, &substs) {
+                    Some(field_ty) => {
+                        // (2) look up what field's type is, and return it
+                        fcx.write_ty(expr.id, field_ty);
+                        fcx.write_autoderef_adjustment(base.id, derefs);
+                        return bot;
+                    }
+                    None => ()
+                }
+            }
+            _ => ()
         }
+
+        let tps = vec::map(tys, |ty| fcx.to_ty(ty));
+
+        match method::lookup(fcx, expr, base, expr.id,
+                             field, expr_t, tps) {
+            Some(entry) => {
+                fcx.ccx.method_map.insert(expr.id, entry);
+
+                // If we have resolved to a method but this is not in
+                // a callee position, error
+                if !is_callee {
+                    tcx.sess.span_err(
+                        expr.span,
+                        ~"attempted to take value of method \
+                          (try writing an anonymous function)");
+                }
+            }
+            None => {
+                let t_err =
+                    fcx.infcx().resolve_type_vars_if_possible(expr_t);
+                let msg =
+                    fmt!(
+                        "attempted access of field `%s` on type `%s`, \
+                         but no field or method with that name was found",
+                        tcx.sess.str_of(field),
+                        fcx.infcx().ty_to_str(t_err));
+                tcx.sess.span_err(expr.span, msg);
+                // NB: Add bogus type to allow typechecking to continue
+                fcx.write_ty(expr.id, fcx.infcx().next_ty_var());
+            }
+        }
+
         return bot;
     }
 
@@ -2000,32 +2057,33 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         bot = check_field(fcx, expr, false, base, field, tys);
       }
       ast::expr_index(base, idx) => {
-        bot |= check_expr(fcx, base, None);
-        let raw_base_t = fcx.expr_ty(base);
-        let base_t = do_autoderef(fcx, expr.span, raw_base_t);
-        bot |= check_expr(fcx, idx, None);
-        let idx_t = fcx.expr_ty(idx);
-        let base_sty = structure_of(fcx, expr.span, base_t);
-        match ty::index_sty(tcx, &base_sty) {
-          Some(mt) => {
-            require_integral(fcx, idx.span, idx_t);
-            fcx.write_ty(id, mt.ty);
-          }
-          None => {
-            let resolved = structurally_resolved_type(fcx, expr.span,
-                                                      raw_base_t);
-            match lookup_op_method(fcx, expr, base, resolved,
-                                   tcx.sess.ident_of(~"index"),
-                                 ~[idx]) {
-              Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
-              _ => {
-                tcx.sess.span_fatal(
-                    expr.span, ~"cannot index a value of type `" +
-                    fcx.infcx().ty_to_str(base_t) + ~"`");
+          bot |= check_expr(fcx, base, None);
+          let raw_base_t = fcx.expr_ty(base);
+          let (base_t, derefs) = do_autoderef(fcx, expr.span, raw_base_t);
+          bot |= check_expr(fcx, idx, None);
+          let idx_t = fcx.expr_ty(idx);
+          let base_sty = structure_of(fcx, expr.span, base_t);
+          match ty::index_sty(tcx, &base_sty) {
+              Some(mt) => {
+                  require_integral(fcx, idx.span, idx_t);
+                  fcx.write_ty(id, mt.ty);
+                  fcx.write_autoderef_adjustment(base.id, derefs);
+              }
+              None => {
+                  let resolved = structurally_resolved_type(fcx, expr.span,
+                                                            raw_base_t);
+                  match lookup_op_method(fcx, expr, base, resolved,
+                                         tcx.sess.ident_of(~"index"), true,
+                                         ~[idx]) {
+                      Some((ret_ty, _)) => fcx.write_ty(id, ret_ty),
+                      _ => {
+                          tcx.sess.span_fatal(
+                              expr.span, ~"cannot index a value of type `" +
+                              fcx.infcx().ty_to_str(base_t) + ~"`");
+                      }
+                  }
               }
-            }
           }
-        }
       }
     }
     if bot { fcx.write_bot(expr.id); }
@@ -2304,16 +2362,6 @@ fn check_enum_variants(ccx: @crate_ctxt,
     check_instantiable(ccx.tcx, sp, id);
 }
 
-// Determines whether the given node ID is a use of the def of
-// the self ID for the current method, if there is one
-// self IDs in an outer scope count. so that means that you can
-// call your own private methods from nested functions inside
-// class methods
-fn self_ref(fcx: @fn_ctxt, id: ast::node_id) -> bool {
-    option::map_default(fcx.ccx.tcx.def_map.find(id), false,
-                        ast_util::is_self)
-}
-
 fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> TyVid {
     match fcx.inh.locals.find(id) {
         Some(x) => x,
@@ -2480,19 +2528,19 @@ fn type_is_c_like_enum(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
 }
 
 fn ast_expr_vstore_to_vstore(fcx: @fn_ctxt, e: @ast::expr, n: uint,
-                             v: ast::vstore) -> ty::vstore {
+                             v: ast::expr_vstore) -> ty::vstore {
     match v {
-        ast::vstore_fixed(None) => ty::vstore_fixed(n),
-        ast::vstore_fixed(Some(u)) => {
+        ast::expr_vstore_fixed(None) => ty::vstore_fixed(n),
+        ast::expr_vstore_fixed(Some(u)) => {
             if n != u {
                 let s = fmt!("fixed-size sequence mismatch: %u vs. %u",u, n);
                 fcx.ccx.tcx.sess.span_err(e.span,s);
             }
             ty::vstore_fixed(u)
         }
-        ast::vstore_uniq => ty::vstore_uniq,
-        ast::vstore_box => ty::vstore_box,
-        ast::vstore_slice(_) => {
+        ast::expr_vstore_uniq => ty::vstore_uniq,
+        ast::expr_vstore_box => ty::vstore_box,
+        ast::expr_vstore_slice => {
             let r = fcx.infcx().next_region_var(e.span, e.id);
             ty::vstore_slice(r)
         }
diff --git a/src/rustc/middle/typeck/check/demand.rs b/src/rustc/middle/typeck/check/demand.rs
index 8575bc6906a..3ae59910fe4 100644
--- a/src/rustc/middle/typeck/check/demand.rs
+++ b/src/rustc/middle/typeck/check/demand.rs
@@ -27,10 +27,9 @@ fn eqtype(fcx: @fn_ctxt, sp: span,
 }
 
 // Checks that the type `actual` can be assigned to `expected`.
-fn assign(fcx: @fn_ctxt, sp: span, borrow_lb: ast::node_id,
-          expected: ty::t, expr: @ast::expr) {
+fn assign(fcx: @fn_ctxt, sp: span, expected: ty::t, expr: @ast::expr) {
     let expr_ty = fcx.expr_ty(expr);
-    match fcx.mk_assignty(expr, borrow_lb, expr_ty, expected) {
+    match fcx.mk_assignty(expr, expr_ty, expected) {
       result::Ok(()) => { /* ok */ }
       result::Err(ref err) => {
         fcx.report_mismatched_types(sp, expected, expr_ty, err);
diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs
index 05c182a81bf..ff90f671d75 100644
--- a/src/rustc/middle/typeck/check/method.rs
+++ b/src/rustc/middle/typeck/check/method.rs
@@ -1,39 +1,813 @@
-/* Code to handle method lookups (which can be quite complex) */
+/*!
+
+# Method lookup
+
+Method lookup can be rather complex due to the interaction of a number
+of factors, such as self types, autoderef, trait lookup, etc.  The
+algorithm is divided into two parts: candidate collection and
+candidate selection.
+
+## Candidate collection
+
+A `Candidate` is a method item that might plausibly be the method
+being invoked.  Candidates are grouped into two kinds, inherent and
+extension.  Inherent candidates are those that are derived from the
+type of the receiver itself.  So, if you have a receiver of some
+nominal type `Foo` (e.g., a struct), any methods defined within an
+impl like `impl Foo` are inherent methods.  Nothing needs to be
+imported to use an inherent method, they are associated with the type
+itself (note that inherent impls can only be defined in the same
+module as the type itself).
+
+Inherent candidates are not always derived from impls.  If you have a
+trait instance, such as a value of type `ToStr`, then the trait
+methods (`to_str()`, in this case) are inherently associated with it.
+Another case is type parameters, in which case the methods of their
+bounds are inherent.
+
+Extension candidates are derived from imported traits.  If I have the
+trait `ToStr` imported, and I call `to_str()` on a value of type `T`,
+then we will go off to find out whether there is an impl of `ToStr`
+for `T`.  These kinds of method calls are called "extension methods".
+They can be defined in any module, not only the one that defined `T`.
+Furthermore, you must import the trait to call such a method.
+
+For better or worse, we currently give weight to inherent methods over
+extension methods during candidate selection (below).
+
+## Candidate selection
+
+Once we know the set of candidates, we can go off and try to select
+which one is actually being called.  We do this by taking the type of
+the receiver, let's call it R, and checking whether it matches against
+the expected receiver type for each of the collected candidates.  We
+first check for inherent candidates and see whether we get exactly one
+match (zero means keep searching, more than one is an error).  If so,
+we return that as the candidate.  Otherwise we search the extension
+candidates in the same way.
+
+If find no matching candidate at all, we proceed to auto-deref the
+receiver type and search again.  We keep doing that until we cannot
+auto-deref any longer.  At that point, we will attempt an auto-ref.
+If THAT fails, method lookup fails altogether.
+
+## Why two phases?
+
+You might wonder why we first collect the candidates and then select.
+Both the inherent candidate collection and the candidate selection
+proceed by progressively deref'ing the receiver type, after all.  The
+answer is that two phases are needed to elegantly deal with explicit
+self.  After all, if there is an impl for the type `Foo`, it can
+define a method with the type `@self`, which means that it expects a
+receiver of type `@Foo`.  If we have a receiver of type `@Foo`, but we
+waited to search for that impl until we have deref'd the `@` away and
+obtained the type `Foo`, we would never match this method.
+
+*/
 
 use coherence::get_base_type_def_id;
 use middle::resolve::{Impl, MethodInfo};
-use middle::ty::{mk_box, mk_rptr, mk_uniq, FnTyBase, FnMeta, FnSig};
-use syntax::ast::{def_id,
-                     sty_static, sty_box, sty_by_ref, sty_region, sty_uniq};
-use syntax::ast::{sty_value, by_ref, by_copy};
+use middle::ty::*;
+use syntax::ast::{def_id, sty_by_ref, sty_value, sty_region, sty_box,
+                  sty_uniq, sty_static, node_id, by_copy, by_ref,
+                  m_const, m_mutbl, m_imm};
 use syntax::ast_map;
 use syntax::ast_map::node_id_to_str;
 use syntax::ast_util::{dummy_sp, new_def_hash};
 use dvec::DVec;
 
-enum method_lookup_mode {
-    subtyping_mode,
-    assignability_mode,
-    immutable_reference_mode,
-    mutable_reference_mode
+fn lookup(
+    fcx: @fn_ctxt,
+
+    // In a call `a.b::<X, Y, ...>(...)`:
+    expr: @ast::expr,        // The expression `a.b`.
+    self_expr: @ast::expr,   // The expression `a`.
+    callee_id: node_id, // Where to store the type of `a.b`
+    m_name: ast::ident,      // The ident `b`.
+    self_ty: ty::t,          // The type of `a`.
+    supplied_tps: &[ty::t])  // The list of types X, Y, ... .
+    -> Option<method_map_entry>
+{
+    let lcx = LookupContext {
+        fcx: fcx,
+        expr: expr,
+        self_expr: self_expr,
+        callee_id: callee_id,
+        m_name: m_name,
+        supplied_tps: supplied_tps,
+        impl_dups: new_def_hash(),
+        inherent_candidates: DVec(),
+        extension_candidates: DVec()
+    };
+    return lcx.do_lookup(self_ty);
 }
 
-type candidate = {
-    self_ty: ty::t,             // type of a in a.b()
-    self_substs: ty::substs,    // values for any tvars def'd on the class
-    rcvr_ty: ty::t,             // type of receiver in the method def
-    n_tps_m: uint,              // number of tvars defined on the method
-    fty: ty::t,                 // type of the method
-    entry: method_map_entry,
-    mode: method_lookup_mode    // the mode we used
-};
+struct LookupContext {
+    fcx: @fn_ctxt,
+    expr: @ast::expr,
+    self_expr: @ast::expr,
+    callee_id: node_id,
+    m_name: ast::ident,
+    supplied_tps: &[ty::t],
+    impl_dups: HashMap<def_id, ()>,
+    inherent_candidates: DVec<Candidate>,
+    extension_candidates: DVec<Candidate>
+}
 
-fn transform_self_type_for_method
-    (tcx: ty::ctxt,
-     self_region: Option<ty::region>,
-     impl_ty: ty::t,
-     self_type: ast::self_ty_)
-                               -> ty::t {
+/**
+ * A potential method that might be called, assuming the receiver
+ * is of a suitable type. */
+struct Candidate {
+    rcvr_ty: ty::t,
+    rcvr_substs: ty::substs,
+
+    // FIXME #3446---these two fields should be easily derived from
+    // origin, yet are not
+    num_method_tps: uint,
+    self_mode: ast::rmode,
+
+    origin: method_origin,
+}
+
+impl LookupContext {
+    fn do_lookup(&self, self_ty: ty::t) -> Option<method_map_entry> {
+        debug!("do_lookup(self_ty=%s, expr=%s, self_expr=%s)",
+               self.ty_to_str(self_ty),
+               expr_repr(self.tcx(), self.expr),
+               expr_repr(self.tcx(), self.self_expr));
+        let _indenter = indenter();
+
+        // Prepare the list of candidates
+        self.push_inherent_candidates(self_ty);
+        self.push_extension_candidates();
+
+        let enum_dids = DVec();
+        let mut self_ty = self_ty;
+        let mut autoderefs = 0;
+        loop {
+            debug!("loop: self_ty=%s autoderefs=%u",
+                   self.ty_to_str(self_ty), autoderefs);
+
+            match self.search_for_autoderefd_method(self_ty, autoderefs) {
+                Some(move mme) => { return Some(mme); }
+                None => {}
+            }
+
+            // some special logic around newtypes:
+            match ty::get(self_ty).sty {
+                ty_enum(*) => {
+                    // Note: in general, we prefer not to auto-ref a
+                    // partially autoderef'd type, because it
+                    // seems... crazy.  But we have to be careful
+                    // around newtype enums.  They can be further
+                    // deref'd, but they may also have intrinsic
+                    // methods hanging off of them with interior type.
+                    match self.search_for_appr_autorefd_method(self_ty,
+                                                               autoderefs) {
+                        Some(move mme) => { return Some(mme); }
+                        None => {}
+                    }
+                }
+                _ => {}
+            }
+
+            match self.deref(self_ty, &enum_dids) {
+                None => { break; }
+                Some(ty) => {
+                    self_ty = ty;
+                    autoderefs += 1;
+                }
+            }
+        }
+
+        self.search_for_appr_autorefd_method(self_ty, autoderefs)
+    }
+
+    fn deref(ty: ty::t, enum_dids: &DVec<ast::def_id>) -> Option<ty::t> {
+        match ty::get(ty).sty {
+            ty_enum(did, _) => {
+                // Watch out for newtype'd enums like "enum t = @T".
+                // See discussion in typeck::check::do_autoderef().
+                if enum_dids.contains(did) {
+                    return None;
+                }
+                enum_dids.push(did);
+            }
+            _ => {}
+        }
+
+        match ty::deref(self.tcx(), ty, false) {
+            None => None,
+            Some(t) => {
+                //FIXME(#3211) -- probably want to force ivars
+                Some(structurally_resolved_type(self.fcx,
+                                                self.self_expr.span,
+                                                t.ty))
+            }
+        }
+    }
+
+    // ______________________________________________________________________
+    // Candidate collection (see comment at start of file)
+
+    fn push_inherent_candidates(&self, self_ty: ty::t) {
+        /*!
+         *
+         * Collect all inherent candidates into
+         * `self.inherent_candidates`.  See comment at the start of
+         * the file.  To find the inherent candidates, we repeatedly
+         * deref the self-ty to find the "base-type".  So, for
+         * example, if the receiver is @@C where `C` is a struct type,
+         * we'll want to find the inherent impls for `C`. */
+
+        let enum_dids = DVec();
+        let mut self_ty = self_ty;
+        loop {
+            match get(self_ty).sty {
+                ty_param(p) => {
+                    self.push_inherent_candidates_from_param(p);
+                }
+                ty_trait(did, ref substs, _) => {
+                    self.push_inherent_candidates_from_trait(
+                        self_ty, did, substs);
+                    self.push_inherent_impl_candidates_for_type(did);
+                }
+                ty_self => {
+                    // Call is of the form "self.foo()" and appears in one
+                    // of a trait's default method implementations.
+                    let self_did = self.fcx.self_impl_def_id.expect(
+                        ~"unexpected `none` for self_impl_def_id");
+                    let substs = {self_r: None, self_ty: None, tps: ~[]};
+                    self.push_inherent_candidates_from_trait(
+                        self_ty, self_did, &substs);
+                }
+                ty_enum(did, _) | ty_class(did, _) => {
+                    self.push_inherent_impl_candidates_for_type(did);
+                }
+                _ => { /* No inherent methods in these types */ }
+            }
+
+            // n.b.: Generally speaking, we only loop if we hit the
+            // fallthrough case in the match above.  The exception
+            // would be newtype enums.
+            self_ty = match self.deref(self_ty, &enum_dids) {
+                None => { return; }
+                Some(ty) => { ty }
+            }
+        }
+    }
+
+    fn push_extension_candidates(&self) {
+        // If the method being called is associated with a trait, then
+        // find all the impls of that trait.  Each of those are
+        // candidates.
+        let opt_applicable_traits = self.fcx.ccx.trait_map.find(self.expr.id);
+        for opt_applicable_traits.each |applicable_traits| {
+            for applicable_traits.each |trait_did| {
+                let coherence_info = self.fcx.ccx.coherence_info;
+                let opt_impl_infos =
+                    coherence_info.extension_methods.find(trait_did);
+                for opt_impl_infos.each |impl_infos| {
+                    for impl_infos.each |impl_info| {
+                        self.push_candidates_from_impl(
+                            &self.extension_candidates, impl_info);
+                    }
+                }
+            }
+        }
+    }
+
+    fn push_inherent_candidates_from_param(&self, param_ty: param_ty)
+    {
+        debug!("push_inherent_candidates_from_param(param_ty=%?)",
+               param_ty);
+        let _indenter = indenter();
+
+        let tcx = self.tcx();
+        let mut next_bound_idx = 0; // count only trait bounds
+        let bounds = tcx.ty_param_bounds.get(param_ty.def_id.node);
+        for vec::each(*bounds) |bound| {
+            let bound_t = match bound {
+                ty::bound_trait(bound_t) => bound_t,
+
+                ty::bound_copy | ty::bound_send |
+                ty::bound_const | ty::bound_owned => {
+                    loop; // skip non-trait bounds
+                }
+            };
+
+            let this_bound_idx = next_bound_idx;
+            next_bound_idx += 1;
+
+            let (trait_id, bound_substs) = match ty::get(bound_t).sty {
+                ty::ty_trait(i, substs, _) => (i, substs),
+                _ => {
+                    self.bug(fmt!("add_candidates_from_param: \
+                                   non-trait bound %s",
+                                  self.ty_to_str(bound_t)));
+                }
+            };
+
+            let trait_methods = ty::trait_methods(tcx, trait_id);
+            let pos = {
+                // FIXME #3453 can't use trait_methods.position
+                match vec::position(*trait_methods,
+                                    |m| (m.self_ty != ast::sty_static &&
+                                         m.ident == self.m_name))
+                {
+                    Some(pos) => pos,
+                    None => {
+                        loop; // check next bound
+                    }
+                }
+            };
+            let method = &trait_methods[pos];
+
+            // Replace any appearance of `self` with the type of the
+            // generic parameter itself.  Note that this is the only
+            // case where this replacement is necessary: in all other
+            // cases, we are either invoking a method directly from an
+            // impl or class (where the self type is not permitted),
+            // or from a trait type (in which case methods that refer
+            // to self are not permitted).
+            let rcvr_ty = ty::mk_param(tcx, param_ty.idx, param_ty.def_id);
+            let rcvr_substs = {self_ty: Some(rcvr_ty), ..bound_substs};
+
+            self.inherent_candidates.push(Candidate {
+                rcvr_ty: rcvr_ty,
+                rcvr_substs: rcvr_substs,
+                num_method_tps: method.tps.len(),
+                self_mode: get_mode_from_self_type(method.self_ty),
+                origin: method_param({trait_id:trait_id,
+                                      method_num:pos,
+                                      param_num:param_ty.idx,
+                                      bound_num:this_bound_idx})
+            });
+        }
+    }
+
+    fn push_inherent_candidates_from_trait(&self,
+                                           self_ty: ty::t,
+                                           did: def_id,
+                                           substs: &ty::substs)
+    {
+        debug!("push_inherent_candidates_from_trait(did=%s, substs=%s)",
+               self.did_to_str(did),
+               substs_to_str(self.tcx(), substs));
+        let _indenter = indenter();
+
+        let tcx = self.tcx();
+        let ms = ty::trait_methods(tcx, did);
+        let index = match vec::position(*ms, |m| m.ident == self.m_name) {
+            Some(i) => i,
+            None => { return; } // no method with the right name
+        };
+        let method = &ms[index];
+
+        /* FIXME(#3468) we should transform the vstore in accordance
+           with the self type
+
+        match method.self_type {
+            ast::sty_region(_) => {
+                return; // inapplicable
+            }
+            ast::sty_by_ref | ast::sty_region(_) => vstore_slice(r)
+            ast::sty_box(_) => vstore_box, // XXX NDM mutability
+            ast::sty_uniq(_) => vstore_uniq
+        }
+        */
+
+        // It is illegal to invoke a method on a trait instance that
+        // refers to the `self` type.  Nonetheless, we substitute
+        // `trait_ty` for `self` here, because it allows the compiler
+        // to soldier on.  An error will be reported should this
+        // candidate be selected if the method refers to `self`.
+        let rcvr_substs = {self_ty: Some(self_ty), ..*substs};
+
+        self.inherent_candidates.push(Candidate {
+            rcvr_ty: self_ty,
+            rcvr_substs: move rcvr_substs,
+            num_method_tps: method.tps.len(),
+            self_mode: get_mode_from_self_type(method.self_ty),
+            origin: method_trait(did, index)
+        });
+    }
+
+    fn push_inherent_impl_candidates_for_type(did: def_id)
+    {
+        let opt_impl_infos =
+            self.fcx.ccx.coherence_info.inherent_methods.find(did);
+        for opt_impl_infos.each |impl_infos| {
+            for impl_infos.each |impl_info| {
+                self.push_candidates_from_impl(
+                    &self.inherent_candidates, impl_info);
+            }
+        }
+    }
+
+    fn push_candidates_from_impl(&self, candidates: &DVec<Candidate>,
+                                 impl_info: &resolve::Impl)
+    {
+        if !self.impl_dups.insert(impl_info.did, ()) {
+            return; // already visited
+        }
+
+        let idx = {
+            // FIXME #3453 can't use impl_info.methods.position
+            match vec::position(impl_info.methods,
+                                |m| m.ident == self.m_name) {
+                Some(idx) => idx,
+                None => { return; } // No method with the right name.
+            }
+        };
+
+        let tcx = self.tcx();
+        let method = &impl_info.methods[idx];
+
+        let need_rp = match method.self_type {
+            ast::sty_region(_) => true,
+            _ => false
+        };
+
+        // determine the `self` of the impl with fresh
+        // variables for each parameter:
+        let {substs: impl_substs, ty: impl_ty} =
+            impl_self_ty(self.fcx, self.self_expr, impl_info.did, need_rp);
+
+        let impl_ty = transform_self_type_for_method(
+            tcx, impl_substs.self_r, impl_ty, method.self_type);
+
+        candidates.push(Candidate {
+            rcvr_ty: impl_ty,
+            rcvr_substs: move impl_substs,
+            num_method_tps: method.n_tps,
+            self_mode: get_mode_from_self_type(method.self_type),
+            origin: method_static(method.did)
+        });
+    }
+
+    // ______________________________________________________________________
+    // Candidate selection (see comment at start of file)
+
+    fn search_for_autoderefd_method(
+        &self,
+        self_ty: ty::t,
+        autoderefs: uint)
+        -> Option<method_map_entry>
+    {
+        match self.search_for_method(self_ty) {
+            None => None,
+            Some(move mme) => {
+                self.fcx.write_autoderef_adjustment(
+                    self.self_expr.id, autoderefs);
+                Some(mme)
+            }
+        }
+    }
+
+    fn search_for_appr_autorefd_method(
+        &self,
+        self_ty: ty::t,
+        autoderefs: uint)
+        -> Option<method_map_entry>
+    {
+        let tcx = self.tcx();
+
+        // Next, try auto-ref. The precise kind of auto-ref depends on
+        // the fully deref'd receiver type.  In particular, we must
+        // treat dynamically sized types like `str`, `[]` or `fn`
+        // differently than other types because they cannot be fully
+        // deref'd, unlike say @T.
+        match ty::get(self_ty).sty {
+            ty_box(*) | ty_uniq(*) | ty_rptr(*) => {
+                // we should be fully autoderef'd
+                self.bug(fmt!("Receiver type %s should be fully \
+                               autoderef'd by this point",
+                              self.ty_to_str(self_ty)));
+            }
+
+            ty_infer(IntVar(_)) | // FIXME(#3211)---should be resolved
+            ty_self | ty_param(*) | ty_nil | ty_bot | ty_bool |
+            ty_int(*) | ty_uint(*) |
+            ty_float(*) | ty_enum(*) | ty_ptr(*) | ty_rec(*) |
+            ty_class(*) | ty_tup(*) => {
+                return self.search_for_autorefd_method(
+                    AutoPtr, autoderefs, [m_const, m_imm, m_mutbl],
+                    |m,r| ty::mk_rptr(tcx, r, {ty:self_ty, mutbl:m}));
+            }
+
+            ty_trait(*) | ty_fn(*) => {
+                // NDM---eventually these should be some variant of autoref
+                return None;
+            }
+
+            ty_estr(vstore_slice(_)) |
+            ty_evec(_, vstore_slice(_)) => {
+                return None;
+            }
+
+            ty_evec(mt, vstore_box) |
+            ty_evec(mt, vstore_uniq) |
+            ty_evec(mt, vstore_fixed(_)) => {
+                return self.search_for_autorefd_method(
+                    AutoSlice, autoderefs, [m_const, m_imm, m_mutbl],
+                    |m,r| ty::mk_evec(tcx,
+                                      {ty:mt.ty, mutbl:m},
+                                      vstore_slice(r)));
+            }
+
+            ty_estr(vstore_box) |
+            ty_estr(vstore_uniq) |
+            ty_estr(vstore_fixed(_)) => {
+                return self.search_for_autorefd_method(
+                    AutoSlice, autoderefs, [m_imm],
+                    |_m,r| ty::mk_estr(tcx, vstore_slice(r)));
+            }
+
+            ty_opaque_closure_ptr(_) | ty_unboxed_vec(_) |
+            ty_opaque_box | ty_type | ty_infer(TyVar(_)) => {
+                self.bug(fmt!("Unexpected type: %s",
+                              self.ty_to_str(self_ty)));
+            }
+        }
+    }
+
+    fn search_for_autorefd_method(
+        &self,
+        kind: AutoRefKind,
+        autoderefs: uint,
+        mutbls: &[ast::mutability],
+        mk_autoref_ty: &fn(ast::mutability, ty::region) -> ty::t)
+        -> Option<method_map_entry>
+    {
+        // This is hokey. We should have mutability inference as a
+        // variable.  But for now, try &const, then &, then &mut:
+        let region = self.infcx().next_region_var(self.expr.span,
+                                                  self.expr.id);
+        for mutbls.each |mutbl| {
+            let autoref_ty = mk_autoref_ty(mutbl, region);
+            match self.search_for_method(autoref_ty) {
+                None => {}
+                Some(move mme) => {
+                    self.fcx.write_adjustment(
+                        self.self_expr.id,
+                        @{autoderefs: autoderefs,
+                          autoref: Some({kind: kind,
+                                         region: region,
+                                         mutbl: mutbl})});
+                    return Some(mme);
+                }
+            }
+        }
+        return None;
+    }
+
+    fn search_for_method(&self,
+                         self_ty: ty::t)
+        -> Option<method_map_entry>
+    {
+        debug!("search_for_method(self_ty=%s)", self.ty_to_str(self_ty));
+        let _indenter = indenter();
+
+        // I am not sure that inherent methods should have higher
+        // priority, but it is necessary ATM to handle some of the
+        // existing code.
+
+        debug!("searching inherent candidates");
+        match self.consider_candidates(self_ty, &self.inherent_candidates) {
+            None => {}
+            Some(move mme) => {
+                return Some(move mme);
+            }
+        }
+
+        debug!("searching extension candidates");
+        match self.consider_candidates(self_ty, &self.extension_candidates) {
+            None => {
+                return None;
+            }
+            Some(move mme) => {
+                return Some(move mme);
+            }
+        }
+    }
+
+    fn consider_candidates(&self,
+                           self_ty: ty::t,
+                           candidates: &DVec<Candidate>)
+        -> Option<method_map_entry>
+    {
+        let relevant_candidates =
+            candidates.filter_to_vec(|c| self.is_relevant(self_ty, &c));
+
+        if relevant_candidates.len() == 0 {
+            return None;
+        }
+
+        if relevant_candidates.len() > 1 {
+            self.tcx().sess.span_err(
+                self.expr.span,
+                ~"multiple applicable methods in scope");
+            for uint::range(0, relevant_candidates.len()) |idx| {
+                self.report_candidate(idx, &relevant_candidates[idx].origin);
+            }
+        }
+
+        Some(self.confirm_candidate(self_ty, &relevant_candidates[0]))
+    }
+
+    fn confirm_candidate(&self,
+                         self_ty: ty::t,
+                         candidate: &Candidate)
+        -> method_map_entry
+    {
+        let tcx = self.tcx();
+        let fty = self.fn_ty_from_origin(&candidate.origin);
+
+        self.enforce_trait_instance_limitations(fty, candidate);
+
+        // before we only checked whether self_ty could be a subtype
+        // of rcvr_ty; now we actually make it so (this may cause
+        // variables to unify etc).  Since we checked beforehand, and
+        // nothing has changed in the meantime, this unification
+        // should never fail.
+        match self.fcx.mk_subty(false, self.self_expr.span,
+                                self_ty, candidate.rcvr_ty) {
+            result::Ok(_) => (),
+            result::Err(_) => {
+                self.bug(fmt!("%s was assignable to %s but now is not?",
+                              self.ty_to_str(self_ty),
+                              self.ty_to_str(candidate.rcvr_ty)));
+            }
+        }
+
+        // Determine the values for the type parameters of the method.
+        // If they were not explicitly supplied, just construct fresh
+        // type variables.
+        let num_supplied_tps = self.supplied_tps.len();
+        let m_substs = {
+            if num_supplied_tps == 0u {
+                self.fcx.infcx().next_ty_vars(candidate.num_method_tps)
+            } else if candidate.num_method_tps == 0u {
+                tcx.sess.span_err(
+                    self.expr.span,
+                    ~"this method does not take type parameters");
+                self.fcx.infcx().next_ty_vars(candidate.num_method_tps)
+            } else if num_supplied_tps != candidate.num_method_tps {
+                tcx.sess.span_err(
+                    self.expr.span,
+                    ~"incorrect number of type \
+                     parameters given for this method");
+                self.fcx.infcx().next_ty_vars(candidate.num_method_tps)
+            } else {
+                self.supplied_tps.to_vec()
+            }
+        };
+
+        // Construct the full set of type parameters for the method,
+        // which is equal to the class tps + the method tps.
+        let all_substs = {tps: vec::append(candidate.rcvr_substs.tps,
+                                           m_substs),
+                          ..candidate.rcvr_substs};
+
+        self.fcx.write_ty_substs(self.callee_id, fty, all_substs);
+        return {self_arg: {mode: ast::expl(candidate.self_mode),
+                           ty: candidate.rcvr_ty},
+                origin: candidate.origin};
+    }
+
+    fn enforce_trait_instance_limitations(&self,
+                                          method_fty: ty::t,
+                                          candidate: &Candidate)
+    {
+        /*!
+         *
+         * There are some limitations to calling functions through a
+         * traint instance, because (a) the self type is not known
+         * (that's the whole point of a trait instance, after all, to
+         * obscure the self type) and (b) the call must go through a
+         * vtable and hence cannot be monomorphized. */
+
+        match candidate.origin {
+            method_static(*) | method_param(*) => {
+                return; // not a call to a trait instance
+            }
+            method_trait(*) => {}
+        }
+
+        if ty::type_has_self(method_fty) {
+            self.tcx().sess.span_err(
+                self.expr.span,
+                ~"cannot call a method whose type contains a \
+                  self-type through a boxed trait");
+        }
+
+        if candidate.num_method_tps > 0 {
+            self.tcx().sess.span_err(
+                self.expr.span,
+                ~"cannot call a generic method through a boxed trait");
+        }
+    }
+
+    fn is_relevant(&self, self_ty: ty::t, candidate: &Candidate) -> bool {
+        self.fcx.can_mk_subty(self_ty, candidate.rcvr_ty).is_ok()
+    }
+
+    fn fn_ty_from_origin(&self, origin: &method_origin) -> ty::t {
+        return match *origin {
+            method_static(did) => {
+                ty::lookup_item_type(self.tcx(), did).ty
+            }
+            method_param(ref mp) => {
+                type_of_trait_method(self.tcx(), mp.trait_id, mp.method_num)
+            }
+            method_trait(did, idx) => {
+                type_of_trait_method(self.tcx(), did, idx)
+            }
+        };
+
+        fn type_of_trait_method(tcx: ty::ctxt,
+                                trait_did: def_id,
+                                method_num: uint) -> ty::t {
+            let trait_methods = ty::trait_methods(tcx, trait_did);
+            ty::mk_fn(tcx, trait_methods[method_num].fty)
+        }
+    }
+
+    fn report_candidate(idx: uint, origin: &method_origin) {
+        match *origin {
+            method_static(impl_did) => {
+                self.report_static_candidate(idx, impl_did)
+            }
+            method_param(mp) => {
+                self.report_param_candidate(idx, mp.trait_id)
+            }
+            method_trait(trait_did, _) => {
+                self.report_param_candidate(idx, trait_did)
+            }
+        }
+    }
+
+    fn report_static_candidate(idx: uint, did: def_id) {
+        let span = if did.crate == ast::local_crate {
+            match self.tcx().items.get(did.node) {
+              ast_map::node_method(m, _, _) => m.span,
+              _ => fail ~"report_static_candidate: bad item"
+            }
+        } else {
+            self.expr.span
+        };
+        self.tcx().sess.span_note(
+            span,
+            fmt!("candidate #%u is `%s`",
+                 (idx+1u),
+                 ty::item_path_str(self.tcx(), did)));
+    }
+
+    fn report_param_candidate(idx: uint, did: def_id) {
+        self.tcx().sess.span_note(
+            self.expr.span,
+            fmt!("candidate #%u derives from the bound `%s`",
+                 (idx+1u),
+                 ty::item_path_str(self.tcx(), did)));
+    }
+
+    fn report_trait_candidate(idx: uint, did: def_id) {
+        self.tcx().sess.span_note(
+            self.expr.span,
+            fmt!("candidate #%u derives from the type of the receiver, \
+                  which is the trait `%s`",
+                 (idx+1u),
+                 ty::item_path_str(self.tcx(), did)));
+    }
+
+    fn infcx() -> infer::infer_ctxt {
+        self.fcx.inh.infcx
+    }
+
+    fn tcx() -> ty::ctxt {
+        self.fcx.tcx()
+    }
+
+    fn ty_to_str(t: ty::t) -> ~str {
+        self.fcx.infcx().ty_to_str(t)
+    }
+
+    fn did_to_str(did: def_id) -> ~str {
+        ty::item_path_str(self.tcx(), did)
+    }
+
+    fn bug(s: ~str) -> ! {
+        self.tcx().sess.bug(s)
+    }
+}
+
+fn transform_self_type_for_method(tcx: ty::ctxt,
+                                  self_region: Option<ty::region>,
+                                  impl_ty: ty::t,
+                                  self_type: ast::self_ty_)
+    -> ty::t
+{
     match self_type {
       sty_static => {
         tcx.sess.bug(~"calling transform_self_type_for_method on \
@@ -57,663 +831,5 @@ fn transform_self_type_for_method
 }
 
 fn get_mode_from_self_type(self_type: ast::self_ty_) -> ast::rmode {
-    match self_type {
-      sty_value => by_copy,
-      _ => by_ref
-    }
+    match self_type { sty_value => by_copy, _ => by_ref }
 }
-
-fn lookup(fcx: @fn_ctxt,
-
-        // In a call `a.b::<X, Y, ...>(...)`:
-        expr: @ast::expr,        // The expression `a.b`.
-        self_expr: @ast::expr,   // The expression `a`.
-        borrow_lb: ast::node_id, // Scope to borrow the expression `a` for.
-        node_id: ast::node_id,   // The node_id in which to store the type of
-                                 // `a.b`.
-        m_name: ast::ident,      // The ident `b`.
-        self_ty: ty::t,          // The type of `a`.
-        supplied_tps: ~[ty::t],  // The list of types X, Y, ... .
-        include_private: bool) -> lookup {
-
-    lookup {
-        fcx: fcx,
-        expr: expr,
-        self_expr: self_expr,
-        borrow_lb: borrow_lb,
-        node_id: node_id,
-        m_name: m_name,
-        self_ty: self_ty,
-        derefs: 0u,
-        candidates: DVec(),
-        candidate_impls: new_def_hash(),
-        supplied_tps: supplied_tps,
-        include_private: include_private
-    }
-}
-
-struct lookup {
-    fcx: @fn_ctxt,
-    expr: @ast::expr,
-    self_expr: @ast::expr,
-    borrow_lb: ast::node_id,
-    node_id: ast::node_id,
-    m_name: ast::ident,
-    mut self_ty: ty::t,
-    mut derefs: uint,
-    candidates: DVec<candidate>,
-    candidate_impls: HashMap<def_id, ()>,
-    supplied_tps: ~[ty::t],
-    include_private: bool,
-}
-
-impl lookup {
-
-    // Entrypoint:
-    fn method() -> Option<method_map_entry> {
-        debug!("method lookup(m_name=%s, self_ty=%s, %?)",
-               self.fcx.tcx().sess.str_of(self.m_name),
-               self.fcx.infcx().ty_to_str(self.self_ty),
-               ty::get(self.self_ty).sty);
-
-        // Determine if there are any inherent methods we can call.
-        // (An inherent method is one that belongs to no trait, but is
-        // inherent to a class or impl.)
-        let optional_inherent_methods;
-        match get_base_type_def_id(self.fcx.infcx(),
-                                 self.self_expr.span,
-                                 self.self_ty) {
-          None => {
-            optional_inherent_methods = None;
-          }
-          Some(base_type_def_id) => {
-            debug!("(checking method) found base type");
-            optional_inherent_methods =
-                self.fcx.ccx.coherence_info.inherent_methods.find
-                (base_type_def_id);
-
-            if optional_inherent_methods.is_none() {
-                debug!("(checking method) ... no inherent methods found");
-            } else {
-                debug!("(checking method) ... inherent methods found");
-            }
-          }
-        }
-
-        let matching_modes =
-            [subtyping_mode, assignability_mode,
-             immutable_reference_mode, mutable_reference_mode];
-
-        loop {
-            // Try to find a method that is keyed directly off of the
-            // type. This only happens for boxed traits, type params,
-            // classes, and self. If we see some sort of pointer, then
-            // we look at candidates for the pointed to type to match
-            // them against methods that take explicit self parameters.
-            // N.B.: this looking through boxes to match against
-            // explicit self parameters is *not* the same as
-            // autoderef.
-            // Try each of the possible matching semantics in turn.
-            for matching_modes.each |mode| {
-                match ty::get(self.self_ty).sty {
-                  ty::ty_box(mt) | ty::ty_uniq(mt) | ty::ty_rptr(_, mt) => {
-                    self.add_candidates_from_type(mt.ty, mode);
-                  }
-                  _ => { self.add_candidates_from_type(self.self_ty, mode); }
-                }
-                if self.candidates.len() > 0u { break; }
-            }
-
-            // if we found anything, stop now.  otherwise continue to
-            // loop for impls in scope.  Note: I don't love these
-            // semantics, but that's what we had so I am preserving
-            // it.
-            if self.candidates.len() > 0u { break; }
-
-            // Try each of the possible matching semantics in turn.
-            for matching_modes.each |mode| {
-                self.add_inherent_and_extension_candidates(
-                    optional_inherent_methods, mode);
-                // If we find anything, stop.
-                if self.candidates.len() > 0u { break; }
-            }
-            // if we found anything, stop before attempting auto-deref.
-            if self.candidates.len() > 0u {
-                debug!("(checking method) found at least one inherent \
-                        method; giving up looking now");
-                break;
-            }
-
-            // check whether we can autoderef and if so loop around again.
-            match ty::deref(self.tcx(), self.self_ty, false) {
-              None => break,
-              Some(mt) => {
-                debug!("(checking method) ... autodereffing");
-                self.self_ty = mt.ty;
-                self.derefs += 1u;
-              }
-            }
-        }
-
-        if self.candidates.len() == 0u {
-            debug!("(checking method) couldn't find any candidate methods; \
-                    returning none");
-            return None;
-        }
-
-        if self.candidates.len() > 1u {
-            self.tcx().sess.span_err(
-                self.expr.span,
-                ~"multiple applicable methods in scope");
-
-            for self.candidates.eachi |i, candidate| {
-                match candidate.entry.origin {
-                  method_static(did) => {
-                    self.report_static_candidate(i, did);
-                  }
-                  method_param(p) => {
-                    self.report_param_candidate(i, p.trait_id);
-                  }
-                  method_trait(did, _) => {
-                    self.report_trait_candidate(i, did);
-                  }
-                }
-            }
-        }
-
-        Some(self.write_mty_from_candidate(self.candidates[0u]))
-    }
-
-    fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
-
-    fn report_static_candidate(idx: uint, did: ast::def_id) {
-        let span = if did.crate == ast::local_crate {
-            match self.tcx().items.get(did.node) {
-              ast_map::node_method(m, _, _) => m.span,
-              _ => fail ~"report_static_candidate: bad item"
-            }
-        } else {
-            self.expr.span
-        };
-        self.tcx().sess.span_note(
-            span,
-            fmt!("candidate #%u is `%s`",
-                 (idx+1u),
-                 ty::item_path_str(self.tcx(), did)));
-    }
-
-    fn report_param_candidate(idx: uint, did: ast::def_id) {
-        self.tcx().sess.span_note(
-            self.expr.span,
-            fmt!("candidate #%u derives from the bound `%s`",
-                 (idx+1u),
-                 ty::item_path_str(self.tcx(), did)));
-    }
-
-    fn report_trait_candidate(idx: uint, did: ast::def_id) {
-        self.tcx().sess.span_note(
-            self.expr.span,
-            fmt!("candidate #%u derives from the type of the receiver, \
-                  which is the trait `%s`",
-                 (idx+1u),
-                 ty::item_path_str(self.tcx(), did)));
-    }
-
-    fn add_candidates_from_type(inner_ty: ty::t, mode: method_lookup_mode) {
-        match ty::get(inner_ty).sty {
-          // First, see whether this is a bounded parameter.
-          ty::ty_param(p) => {
-            self.add_candidates_from_param(inner_ty, mode, p.idx, p.def_id);
-          }
-          ty::ty_trait(did, substs, _) => {
-            self.add_candidates_from_trait(inner_ty, mode, did, substs);
-          }
-          ty::ty_class(did, substs) => {
-            self.add_candidates_from_class(inner_ty, mode, did, substs);
-          }
-          ty::ty_self => {
-            // Call is of the form "self.foo()" and appears in one
-            // of a trait's provided methods.
-            let self_def_id = self.fcx.self_impl_def_id.expect(
-                ~"unexpected `none` for self_impl_def_id");
-
-            let substs = {
-                self_r: None,
-                self_ty: None,
-                tps: ~[],
-            };
-
-            self.add_candidates_from_trait(inner_ty, mode,
-                                           self_def_id, substs);
-          }
-          _ => ()
-        }
-    }
-
-    fn add_candidates_from_param(inner_ty: ty::t, mode: method_lookup_mode,
-                                 n: uint, did: ast::def_id) {
-        debug!("add_candidates_from_param");
-
-        let tcx = self.tcx();
-        let mut trait_bnd_idx = 0u; // count only trait bounds
-        let bounds = tcx.ty_param_bounds.get(did.node);
-        for vec::each(*bounds) |bound| {
-            let (trait_id, bound_substs) = match bound {
-              ty::bound_copy | ty::bound_send | ty::bound_const |
-              ty::bound_owned => {
-                loop; /* ok */
-              }
-              ty::bound_trait(bound_t) => {
-                match ty::get(bound_t).sty {
-                  ty::ty_trait(i, substs, _) => (i, substs),
-                  _ => fail ~"add_candidates_from_param: non-trait bound"
-                }
-              }
-            };
-
-            let trt_methods = ty::trait_methods(tcx, trait_id);
-            let match_fn: &fn(m: ty::method) -> bool = |m| {
-                m.self_ty != ast::sty_static && m.ident == self.m_name
-            };
-            match vec::position(*trt_methods, match_fn) {
-              None => {
-                /* check next bound */
-                trait_bnd_idx += 1u;
-              }
-
-              Some(pos) => {
-                // Replace any appearance of `self` with the type of the
-                // generic parameter itself.  Note that this is the only case
-                // where this replacement is necessary: in all other cases, we
-                // are either invoking a method directly from an impl or class
-                // (where the self type is not permitted), or from a trait
-                // type (in which case methods that refer to self are not
-                // permitted).
-                let substs = {self_ty: Some(self.self_ty),
-                              .. bound_substs};
-
-                self.add_candidates_from_m(
-                    inner_ty,
-                    mode,
-                    substs, trt_methods[pos],
-                    method_param({trait_id:trait_id,
-                                  method_num:pos,
-                                  param_num:n,
-                                  bound_num:trait_bnd_idx}));
-              }
-            }
-        }
-
-    }
-
-    fn add_candidates_from_trait(inner_ty: ty::t,
-                                 mode: method_lookup_mode,
-                                 did: ast::def_id,
-                                 trait_substs: ty::substs) {
-
-        debug!("add_candidates_from_trait");
-
-        let ms = *ty::trait_methods(self.tcx(), did);
-        for ms.eachi |i, m| {
-            if m.ident != self.m_name { loop; }
-
-            let m_fty = ty::mk_fn(self.tcx(), m.fty);
-
-            if ty::type_has_self(m_fty) {
-                self.tcx().sess.span_err(
-                    self.expr.span,
-                    ~"cannot call a method whose type contains a \
-                     self-type through a boxed trait");
-            }
-
-            if (*m.tps).len() > 0u {
-                self.tcx().sess.span_err(
-                    self.expr.span,
-                    ~"cannot call a generic method through a \
-                     boxed trait");
-            }
-
-            // Note: although it is illegal to invoke a method that uses self
-            // through a trait instance, we use a dummy subst here so that we
-            // can soldier on with the compilation.
-            let substs = {self_ty: Some(self.self_ty),
-                          .. trait_substs};
-
-            self.add_candidates_from_m(
-                inner_ty, mode, substs, m, method_trait(did, i));
-        }
-    }
-
-    fn add_candidates_from_class(inner_ty: ty::t,
-                                 mode: method_lookup_mode,
-                                 did: ast::def_id,
-                                 class_substs: ty::substs) {
-
-        debug!("add_candidates_from_class");
-
-        let ms = *ty::trait_methods(self.tcx(), did);
-
-        for ms.each |m| {
-            if m.ident != self.m_name { loop; }
-
-            if m.vis == ast::private && !self.include_private {
-                self.tcx().sess.span_fatal(
-                    self.expr.span,
-                    ~"call to private method not allowed outside \
-                     its defining class");
-            }
-
-            // look up method named <name>.
-            let m_declared = ty::lookup_class_method_by_name(
-                self.tcx(), did, self.m_name, self.expr.span);
-
-            self.add_candidates_from_m(
-                inner_ty, mode, class_substs, m, method_static(m_declared));
-        }
-    }
-
-    fn ty_from_did(did: ast::def_id) -> ty::t {
-        match ty::get(ty::lookup_item_type(self.tcx(), did).ty).sty {
-            ty::ty_fn(ref fty) => {
-                ty::mk_fn(self.tcx(), FnTyBase {
-                    meta: FnMeta {proto: ty::proto_vstore(ty::vstore_box),
-                                  ..fty.meta},
-                    sig: fty.sig
-                })
-          }
-          _ => fail ~"ty_from_did: not function ty"
-        }
-        /*
-        if did.crate == ast::local_crate {
-            match check self.tcx().items.get(did.node) {
-              ast_map::node_method(m, _, _) {
-                // NDM trait/impl regions
-                let mt = ty_of_method(self.fcx.ccx, m, ast::rp_none);
-                ty::mk_fn(self.tcx(), {proto: ast::proto_box with mt.fty})
-              }
-            }
-        } else {
-            match check ty::get(csearch::get_type(self.tcx(), did).ty)
-              .sty {
-
-              ty::ty_fn(fty) {
-                ty::mk_fn(self.tcx(), {proto: ast::proto_box with fty})
-              }
-            }
-        }
-        */
-    }
-
-    fn check_type_match(impl_ty: ty::t,
-                        mode: method_lookup_mode)
-        -> Result<(), ty::type_err> {
-        // Depending on our argument, we find potential matches by
-        // checking subtypability, type assignability, or reference
-        // subtypability. Collect the matches.
-        match mode {
-            subtyping_mode => self.fcx.can_mk_subty(self.self_ty, impl_ty),
-            assignability_mode => self.fcx.can_mk_assignty(self.self_ty,
-                                                           impl_ty),
-            immutable_reference_mode => {
-                let region = self.fcx.infcx().next_region_var(
-                    self.self_expr.span,
-                    self.self_expr.id);
-                let tm = { ty: self.self_ty, mutbl: ast::m_imm };
-                let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
-                self.fcx.can_mk_subty(ref_ty, impl_ty)
-            }
-            mutable_reference_mode => {
-                let region = self.fcx.infcx().next_region_var(
-                    self.self_expr.span,
-                    self.self_expr.id);
-                let tm = { ty: self.self_ty, mutbl: ast::m_mutbl };
-                let ref_ty = ty::mk_rptr(self.tcx(), region, tm);
-                self.fcx.can_mk_subty(ref_ty, impl_ty)
-            }
-        }
-    }
-
-    // Returns true if any were added and false otherwise.
-    fn add_candidates_from_impl(im: @resolve::Impl, mode: method_lookup_mode)
-                             -> bool {
-        let mut added_any = false;
-
-        // Check whether this impl has a method with the right name.
-        for im.methods.find(|m| m.ident == self.m_name).each |m| {
-
-            let need_rp = match m.self_type { ast::sty_region(_) => true,
-                                              _ => false };
-
-            // determine the `self` of the impl with fresh
-            // variables for each parameter:
-            let {substs: impl_substs, ty: impl_ty} =
-                impl_self_ty(self.fcx, self.self_expr, im.did, need_rp);
-
-            let impl_ty = transform_self_type_for_method(
-                self.tcx(), impl_substs.self_r,
-                impl_ty, m.self_type);
-
-            let matches = self.check_type_match(impl_ty, mode);
-            debug!("matches = %?", matches);
-            match matches {
-              result::Err(_) => { /* keep looking */ }
-              result::Ok(_) => {
-                if !self.candidate_impls.contains_key(im.did) {
-                    let fty = self.ty_from_did(m.did);
-                    self.candidates.push(
-                        {self_ty: self.self_ty,
-                         self_substs: impl_substs,
-                         rcvr_ty: impl_ty,
-                         n_tps_m: m.n_tps,
-                         fty: fty,
-                         entry: {derefs: self.derefs,
-                                 self_mode: get_mode_from_self_type(
-                                     m.self_type),
-                                 origin: method_static(m.did)},
-                         mode: mode});
-                    self.candidate_impls.insert(im.did, ());
-                    added_any = true;
-                }
-              }
-            }
-        }
-
-        return added_any;
-    }
-
-    fn add_candidates_from_m(inner_ty: ty::t,
-                             mode: method_lookup_mode,
-                             self_substs: ty::substs,
-                             m: ty::method,
-                             origin: method_origin) {
-        let tcx = self.fcx.ccx.tcx;
-
-        // If we don't have a self region but have an region pointer
-        // explicit self, we need to make up a new region.
-        let self_r = match self_substs.self_r {
-          None => {
-            match m.self_ty {
-              ast::sty_region(_) =>
-                  Some(self.fcx.infcx().next_region_var(
-                      self.self_expr.span,
-                      self.self_expr.id)),
-              _ => None
-            }
-          }
-          Some(_) => self_substs.self_r
-        };
-        let self_substs = {self_r: self_r,.. self_substs};
-
-        // Before we can be sure we succeeded we need to match the
-        // self type against the impl type that we get when we apply
-        // the explicit self parameter to whatever inner type we are
-        // looking at (which may be something that the self_type
-        // points to).
-        let impl_ty = transform_self_type_for_method(
-            self.tcx(), self_substs.self_r,
-            inner_ty, m.self_ty);
-
-        let matches = self.check_type_match(impl_ty, mode);
-        debug!("matches = %?", matches);
-        if matches.is_err() { return; }
-
-        // a bit hokey, but the method unbound has a bare protocol, whereas
-        // a.b has a protocol like fn@() (perhaps eventually fn&()):
-        let fty = ty::mk_fn(tcx, FnTyBase {
-            meta: FnMeta {proto: ty::proto_vstore(ty::vstore_box),
-                          ..m.fty.meta},
-            sig: m.fty.sig
-        });
-
-        self.candidates.push(
-            {self_ty: self.self_ty,
-             self_substs: self_substs,
-             rcvr_ty: self.self_ty,
-             n_tps_m: (*m.tps).len(),
-             fty: fty,
-             entry: {derefs: self.derefs,
-                     self_mode: get_mode_from_self_type(m.self_ty),
-                     origin: origin},
-             mode: mode});
-    }
-
-    fn add_inherent_and_extension_candidates(optional_inherent_methods:
-                                                Option<@DVec<@Impl>>,
-                                             mode: method_lookup_mode) {
-
-        // Add inherent methods.
-        match optional_inherent_methods {
-          None => {
-            // Continue.
-          }
-          Some(inherent_methods) => {
-            debug!("(adding inherent and extension candidates) adding \
-                    inherent candidates");
-            for inherent_methods.each |implementation| {
-                debug!("(adding inherent and extension candidates) \
-                        adding candidates from impl: %s",
-                        node_id_to_str(self.tcx().items,
-                                       implementation.did.node,
-                                       self.fcx.tcx().sess.parse_sess
-                                           .interner));
-                self.add_candidates_from_impl(implementation, mode);
-            }
-          }
-        }
-
-        // Add trait methods.
-        match self.fcx.ccx.trait_map.find(self.expr.id) {
-          None => {
-            // Should only happen for placement new right now.
-          }
-          Some(trait_ids) => {
-            for (*trait_ids).each |trait_id| {
-                debug!("(adding inherent and extension candidates) \
-                        trying trait: %s",
-                        self.def_id_to_str(trait_id));
-
-                let coherence_info = self.fcx.ccx.coherence_info;
-                match coherence_info.extension_methods.find(trait_id) {
-                  None => {
-                    // Do nothing.
-                  }
-                  Some(extension_methods) => {
-                    for extension_methods.each |implementation| {
-                        debug!("(adding inherent and extension \
-                                candidates) adding impl %s",
-                                self.def_id_to_str
-                                (implementation.did));
-                        self.add_candidates_from_impl(implementation, mode);
-                    }
-                  }
-                }
-            }
-          }
-        }
-    }
-
-    fn def_id_to_str(def_id: ast::def_id) -> ~str {
-        if def_id.crate == ast::local_crate {
-            node_id_to_str(self.tcx().items, def_id.node,
-                           self.fcx.tcx().sess.parse_sess.interner)
-        } else {
-            ast_map::path_to_str(csearch::get_item_path(self.tcx(), def_id),
-                                 self.fcx.tcx().sess.parse_sess.interner)
-        }
-    }
-
-    fn write_mty_from_candidate(cand: candidate) -> method_map_entry {
-        let tcx = self.fcx.ccx.tcx;
-
-        debug!("write_mty_from_candidate(n_tps_m=%u, fty=%s, entry=%?)",
-               cand.n_tps_m,
-               self.fcx.infcx().ty_to_str(cand.fty),
-               cand.entry);
-
-        match cand.mode {
-            subtyping_mode | assignability_mode => {
-                // Make the actual receiver type (cand.self_ty) assignable to
-                // the required receiver type (cand.rcvr_ty).  If this method
-                // is not from an impl, this'll basically be a no-nop.
-                match self.fcx.mk_assignty(self.self_expr, self.borrow_lb,
-                                           cand.self_ty, cand.rcvr_ty) {
-                  result::Ok(_) => (),
-                  result::Err(_) => {
-                    self.tcx().sess.span_bug(
-                        self.expr.span,
-                        fmt!("%s was assignable to %s but now is not?",
-                             self.fcx.infcx().ty_to_str(cand.self_ty),
-                             self.fcx.infcx().ty_to_str(cand.rcvr_ty)));
-                  }
-                }
-            }
-            immutable_reference_mode => {
-                // Borrow as an immutable reference.
-                self.add_borrow(ast::m_imm);
-            }
-            mutable_reference_mode => {
-                // Borrow as a mutable reference.
-                self.add_borrow(ast::m_mutbl);
-            }
-        }
-
-        // Construct the full set of type parameters for the method,
-        // which is equal to the class tps + the method tps.
-        let n_tps_supplied = self.supplied_tps.len();
-        let n_tps_m = cand.n_tps_m;
-        let m_substs = {
-            if n_tps_supplied == 0u {
-                self.fcx.infcx().next_ty_vars(n_tps_m)
-            } else if n_tps_m == 0u {
-                tcx.sess.span_err(
-                    self.expr.span,
-                    ~"this method does not take type parameters");
-                self.fcx.infcx().next_ty_vars(n_tps_m)
-            } else if n_tps_supplied != n_tps_m {
-                tcx.sess.span_err(
-                    self.expr.span,
-                    ~"incorrect number of type \
-                     parameters given for this method");
-                self.fcx.infcx().next_ty_vars(n_tps_m)
-            } else {
-                self.supplied_tps
-            }
-        };
-
-        let all_substs = {tps: vec::append(cand.self_substs.tps, m_substs),
-                          .. cand.self_substs};
-
-        self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs);
-
-        return cand.entry;
-    }
-
-    fn add_borrow(mutbl: ast::mutability) {
-        let region_var = self.fcx.infcx().next_region_var(
-            self.self_expr.span,
-            self.self_expr.id);
-        self.fcx.inh.borrowings.insert(self.self_expr.id, {region: region_var,
-                                                           mutbl: mutbl});
-    }
-}
-
diff --git a/src/rustc/middle/typeck/check/regionck.rs b/src/rustc/middle/typeck/check/regionck.rs
index 17c29b704f5..d5c6be4116a 100644
--- a/src/rustc/middle/typeck/check/regionck.rs
+++ b/src/rustc/middle/typeck/check/regionck.rs
@@ -155,116 +155,157 @@ fn visit_block(b: ast::blk, &&rcx: @rcx, v: rvt) {
     visit::visit_block(b, rcx, v);
 }
 
-fn visit_expr(e: @ast::expr, &&rcx: @rcx, v: rvt) {
+fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
     debug!("visit_expr(e=%s)",
-           pprust::expr_to_str(e, rcx.fcx.tcx().sess.intr()));
+           pprust::expr_to_str(expr, rcx.fcx.tcx().sess.intr()));
 
-    match e.node {
-      ast::expr_path(*) => {
-        // Avoid checking the use of local variables, as we already
-        // check their definitions.  The def'n always encloses the
-        // use.  So if the def'n is enclosed by the region, then the
-        // uses will also be enclosed (and otherwise, an error will
-        // have been reported at the def'n site).
-        match lookup_def(rcx.fcx, e.span, e.id) {
-          ast::def_local(*) | ast::def_arg(*) | ast::def_upvar(*) => return,
-          _ => ()
-        }
-      }
+    // constrain_auto_ref(rcx, expr);
 
-      ast::expr_cast(source, _) => {
-        // Determine if we are casting `source` to an trait instance.
-        // If so, we have to be sure that the type of the source obeys
-        // the trait's region bound.
-        //
-        // Note: there is a subtle point here concerning type
-        // parameters.  It is possible that the type of `source`
-        // contains type parameters, which in turn may contain regions
-        // that are not visible to us (only the caller knows about
-        // them).  The kind checker is ultimately responsible for
-        // guaranteeing region safety in that particular case.  There
-        // is an extensive comment on the function
-        // check_cast_for_escaping_regions() in kind.rs explaining how
-        // it goes about doing that.
-        match rcx.resolve_node_type(e.id) {
-          result::Err(_) => { return; /* typeck will fail anyhow */ }
-          result::Ok(target_ty) => {
-            match ty::get(target_ty).sty {
-              ty::ty_trait(_, substs, _) => {
-                let trait_region = match substs.self_r {
-                  Some(r) => {r}
-                  None => {ty::re_static}
-                };
-                let source_ty = rcx.fcx.expr_ty(source);
-                constrain_regions_in_type(rcx, trait_region,
-                                          e.span, source_ty);
-              }
-              _ => ()
+    match expr.node {
+        ast::expr_path(*) => {
+            // Avoid checking the use of local variables, as we
+            // already check their definitions.  The def'n always
+            // encloses the use.  So if the def'n is enclosed by the
+            // region, then the uses will also be enclosed (and
+            // otherwise, an error will have been reported at the
+            // def'n site).
+            match lookup_def(rcx.fcx, expr.span, expr.id) {
+                ast::def_local(*) | ast::def_arg(*) |
+                ast::def_upvar(*) => return,
+                _ => ()
             }
-          }
-        };
-      }
-
-      ast::expr_addr_of(*) => {
-        // FIXME(#3148) -- in some cases, we need to capture a dependency
-        // between the regions found in operand the resulting region type.
-        // See #3148 for more details.
-      }
-
-      ast::expr_fn(*) | ast::expr_fn_block(*) => {
-        match rcx.resolve_node_type(e.id) {
-          result::Err(_) => return,   // Typechecking will fail anyhow.
-          result::Ok(function_type) => {
-            match ty::get(function_type).sty {
-              ty::ty_fn(ref fn_ty) => {
-                  match fn_ty.meta.proto {
-                      proto_vstore(vstore_slice(region)) => {
-                          constrain_free_variables(rcx, region, e);
-                      }
-                      _ => {}
-                  }
-              }
-              _ => ()
-            }
-          }
         }
-      }
 
-      _ => ()
+        ast::expr_cast(source, _) => {
+            // Determine if we are casting `source` to an trait
+            // instance.  If so, we have to be sure that the type of
+            // the source obeys the trait's region bound.
+            //
+            // Note: there is a subtle point here concerning type
+            // parameters.  It is possible that the type of `source`
+            // contains type parameters, which in turn may contain
+            // regions that are not visible to us (only the caller
+            // knows about them).  The kind checker is ultimately
+            // responsible for guaranteeing region safety in that
+            // particular case.  There is an extensive comment on the
+            // function check_cast_for_escaping_regions() in kind.rs
+            // explaining how it goes about doing that.
+            match rcx.resolve_node_type(expr.id) {
+                result::Err(_) => { return; /*typeck will fail anyhow*/ }
+                result::Ok(target_ty) => {
+                    match ty::get(target_ty).sty {
+                        ty::ty_trait(_, substs, _) => {
+                            let trait_region = match substs.self_r {
+                                Some(r) => {r}
+                                None => {ty::re_static}
+                            };
+                            let source_ty = rcx.fcx.expr_ty(source);
+                            constrain_regions_in_type(rcx, trait_region,
+                                                      expr.span, source_ty);
+                        }
+                        _ => ()
+                    }
+                }
+            };
+        }
+
+        ast::expr_addr_of(*) => {
+            // FIXME(#3148) -- in some cases, we need to capture a
+            // dependency between the regions found in operand the
+            // resulting region type.  See #3148 for more details.
+        }
+
+        ast::expr_fn(*) | ast::expr_fn_block(*) => {
+            match rcx.resolve_node_type(expr.id) {
+                result::Err(_) => return, // Typechecking will fail anyhow.
+                result::Ok(function_type) => {
+                    match ty::get(function_type).sty {
+                        ty::ty_fn(ref fn_ty) => {
+                            match fn_ty.meta.proto {
+                                proto_vstore(vstore_slice(region)) => {
+                                    constrain_free_variables(rcx, region,
+                                                             expr);
+                                }
+                                _ => {}
+                            }
+                        }
+                        _ => ()
+                    }
+                }
+            }
+        }
+
+        _ => ()
     }
 
-    if !visit_node(e.id, e.span, rcx) { return; }
-    visit::visit_expr(e, rcx, v);
+    if !visit_node(expr.id, expr.span, rcx) { return; }
+    visit::visit_expr(expr, rcx, v);
 }
 
 fn visit_stmt(s: @ast::stmt, &&rcx: @rcx, v: rvt) {
     visit::visit_stmt(s, rcx, v);
 }
 
-// checks the type of the node `id` and reports an error if it
-// references a region that is not in scope for that node.  Returns
-// false if an error is reported; this is used to cause us to cut off
-// region checking for that subtree to avoid reporting tons of errors.
 fn visit_node(id: ast::node_id, span: span, rcx: @rcx) -> bool {
-    let fcx = rcx.fcx;
+    /*!
+     *
+     * checks the type of the node `id` and reports an error if it
+     * references a region that is not in scope for that node.
+     * Returns false if an error is reported; this is used to cause us
+     * to cut off region checking for that subtree to avoid reporting
+     * tons of errors. */
 
-    // Try to resolve the type.  If we encounter an error, then typeck
-    // is going to fail anyway, so just stop here and let typeck
-    // report errors later on in the writeback phase.
-    let ty = match rcx.resolve_node_type(id) {
-      result::Err(_) => return true,
-      result::Ok(ty) => ty
-    };
+    let fcx = rcx.fcx;
 
     // find the region where this expr evaluation is taking place
     let tcx = fcx.ccx.tcx;
     let encl_region = ty::encl_region(tcx, id);
 
-    debug!("visit_node(ty=%s, id=%d, encl_region=%?)",
-           ty_to_str(tcx, ty), id, encl_region);
-
     // Otherwise, look at the type and see if it is a region pointer.
-    return constrain_regions_in_type(rcx, encl_region, span, ty);
+    constrain_regions_in_type_of_node(rcx, id, encl_region, span)
+}
+
+fn constrain_auto_ref(
+    rcx: @rcx,
+    expr: @ast::expr)
+{
+    /*!
+     *
+     * If `expr` is auto-ref'd (e.g., as part of a borrow), then this
+     * function ensures that the lifetime of the resulting borrowed
+     * ptr includes at least the expression `expr`. */
+
+    let adjustment = rcx.fcx.inh.adjustments.find(expr.id);
+    let region = match adjustment {
+        Some(@{autoref: Some(ref auto_ref), _}) => auto_ref.region,
+        _ => { return; }
+    };
+
+    let tcx = rcx.fcx.tcx();
+    let expr_region = ty::re_scope(expr.id);
+    match rcx.fcx.mk_subr(true, expr.span, expr_region, region) {
+        result::Ok(()) => {}
+        result::Err(_) => {
+            // In practice, this cannot happen: `region` is always a
+            // region variable, and constraints on region variables
+            // are collected and then resolved later.  However, I
+            // included the span_err() here (rather than, say,
+            // span_bug()) because it seemed more future-proof: if,
+            // for some reason, the code were to change so that in
+            // some cases `region` is not a region variable, then
+            // reporting an error would be the correct path.
+            tcx.sess.span_err(
+                expr.span,
+                ~"lifetime of borrowed pointer does not include \
+                  the expression being borrowed");
+            note_and_explain_region(
+                tcx,
+                ~"lifetime of the borrowed pointer is",
+                region,
+                ~"");
+            rcx.errors_reported += 1;
+        }
+    }
 }
 
 fn constrain_free_variables(
@@ -272,9 +313,11 @@ fn constrain_free_variables(
     region: ty::region,
     expr: @ast::expr)
 {
-    // Make sure that all regions referenced by the free
-    // variables inside the closure outlive the closure
-    // itself.
+    /*!
+     *
+     * Make sure that all free variables referenced inside the closure
+     * outlive the closure itself. */
+
     let tcx = rcx.fcx.ccx.tcx;
     for get_freevars(tcx, expr.id).each |freevar| {
         debug!("freevar def is %?", freevar.def);
@@ -302,11 +345,44 @@ fn constrain_free_variables(
     }
 }
 
+fn constrain_regions_in_type_of_node(
+    rcx: @rcx,
+    id: ast::node_id,
+    encl_region: ty::region,
+    span: span) -> bool
+{
+    let tcx = rcx.fcx.tcx();
+
+    // Try to resolve the type.  If we encounter an error, then typeck
+    // is going to fail anyway, so just stop here and let typeck
+    // report errors later on in the writeback phase.
+    let ty = match rcx.resolve_node_type(id) {
+      result::Err(_) => return true,
+      result::Ok(ty) => ty
+    };
+
+    debug!("constrain_regions_in_type_of_node(\
+            ty=%s, id=%d, encl_region=%?)",
+           ty_to_str(tcx, ty), id, encl_region);
+
+    constrain_regions_in_type(rcx, encl_region, span, ty)
+}
+
 fn constrain_regions_in_type(
     rcx: @rcx,
     encl_region: ty::region,
     span: span,
-    ty: ty::t) -> bool {
+    ty: ty::t) -> bool
+{
+    /*!
+     *
+     * Requires that any regions which appear in `ty` must be
+     * superregions of `encl_region`.  This prevents regions from
+     * being used outside of the block in which they are valid.
+     * Recall that regions represent blocks of code or expressions:
+     * this requirement basically says "any place that uses or may use
+     * a region R must be within the block of code that R corresponds
+     * to." */
 
     let e = rcx.errors_reported;
     ty::walk_regions_and_ty(
diff --git a/src/rustc/middle/typeck/check/vtable.rs b/src/rustc/middle/typeck/check/vtable.rs
index 276b7eef578..ccc5e4c1a82 100644
--- a/src/rustc/middle/typeck/check/vtable.rs
+++ b/src/rustc/middle/typeck/check/vtable.rs
@@ -4,6 +4,7 @@ use infer::{resolve_type, resolve_and_force_all_but_regions,
 use ast_util::new_def_hash;
 use syntax::print::pprust;
 use result::{Result, Ok, Err};
+use util::common::indenter;
 
 // vtable resolution looks for places where trait bounds are
 // subsituted in and figures out which vtable is used. There is some
@@ -410,6 +411,8 @@ fn insert_vtables(ccx: @crate_ctxt, callee_id: ast::node_id,
 fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
     debug!("vtable: early_resolve_expr() ex with id %? (early: %b): %s",
            ex.id, is_early, expr_to_str(ex, fcx.tcx().sess.intr()));
+    let _indent = indenter();
+
     let cx = fcx.ccx;
     match ex.node {
       ast::expr_path(*) => {
diff --git a/src/rustc/middle/typeck/check/writeback.rs b/src/rustc/middle/typeck/check/writeback.rs
index f132337b686..d57b012503b 100644
--- a/src/rustc/middle/typeck/check/writeback.rs
+++ b/src/rustc/middle/typeck/check/writeback.rs
@@ -27,30 +27,53 @@ fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t)
     }
 }
 
+fn resolve_method_map_entry(fcx: @fn_ctxt, sp: span, id: ast::node_id)
+{
+    // Resolve any method map entry
+    match fcx.ccx.method_map.find(id) {
+        None => {}
+        Some(ref mme) => {
+            for resolve_type_vars_in_type(fcx, sp, mme.self_arg.ty).each |t| {
+                fcx.ccx.method_map.insert(
+                    id,
+                    {self_arg: {mode: mme.self_arg.mode, ty: t},
+                     ..*mme});
+            }
+        }
+    }
+}
+
 fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id)
     -> Option<ty::t>
 {
     let fcx = wbcx.fcx, tcx = fcx.ccx.tcx;
 
     // Resolve any borrowings for the node with id `id`
-    match fcx.inh.borrowings.find(id) {
+    match fcx.inh.adjustments.find(id) {
         None => (),
-        Some(borrow) => {
-            match resolve_region(fcx.infcx(), borrow.region,
-                                 resolve_all | force_all) {
-                Err(e) => {
-                    // This should not, I think, happen.
-                    fcx.ccx.tcx.sess.span_err(
-                        sp, fmt!("cannot resolve scope of borrow: %s",
-                                 infer::fixup_err_to_str(e)));
+        Some(adj) => {
+            let resolved_autoref = match adj.autoref {
+                Some(ref autoref) => {
+                    match resolve_region(fcx.infcx(), autoref.region,
+                                         resolve_all | force_all) {
+                        Err(e) => {
+                            // This should not, I think, happen.
+                            fcx.ccx.tcx.sess.span_err(
+                                sp, fmt!("cannot resolve scope of borrow: %s",
+                                         infer::fixup_err_to_str(e)));
+                            Some(*autoref)
+                        }
+                        Ok(r) => {
+                            Some({region: r, ..*autoref})
+                        }
+                    }
                 }
-                Ok(r) => {
-                    debug!("Borrowing node %d -> region %?, mutbl %?",
-                           id, r, borrow.mutbl);
-                    fcx.tcx().borrowings.insert(id, {region: r,
-                                                     mutbl: borrow.mutbl});
-                }
-            }
+                None => None
+            };
+
+            let resolved_adj = @{autoref: resolved_autoref, ..*adj};
+            debug!("Adjustments for node %d: %?", id, resolved_adj);
+            fcx.tcx().adjustments.insert(id, resolved_adj);
         }
     }
 
@@ -109,6 +132,8 @@ fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) {
 fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
     if !wbcx.success { return; }
     resolve_type_vars_for_node(wbcx, e.span, e.id);
+    resolve_method_map_entry(wbcx.fcx, e.span, e.id);
+    resolve_method_map_entry(wbcx.fcx, e.span, e.callee_id);
     match e.node {
       ast::expr_fn(_, decl, _, _) |
       ast::expr_fn_block(decl, _, _) => {
diff --git a/src/rustc/middle/typeck/infer.rs b/src/rustc/middle/typeck/infer.rs
index ed77217c625..a0bb828e412 100644
--- a/src/rustc/middle/typeck/infer.rs
+++ b/src/rustc/middle/typeck/infer.rs
@@ -275,6 +275,7 @@ use unify::{vals_and_bindings, root};
 use integral::{int_ty_set, int_ty_set_all};
 use combine::{combine_fields, eq_tys};
 use assignment::Assign;
+use to_str::to_str;
 
 use sub::Sub;
 use lub::Lub;
@@ -304,7 +305,7 @@ type bounds<T:Copy> = {lb: bound<T>, ub: bound<T>};
 type cres<T> = Result<T,ty::type_err>; // "combine result"
 type ures = cres<()>; // "unify result"
 type fres<T> = Result<T, fixup_err>; // "fixup result"
-type ares = cres<Option<ty::borrow>>; // "assignment result"
+type ares = cres<Option<@ty::AutoAdjustment>>; // "assignment result"
 
 enum infer_ctxt = @{
     tcx: ty::ctxt,
@@ -469,19 +470,24 @@ impl ures: then {
     }
 }
 
-trait cres_helpers<T> {
+trait ToUres {
     fn to_ures() -> ures;
-    fn compare(t: T, f: fn() -> ty::type_err) -> cres<T>;
 }
 
-impl<T:Copy Eq> cres<T>: cres_helpers<T> {
+impl<T> cres<T>: ToUres {
     fn to_ures() -> ures {
         match self {
           Ok(_v) => Ok(()),
           Err(e) => Err(e)
         }
     }
+}
 
+trait CresCompare<T> {
+    fn compare(t: T, f: fn() -> ty::type_err) -> cres<T>;
+}
+
+impl<T:Copy Eq> cres<T>: CresCompare<T> {
     fn compare(t: T, f: fn() -> ty::type_err) -> cres<T> {
         do self.chain |s| {
             if s == t {
diff --git a/src/rustc/middle/typeck/infer/assignment.rs b/src/rustc/middle/typeck/infer/assignment.rs
index fdb23c90b10..53731551df5 100644
--- a/src/rustc/middle/typeck/infer/assignment.rs
+++ b/src/rustc/middle/typeck/infer/assignment.rs
@@ -134,18 +134,24 @@ priv impl Assign {
                     (ty::ty_box(_), ty::ty_rptr(r_b, mt_b)) => {
                         let nr_b = ty::mk_box(self.infcx.tcx,
                                               {ty: mt_b.ty, mutbl: m_const});
-                        self.try_assign(a, nr_b, mt_b.mutbl, r_b)
+                        self.try_assign(1, ty::AutoPtr,
+                                        a, nr_b,
+                                        mt_b.mutbl, r_b)
                     }
                     (ty::ty_uniq(_), ty::ty_rptr(r_b, mt_b)) => {
                         let nr_b = ty::mk_uniq(self.infcx.tcx,
                                                {ty: mt_b.ty, mutbl: m_const});
-                        self.try_assign(a, nr_b, mt_b.mutbl, r_b)
+                        self.try_assign(1, ty::AutoPtr,
+                                        a, nr_b,
+                                        mt_b.mutbl, r_b)
                     }
                     (ty::ty_estr(vs_a),
                      ty::ty_estr(ty::vstore_slice(r_b)))
                     if is_borrowable(vs_a) => {
                         let nr_b = ty::mk_estr(self.infcx.tcx, vs_a);
-                        self.try_assign(a, nr_b, m_imm, r_b)
+                        self.try_assign(0, ty::AutoSlice,
+                                        a, nr_b,
+                                        m_imm, r_b)
                     }
 
                     (ty::ty_evec(_, vs_a),
@@ -154,7 +160,9 @@ priv impl Assign {
                         let nr_b = ty::mk_evec(self.infcx.tcx,
                                                {ty: mt_b.ty, mutbl: m_const},
                                                vs_a);
-                        self.try_assign(a, nr_b, mt_b.mutbl, r_b)
+                        self.try_assign(0, ty::AutoSlice,
+                                        a, nr_b,
+                                        mt_b.mutbl, r_b)
                     }
 
                     _ => {
@@ -177,7 +185,9 @@ priv impl Assign {
     /// variable `r_a >= r_b` and returns a corresponding assignment
     /// record.  See the discussion at the top of this file for more
     /// details.
-    fn try_assign(a: ty::t,
+    fn try_assign(autoderefs: uint,
+                  kind: ty::AutoRefKind,
+                  a: ty::t,
                   nr_b: ty::t,
                   m: ast::mutability,
                   r_b: ty::region) -> ares {
@@ -193,7 +203,14 @@ priv impl Assign {
             do sub.tys(a, nr_b).chain |_t| {
                 let r_a = self.infcx.next_region_var_nb(self.span);
                 do sub.contraregions(r_a, r_b).chain |_r| {
-                    Ok(Some({region: r_a, mutbl: m}))
+                    Ok(Some(@{
+                        autoderefs: autoderefs,
+                        autoref: Some({
+                            kind: kind,
+                            region: r_a,
+                            mutbl: m
+                        })
+                    }))
                 }
             }
         }
diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs
index a9363f0f1da..fce115c1c2a 100644
--- a/src/rustc/util/ppaux.rs
+++ b/src/rustc/util/ppaux.rs
@@ -18,7 +18,7 @@ use syntax::codemap;
 use syntax::codemap::span;
 use syntax::print::pprust;
 use syntax::print::pprust::{path_to_str, proto_to_str,
-                               mode_to_str, purity_to_str};
+                            mode_to_str, purity_to_str};
 use syntax::{ast, ast_util};
 use syntax::ast_map;
 use driver::session::session;
@@ -229,10 +229,15 @@ fn proto_ty_to_str(cx: ctxt, proto: ty::fn_proto) -> ~str {
     }
 }
 
+fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str {
+    fmt!("expr(%d: %s)",
+         expr.id,
+         pprust::expr_to_str(expr, cx.sess.intr()))
+}
+
 fn tys_to_str(cx: ctxt, ts: ~[t]) -> ~str {
-    let mut rs = ~"";
-    for ts.each |t| { rs += ty_to_str(cx, t); }
-    rs
+    let tstrs = ts.map(|t| ty_to_str(cx, t));
+    fmt!("[%s]", str::connect(tstrs, ", "))
 }
 
 fn bound_to_str(cx: ctxt, b: param_bound) -> ~str {
diff --git a/src/test/compile-fail/assign-to-method.rs b/src/test/compile-fail/assign-to-method.rs
index fd578ff5c4f..24878e36cf3 100644
--- a/src/test/compile-fail/assign-to-method.rs
+++ b/src/test/compile-fail/assign-to-method.rs
@@ -19,4 +19,5 @@ fn cat(in_x : uint, in_y : int) -> cat {
 fn main() {
   let nyan : cat = cat(52u, 99);
   nyan.speak = fn@() { debug!("meow"); }; //~ ERROR attempted to take value of method
+  //~^ ERROR mismatched types
 }
diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs
new file mode 100644
index 00000000000..c38c5cbbf93
--- /dev/null
+++ b/src/test/compile-fail/borrowck-autoref-3261.rs
@@ -0,0 +1,19 @@
+use either::*;
+enum X = Either<(uint,uint),fn()>;
+impl &X {
+    fn with(blk: fn(x: &Either<(uint,uint),fn()>)) {
+        blk(&**self)
+    }
+}
+fn main() {
+    let mut x = X(Right(main));
+    do (&mut x).with |opt| {  //~ ERROR illegal borrow
+        match *opt {
+            Right(f) => {
+                x = X(Left((0,0)));
+                f()
+            },
+            _ => fail
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
new file mode 100644
index 00000000000..14e268e52ad
--- /dev/null
+++ b/src/test/compile-fail/borrowck-call-method-from-mut-aliasable.rs
@@ -0,0 +1,31 @@
+struct Foo {
+    x: int,
+}
+
+impl Foo {
+    fn f(&self) {}
+    fn g(&const self) {}
+    fn h(&mut self) {}
+}
+
+fn a(x: &mut Foo) {
+    x.f(); //~ ERROR illegal borrow unless pure
+    x.g();
+    x.h();
+}
+
+fn b(x: &Foo) {
+    x.f();
+    x.g();
+    x.h(); //~ ERROR illegal borrow
+}
+
+fn c(x: &const Foo) {
+    x.f(); //~ ERROR illegal borrow unless pure
+    x.g();
+    x.h(); //~ ERROR illegal borrow
+}
+
+fn main() {
+}
+
diff --git a/src/test/compile-fail/borrowck-lend-args.rs b/src/test/compile-fail/borrowck-lend-args.rs
index 7978ffc9550..3cb7009ee27 100644
--- a/src/test/compile-fail/borrowck-lend-args.rs
+++ b/src/test/compile-fail/borrowck-lend-args.rs
@@ -5,7 +5,7 @@ fn borrow_from_arg_imm_ref(&&v: ~int) {
 }
 
 fn borrow_from_arg_mut_ref(&v: ~int) {
-    borrow(v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+    borrow(v); //~ ERROR illegal borrow unless pure
     //~^ NOTE impure due to access to impure function
 }
 
diff --git a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
index 849eefa124e..7fdfbae75a6 100644
--- a/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
+++ b/src/test/compile-fail/borrowck-loan-rcvr-overloaded-op.rs
@@ -47,7 +47,7 @@ fn c() {
 
 
     // ...but not impure fns
-    (*q).times(3); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+    (*q).times(3); //~ ERROR illegal borrow unless pure
     //~^ NOTE impure due to access to impure function
 }
 
diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs
index 39550e6ecd4..7111600d186 100644
--- a/src/test/compile-fail/borrowck-loan-rcvr.rs
+++ b/src/test/compile-fail/borrowck-loan-rcvr.rs
@@ -53,7 +53,7 @@ fn c() {
     (*q).purem();
 
     // ...but not impure fns
-    (*q).impurem(); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+    (*q).impurem(); //~ ERROR illegal borrow unless pure
     //~^ NOTE impure due to access to impure function
 }
 
diff --git a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
index d3e6bb68a48..af002ac686b 100644
--- a/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
+++ b/src/test/compile-fail/borrowck-mut-addr-of-imm-var.rs
@@ -1,6 +1,6 @@
 fn main() {
     let x: int = 3;
-    let y: &mut int = &mut x; //~ ERROR taking mut reference to immutable local variable
+    let y: &mut int = &mut x; //~ ERROR illegal borrow
     *y = 5;
     log (debug, *y);
 }
diff --git a/src/test/compile-fail/borrowck-mut-deref-comp.rs b/src/test/compile-fail/borrowck-mut-deref-comp.rs
index 005689a9897..19964740a6a 100644
--- a/src/test/compile-fail/borrowck-mut-deref-comp.rs
+++ b/src/test/compile-fail/borrowck-mut-deref-comp.rs
@@ -1,7 +1,7 @@
 enum foo = ~int;
 
 fn borrow(x: @mut foo) {
-    let _y = &***x; //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+    let _y = &***x; //~ ERROR illegal borrow unless pure
     *x = foo(~4); //~ NOTE impure due to assigning to dereference of mutable @ pointer
 }
 
diff --git a/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
new file mode 100644
index 00000000000..652183a053e
--- /dev/null
+++ b/src/test/compile-fail/borrowck-mut-slice-of-imm-vec.rs
@@ -0,0 +1,8 @@
+fn write(v: &[mut int]) {
+    v[0] += 1;
+}
+
+fn main() {
+    let v = ~[1, 2, 3];
+    write(v); //~ ERROR illegal borrow
+}
diff --git a/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs b/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
index da394fbf372..7b8d8e99b0a 100644
--- a/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
+++ b/src/test/compile-fail/borrowck-mut-vec-as-imm-slice-bad.rs
@@ -5,7 +5,7 @@ fn want_slice(v: &[int]) -> int {
 }
 
 fn has_mut_vec(+v: @~[mut int]) -> int {
-    want_slice(*v) //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+    want_slice(*v) //~ ERROR illegal borrow unless pure
         //~^ NOTE impure due to access to impure function
 }
 
diff --git a/src/test/compile-fail/borrowck-pure-scope-in-call.rs b/src/test/compile-fail/borrowck-pure-scope-in-call.rs
index b7cb1202c3d..59707bfcf21 100644
--- a/src/test/compile-fail/borrowck-pure-scope-in-call.rs
+++ b/src/test/compile-fail/borrowck-pure-scope-in-call.rs
@@ -4,7 +4,7 @@ fn test1(x: @mut ~int) {
     // Here, evaluating the second argument actually invalidates the
     // first borrow, even though it occurs outside of the scope of the
     // borrow!
-    pure_borrow(*x, *x = ~5);  //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
+    pure_borrow(*x, *x = ~5);  //~ ERROR illegal borrow unless pure
     //~^ NOTE impure due to assigning to dereference of mutable @ pointer
 }
 
diff --git a/src/test/compile-fail/borrowck-uniq-via-box.rs b/src/test/compile-fail/borrowck-uniq-via-box.rs
index 130a6f6bd85..c3fa58b617a 100644
--- a/src/test/compile-fail/borrowck-uniq-via-box.rs
+++ b/src/test/compile-fail/borrowck-uniq-via-box.rs
@@ -1,23 +1,19 @@
 fn borrow(_v: &int) {}
 
 fn box_mut(v: @mut ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(*v); //~ ERROR illegal borrow unless pure
 }
 
 fn box_rec_mut(v: @{mut f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_mut_rec(v: @mut {f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_mut_recs(v: @mut {f: {g: {h: ~int}}}) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
 }
 
 fn box_imm(v: @~int) {
@@ -33,28 +29,23 @@ fn box_imm_recs(v: @{f: {g: {h: ~int}}}) {
 }
 
 fn box_const(v: @const ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(*v); //~ ERROR illegal borrow unless pure
 }
 
 fn box_rec_const(v: @{const f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_recs_const(v: @{f: {g: {const h: ~int}}}) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
 }
 
 fn box_const_rec(v: @const {f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_const_recs(v: @const {f: {g: {h: ~int}}}) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
 }
 
 fn main() {
diff --git a/src/test/compile-fail/borrowck-uniq-via-ref.rs b/src/test/compile-fail/borrowck-uniq-via-ref.rs
index e52b3c7807d..1a5390e62a6 100644
--- a/src/test/compile-fail/borrowck-uniq-via-ref.rs
+++ b/src/test/compile-fail/borrowck-uniq-via-ref.rs
@@ -1,23 +1,19 @@
 fn borrow(_v: &int) {}
 
 fn box_mut(v: &mut ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(*v); //~ ERROR illegal borrow unless pure
 }
 
 fn box_rec_mut(v: &{mut f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_mut_rec(v: &mut {f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_mut_recs(v: &mut {f: {g: {h: ~int}}}) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, mutable memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
 }
 
 fn box_imm(v: &~int) {
@@ -33,28 +29,23 @@ fn box_imm_recs(v: &{f: {g: {h: ~int}}}) {
 }
 
 fn box_const(v: &const ~int) {
-    borrow(*v); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(*v); //~ ERROR illegal borrow unless pure
 }
 
 fn box_rec_const(v: &{const f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_recs_const(v: &{f: {g: {const h: ~int}}}) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
 }
 
 fn box_const_rec(v: &const {f: ~int}) {
-    borrow(v.f); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f); //~ ERROR illegal borrow unless pure
 }
 
 fn box_const_recs(v: &const {f: {g: {h: ~int}}}) {
-    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure: creating immutable alias to aliasable, const memory
-    //~^ NOTE impure due to access to impure function
+    borrow(v.f.g.h); //~ ERROR illegal borrow unless pure
 }
 
 fn main() {
diff --git a/src/test/compile-fail/issue-511.rs b/src/test/compile-fail/issue-511.rs
index 9fb642e0d66..a3498dd1968 100644
--- a/src/test/compile-fail/issue-511.rs
+++ b/src/test/compile-fail/issue-511.rs
@@ -7,6 +7,5 @@ fn f<T:Eq>(&o: Option<T>) {
 
 fn main() {
     f::<int>(option::None);
-    //~^ ERROR taking mut reference to static item
-    //~^^ ERROR illegal borrow: creating mutable alias to aliasable, immutable memory
+    //~^ ERROR illegal borrow: creating mutable alias to static item
 }
diff --git a/src/test/run-pass/auto-ref-newtype.rs b/src/test/run-pass/auto-ref-newtype.rs
new file mode 100644
index 00000000000..184acbfa44f
--- /dev/null
+++ b/src/test/run-pass/auto-ref-newtype.rs
@@ -0,0 +1,14 @@
+// Check that we can define inherent methods on newtype enums that use
+// an auto-ref'd receiver.
+
+enum Foo = uint;
+
+impl Foo {
+    fn len(&self) -> uint { **self }
+}
+
+fn main() {
+    let m = Foo(3);
+    assert m.len() == 3;
+}
+
diff --git a/src/test/run-pass/autoderef-and-borrow-method-receiver.rs b/src/test/run-pass/autoderef-and-borrow-method-receiver.rs
index 3a47a47aad6..2e60b416887 100644
--- a/src/test/run-pass/autoderef-and-borrow-method-receiver.rs
+++ b/src/test/run-pass/autoderef-and-borrow-method-receiver.rs
@@ -3,7 +3,7 @@ struct Foo {
 }
 
 impl Foo {
-    fn f(&self) {}
+    fn f(&const self) {}
 }
 
 fn g(x: &mut Foo) {