about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2012-08-28 15:54:45 -0700
committerNiko Matsakis <niko@alum.mit.edu>2012-09-06 06:11:12 -0700
commit5e36a997945ddc3964a1fe937bc5390cc5b526c8 (patch)
tree0c37dfa0d20004d0098e0bbb620744a1ad4a81d8 /src
parentadc1427282b4da8f963550e87cdbe512157958b4 (diff)
downloadrust-5e36a997945ddc3964a1fe937bc5390cc5b526c8.tar.gz
rust-5e36a997945ddc3964a1fe937bc5390cc5b526c8.zip
Refactor trans to replace lvalue and friends with Datum.
Also:
- report illegal move/ref combos whether or not ref comes first
- commented out fix for #3387, too restrictive and causes an ICE
Diffstat (limited to 'src')
-rw-r--r--src/libcore/util.rs4
-rw-r--r--src/libstd/getopts.rs24
-rw-r--r--src/libstd/par.rs4
-rw-r--r--src/libsyntax/print/pprust.rs51
-rw-r--r--src/rt/rust_upcall.cpp5
-rw-r--r--src/rustc/lib/llvm.rs10
-rw-r--r--src/rustc/middle/borrowck.rs86
-rw-r--r--src/rustc/middle/borrowck/check_loans.rs12
-rw-r--r--src/rustc/middle/borrowck/gather_loans.rs34
-rw-r--r--src/rustc/middle/kind.rs2
-rw-r--r--src/rustc/middle/liveness.rs22
-rw-r--r--src/rustc/middle/region.rs4
-rw-r--r--src/rustc/middle/trans/alt.rs448
-rw-r--r--src/rustc/middle/trans/base.rs3962
-rw-r--r--src/rustc/middle/trans/block.rs0
-rw-r--r--src/rustc/middle/trans/build.rs9
-rw-r--r--src/rustc/middle/trans/callee.rs665
-rw-r--r--src/rustc/middle/trans/closure.rs306
-rw-r--r--src/rustc/middle/trans/common.rs172
-rw-r--r--src/rustc/middle/trans/consts.rs95
-rw-r--r--src/rustc/middle/trans/controlflow.rs346
-rw-r--r--src/rustc/middle/trans/datum.rs703
-rw-r--r--src/rustc/middle/trans/expr.rs1371
-rw-r--r--src/rustc/middle/trans/foreign.rs398
-rw-r--r--src/rustc/middle/trans/glue.rs681
-rw-r--r--src/rustc/middle/trans/impl.rs356
-rw-r--r--src/rustc/middle/trans/inline.rs87
-rw-r--r--src/rustc/middle/trans/macros.rs45
-rw-r--r--src/rustc/middle/trans/monomorphize.rs276
-rw-r--r--src/rustc/middle/trans/reachable.rs6
-rw-r--r--src/rustc/middle/trans/reflect.rs27
-rw-r--r--src/rustc/middle/trans/tvec.rs573
-rw-r--r--src/rustc/middle/trans/type_of.rs34
-rw-r--r--src/rustc/middle/trans/type_use.rs40
-rw-r--r--src/rustc/middle/trans/uniq.rs49
-rw-r--r--src/rustc/middle/ty.rs228
-rw-r--r--src/rustc/middle/typeck.rs13
-rw-r--r--src/rustc/middle/typeck/check.rs30
-rw-r--r--src/rustc/middle/typeck/check/alt.rs110
-rw-r--r--src/rustc/middle/typeck/check/regionck.rs15
-rw-r--r--src/rustc/middle/typeck/infer/region_var_bindings.rs28
-rw-r--r--src/rustc/rustc.rc7
-rw-r--r--src/rustc/util/ppaux.rs11
-rw-r--r--src/test/bench/graph500-bfs.rs16
-rw-r--r--src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs9
-rw-r--r--src/test/compile-fail/borrowck-confuse-region.rs4
-rw-r--r--src/test/compile-fail/borrowck-loan-in-overloaded-op.rs15
-rw-r--r--src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs2
-rw-r--r--src/test/run-pass/autoderef-method-on-trait-monomorphized.rs16
-rw-r--r--src/test/run-pass/autoderef-method-on-trait.rs12
-rw-r--r--src/test/run-pass/const-fields-and-indexing.rs4
-rw-r--r--src/test/run-pass/pipe-presentation-examples.rs4
52 files changed, 6387 insertions, 5044 deletions
diff --git a/src/libcore/util.rs b/src/libcore/util.rs
index 57590dbfc3e..a27b8fe86bc 100644
--- a/src/libcore/util.rs
+++ b/src/libcore/util.rs
@@ -65,7 +65,9 @@ mod tests {
     fn identity_crisis() {
         // Writing a test for the identity function. How did it come to this?
         let x = ~[(5, false)];
-        assert x.eq(id(copy x));
+        //FIXME #3387 assert x.eq(id(copy x));
+        let y = copy x;
+        assert x.eq(id(y));
     }
     #[test]
     fn test_swap() {
diff --git a/src/libstd/getopts.rs b/src/libstd/getopts.rs
index 49d4e8bc268..50bad1e748a 100644
--- a/src/libstd/getopts.rs
+++ b/src/libstd/getopts.rs
@@ -800,10 +800,11 @@ mod tests {
         let rs = getopts(args, opts);
         match rs {
           Ok(m) => {
-            assert (opt_present(m, ~"test"));
-            assert (opt_str(m, ~"test") == ~"20");
-            assert (opt_strs(m, ~"test")[0] == ~"20");
-            assert (opt_strs(m, ~"test")[1] == ~"30");
+              assert (opt_present(m, ~"test"));
+              assert (opt_str(m, ~"test") == ~"20");
+              let pair = opt_strs(m, ~"test");
+              assert (pair[0] == ~"20");
+              assert (pair[1] == ~"30");
           }
           _ => fail
         }
@@ -854,8 +855,9 @@ mod tests {
           Ok(m) => {
             assert (opt_present(m, ~"t"));
             assert (opt_str(m, ~"t") == ~"20");
-            assert (opt_strs(m, ~"t")[0] == ~"20");
-            assert (opt_strs(m, ~"t")[1] == ~"30");
+            let pair = opt_strs(m, ~"t");
+            assert (pair[0] == ~"20");
+            assert (pair[1] == ~"30");
           }
           _ => fail
         }
@@ -903,10 +905,12 @@ mod tests {
             assert (opt_present(m, ~"flag"));
             assert (opt_str(m, ~"long") == ~"30");
             assert (opt_present(m, ~"f"));
-            assert (opt_strs(m, ~"m")[0] == ~"40");
-            assert (opt_strs(m, ~"m")[1] == ~"50");
-            assert (opt_strs(m, ~"n")[0] == ~"-A B");
-            assert (opt_strs(m, ~"n")[1] == ~"-60 70");
+            let pair = opt_strs(m, ~"m");
+            assert (pair[0] == ~"40");
+            assert (pair[1] == ~"50");
+            let pair = opt_strs(m, ~"n");
+            assert (pair[0] == ~"-A B");
+            assert (pair[1] == ~"-60 70");
             assert (!opt_present(m, ~"notpresent"));
           }
           _ => fail
diff --git a/src/libstd/par.rs b/src/libstd/par.rs
index 97057ed20a5..1735765a474 100644
--- a/src/libstd/par.rs
+++ b/src/libstd/par.rs
@@ -19,7 +19,7 @@ const min_granularity : uint = 1024u;
  * like map or alli.
  */
 fn map_slices<A: copy send, B: copy send>(
-    xs: ~[A],
+    xs: &[A],
     f: fn() -> fn~(uint, v: &[A]) -> B)
     -> ~[B] {
 
@@ -104,7 +104,7 @@ fn mapi<A: copy send, B: copy send>(xs: ~[A],
  * inner elements. This is to skirt the need for copy constructors.
  */
 fn mapi_factory<A: copy send, B: copy send>(
-    xs: ~[A], f: fn() -> fn~(uint, A) -> B) -> ~[B] {
+    xs: &[A], f: fn() -> fn~(uint, A) -> B) -> ~[B] {
     let slices = map_slices(xs, || {
         let f = f();
         fn~(base: uint, slice : &[A], move f) -> ~[B] {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index dd5fc57f684..8b17760c7f2 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1169,29 +1169,38 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
               None => ()
             }
             word_space(s, ~"=>");
+
             // Extract the expression from the extra block the parser adds
-            assert arm.body.node.view_items.is_empty();
-            assert arm.body.node.stmts.is_empty();
-            assert arm.body.node.rules == ast::default_blk;
-            match arm.body.node.expr {
-              Some(expr) => {
-                match expr.node {
-                  ast::expr_block(blk) => {
-                    // the block will close the pattern's ibox
-                    print_block_unclosed_indent(s, blk, alt_indent_unit);
-                  }
-                  _ => {
-                    end(s); // close the ibox for the pattern
-                    print_expr(s, expr);
-                  }
-                }
-                if !expr_is_simple_block(expr)
-                    && i < len - 1 {
-                    word(s.s, ~",");
+            // in the case of foo => expr
+            if arm.body.node.view_items.is_empty() &&
+                arm.body.node.stmts.is_empty() &&
+                arm.body.node.rules == ast::default_blk &&
+                arm.body.node.expr.is_some()
+            {
+                match arm.body.node.expr {
+                    Some(expr) => {
+                        match expr.node {
+                            ast::expr_block(blk) => {
+                                // the block will close the pattern's ibox
+                                print_block_unclosed_indent(
+                                    s, blk, alt_indent_unit);
+                            }
+                            _ => {
+                                end(s); // close the ibox for the pattern
+                                print_expr(s, expr);
+                            }
+                        }
+                        if !expr_is_simple_block(expr)
+                            && i < len - 1 {
+                            word(s.s, ~",");
+                        }
+                        end(s); // close enclosing cbox
+                    }
+                    None => fail
                 }
-                end(s); // close enclosing cbox
-              }
-              None => fail
+            } else {
+                // the block will close the pattern's ibox
+                print_block_unclosed_indent(s, arm.body, alt_indent_unit);
             }
         }
         bclose_(s, expr.span, alt_indent_unit);
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 8bab4b9e2d3..e59c1b2b8c7 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -147,7 +147,6 @@ extern "C" CDECL void
 upcall_s_exchange_malloc(s_exchange_malloc_args *args) {
     rust_task *task = args->task;
     LOG_UPCALL_ENTRY(task);
-    LOG(task, mem, "upcall exchange malloc(0x%" PRIxPTR ")", args->td);
 
     size_t total_size = get_box_size(args->size, args->td->align);
     // FIXME--does this have to be calloc? (Issue #2682)
@@ -159,6 +158,9 @@ upcall_s_exchange_malloc(s_exchange_malloc_args *args) {
     header->prev = 0;
     header->next = 0;
 
+    LOG(task, mem, "exchange malloced %p of size %" PRIuPTR,
+        header, args->size);
+
     args->retval = (uintptr_t)header;
 }
 
@@ -187,6 +189,7 @@ extern "C" CDECL void
 upcall_s_exchange_free(s_exchange_free_args *args) {
     rust_task *task = args->task;
     LOG_UPCALL_ENTRY(task);
+    LOG(task, mem, "exchange freed %p", args->ptr);
     task->kernel->free(args->ptr);
 }
 
diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs
index 6da8a9d57c9..fe737442bd0 100644
--- a/src/rustc/lib/llvm.rs
+++ b/src/rustc/lib/llvm.rs
@@ -456,9 +456,11 @@ extern mod llvm {
        ValueRef;
     fn LLVMConstAShr(LHSConstant: ValueRef, RHSConstant: ValueRef) ->
        ValueRef;
-    fn LLVMConstGEP(ConstantVal: ValueRef, ConstantIndices: *uint,
+    fn LLVMConstGEP(ConstantVal: ValueRef,
+                    ConstantIndices: *ValueRef,
                     NumIndices: c_uint) -> ValueRef;
-    fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: *uint,
+    fn LLVMConstInBoundsGEP(ConstantVal: ValueRef,
+                            ConstantIndices: *ValueRef,
                             NumIndices: c_uint) -> ValueRef;
     fn LLVMConstTrunc(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef;
     fn LLVMConstSExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef;
@@ -493,10 +495,10 @@ extern mod llvm {
     fn LLVMConstShuffleVector(VectorAConstant: ValueRef,
                               VectorBConstant: ValueRef,
                               MaskConstant: ValueRef) -> ValueRef;
-    fn LLVMConstExtractValue(AggConstant: ValueRef, IdxList: *uint,
+    fn LLVMConstExtractValue(AggConstant: ValueRef, IdxList: *c_uint,
                              NumIdx: c_uint) -> ValueRef;
     fn LLVMConstInsertValue(AggConstant: ValueRef,
-                            ElementValueConstant: ValueRef, IdxList: *uint,
+                            ElementValueConstant: ValueRef, IdxList: *c_uint,
                             NumIdx: c_uint) -> ValueRef;
     fn LLVMConstInlineAsm(Ty: TypeRef, AsmString: *c_char,
                           Constraints: *c_char, HasSideEffects: Bool,
diff --git a/src/rustc/middle/borrowck.rs b/src/rustc/middle/borrowck.rs
index 55067d82a77..176e133529c 100644
--- a/src/rustc/middle/borrowck.rs
+++ b/src/rustc/middle/borrowck.rs
@@ -220,7 +220,8 @@ use syntax::visit;
 use syntax::ast_util;
 use syntax::ast_map;
 use syntax::codemap::span;
-use util::ppaux::{ty_to_str, region_to_str, explain_region};
+use util::ppaux::{ty_to_str, region_to_str, explain_region,
+                  note_and_explain_region};
 use std::map::{int_hash, hashmap, set};
 use std::list;
 use std::list::{List, Cons, Nil};
@@ -464,6 +465,7 @@ impl borrowck_ctxt {
             err.cmt.span,
             fmt!("illegal borrow: %s",
                  self.bckerr_code_to_str(err.code)));
+        self.note_and_explain_bckerr(err.code);
     }
 
     fn span_err(s: span, m: ~str) {
@@ -488,37 +490,65 @@ 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))
-          }
-          err_mut_uniq => {
-            ~"unique value in aliasable, mutable location"
-          }
-          err_mut_variant => {
-            ~"enum variant in aliasable, mutable location"
-          }
-          err_root_not_permitted => {
-            // note: I don't expect users to ever see this error
-            // message, reasons are discussed in attempt_root() in
-            // preserve.rs.
-            ~"rooting is not permitted"
-          }
-          err_out_of_root_scope(super_scope, sub_scope) => {
-            fmt!("managed value would have to be rooted for %s, \
-                  but can only be rooted for %s",
-                  explain_region(self.tcx, sub_scope),
-                  explain_region(self.tcx, super_scope))
-          }
-          err_out_of_scope(super_scope, sub_scope) => {
-            fmt!("borrowed pointer must be valid for %s, \
-                  but the borrowed value is only valid for %s",
-                  explain_region(self.tcx, sub_scope),
-                  explain_region(self.tcx, super_scope))
+            err_mutbl(req, act) => {
+                fmt!("creating %s alias to aliasable, %s memory",
+                     self.mut_to_str(req), self.mut_to_str(act))
+            }
+            err_mut_uniq => {
+                ~"unique value in aliasable, mutable location"
+            }
+            err_mut_variant => {
+                ~"enum variant in aliasable, mutable location"
+            }
+            err_root_not_permitted => {
+                // note: I don't expect users to ever see this error
+                // message, reasons are discussed in attempt_root() in
+                // preserve.rs.
+                ~"rooting is not permitted"
+            }
+            err_out_of_root_scope(*) => {
+                ~"cannot root managed value long enough"
+            }
+            err_out_of_scope(*) => {
+                ~"borrowed value does not live long enough"
+            }
+        }
+    }
+
+    fn note_and_explain_bckerr(code: bckerr_code) {
+        match code {
+            err_mutbl(*) | err_mut_uniq | err_mut_variant |
+            err_root_not_permitted => {}
+
+            err_out_of_root_scope(super_scope, sub_scope) => {
+                note_and_explain_region(
+                    self.tcx,
+                    ~"managed value would have to be rooted for ",
+                    sub_scope,
+                    ~"...");
+                note_and_explain_region(
+                    self.tcx,
+                    ~"...but can only be rooted for ",
+                    super_scope,
+                    ~"");
+            }
+
+            err_out_of_scope(super_scope, sub_scope) => {
+                note_and_explain_region(
+                    self.tcx,
+                    ~"borrowed pointer must be valid for ",
+                    sub_scope,
+                    ~"...");
+                note_and_explain_region(
+                    self.tcx,
+                    ~"...but borrowed value is only valid for ",
+                    super_scope,
+                    ~"");
           }
         }
     }
 
+
     fn cmt_to_str(cmt: cmt) -> ~str {
         let mc = &mem_categorization_ctxt {tcx: self.tcx,
                                            method_map: self.method_map};
diff --git a/src/rustc/middle/borrowck/check_loans.rs b/src/rustc/middle/borrowck/check_loans.rs
index a2e2049dcf7..7f2964181d8 100644
--- a/src/rustc/middle/borrowck/check_loans.rs
+++ b/src/rustc/middle/borrowck/check_loans.rs
@@ -423,6 +423,7 @@ impl check_loan_ctxt {
                     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.tcx().sess.span_note(
                     sp,
                     fmt!("impure due to %s", msg));
@@ -484,10 +485,14 @@ impl check_loan_ctxt {
     // when there is an outstanding loan.  In that case, it is not
     // safe to consider the use a last_use.
     fn check_last_use(expr: @ast::expr) {
+        debug!("Checking last use of expr %?", expr.id);
         let cmt = self.bccx.cat_expr(expr);
         let lp = match cmt.lp {
-          None => return,
-          Some(lp) => lp
+            None => {
+                debug!("Not a loanable expression");
+                return;
+            }
+            Some(lp) => lp
         };
         for self.walk_loans_of(cmt.id, lp) |_loan| {
             debug!("Removing last use entry %? due to outstanding loan",
@@ -592,6 +597,9 @@ fn check_loans_in_local(local: @ast::local,
 fn check_loans_in_expr(expr: @ast::expr,
                        &&self: check_loan_ctxt,
                        vt: visit::vt<check_loan_ctxt>) {
+    debug!("check_loans_in_expr(expr=%?/%s)",
+           expr.id, pprust::expr_to_str(expr, self.tcx().sess.intr()));
+
     self.check_for_conflicting_loans(expr.id);
 
     match expr.node {
diff --git a/src/rustc/middle/borrowck/gather_loans.rs b/src/rustc/middle/borrowck/gather_loans.rs
index 3f31e011b05..18e27e79fd1 100644
--- a/src/rustc/middle/borrowck/gather_loans.rs
+++ b/src/rustc/middle/borrowck/gather_loans.rs
@@ -90,8 +90,8 @@ fn req_loans_in_expr(ex: @ast::expr,
     let tcx = bccx.tcx;
     let old_root_ub = self.root_ub;
 
-    debug!("req_loans_in_expr(ex=%s)",
-           pprust::expr_to_str(ex, tcx.sess.intr()));
+    debug!("req_loans_in_expr(expr=%?/%s)",
+           ex.id, pprust::expr_to_str(ex, tcx.sess.intr()));
 
     // If this expression is borrowed, have to ensure it remains valid:
     for tcx.borrowings.find(ex.id).each |borrow| {
@@ -200,6 +200,21 @@ fn req_loans_in_expr(ex: @ast::expr,
         visit::visit_expr(ex, self, vt);
       }
 
+      // FIXME--#3387
+      // ast::expr_binary(_, lhs, rhs) => {
+      //     // Universal comparison operators like ==, >=, etc
+      //     // take their arguments by reference.
+      //     let lhs_ty = ty::expr_ty(self.tcx(), lhs);
+      //     if !ty::type_is_scalar(lhs_ty) {
+      //         let scope_r = ty::re_scope(ex.id);
+      //         let lhs_cmt = self.bccx.cat_expr(lhs);
+      //         self.guarantee_valid(lhs_cmt, m_imm, scope_r);
+      //         let rhs_cmt = self.bccx.cat_expr(rhs);
+      //         self.guarantee_valid(rhs_cmt, m_imm, scope_r);
+      //     }
+      //     visit::visit_expr(ex, self, vt);
+      // }
+
       ast::expr_field(rcvr, _, _)
       if self.bccx.method_map.contains_key(ex.id) => {
         // Receivers in method calls are always passed by ref.
@@ -395,14 +410,15 @@ impl gather_loan_ctxt {
     }
 
     fn add_loans(scope_id: ast::node_id, loans: @DVec<loan>) {
+        debug!("adding %u loans to scope_id %?", loans.len(), scope_id);
         match self.req_maps.req_loan_map.find(scope_id) {
-          Some(l) => {
-            (*l).push(loans);
-          }
-          None => {
-            self.req_maps.req_loan_map.insert(
-                scope_id, @dvec::from_vec(~[mut loans]));
-          }
+            Some(l) => {
+                l.push(loans);
+            }
+            None => {
+                self.req_maps.req_loan_map.insert(
+                    scope_id, @dvec::from_vec(~[mut loans]));
+            }
         }
     }
 
diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs
index 259b0a111f8..b1396472d86 100644
--- a/src/rustc/middle/kind.rs
+++ b/src/rustc/middle/kind.rs
@@ -419,7 +419,7 @@ fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
 
 fn check_copy_ex(cx: ctx, ex: @expr, implicit_copy: bool,
                  why: Option<(&str,&str)>) {
-    if ty::expr_is_lval(cx.method_map, ex) &&
+    if ty::expr_is_lval(cx.tcx, cx.method_map, ex) &&
 
         // this is a move
         !cx.last_use_map.contains_key(ex.id) &&
diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs
index 1dbbec91402..f3459aa8cb3 100644
--- a/src/rustc/middle/liveness.rs
+++ b/src/rustc/middle/liveness.rs
@@ -1572,17 +1572,16 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
 
       expr_call(f, args, _) => {
         let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f));
-        vt.visit_expr(f, self, vt);
         do vec::iter2(args, targs) |arg_expr, arg_ty| {
             match ty::resolved_mode(self.tcx, arg_ty.mode) {
-              by_val | by_copy | by_ref | by_mutbl_ref => {
-                vt.visit_expr(arg_expr, self, vt);
-              }
-              by_move => {
-                self.check_move_from_expr(arg_expr, vt);
-              }
+                by_val | by_copy | by_ref | by_mutbl_ref => {}
+                by_move => {
+                    self.check_move_from_expr(arg_expr, vt);
+                }
             }
         }
+
+        visit::visit_expr(expr, self, vt);
       }
 
       // no correctness conditions related to liveness
@@ -1670,6 +1669,9 @@ impl @Liveness {
     }
 
     fn consider_last_use(expr: @expr, ln: LiveNode, var: Variable) {
+        debug!("consider_last_use(expr.id=%?, ln=%s, var=%s)",
+               expr.id, ln.to_str(), var.to_str());
+
         match self.live_on_exit(ln, var) {
           Some(_) => {}
           None => (*self.ir).add_last_use(expr.id, var)
@@ -1682,7 +1684,7 @@ impl @Liveness {
 
         if self.ir.method_map.contains_key(expr.id) {
             // actually an rvalue, since this calls a method
-            return vt.visit_expr(expr, self, vt);
+            return;
         }
 
         match expr.node {
@@ -1703,18 +1705,16 @@ impl @Liveness {
             self.check_move_from_expr(base, vt);
           }
 
-          expr_index(base, idx) => {
+          expr_index(base, _) => {
             // Moving from x[y] is allowed if x is never used later.
             // (Note that the borrowck guarantees that anything
             //  being moved from is uniquely tied to the stack frame)
             self.check_move_from_expr(base, vt);
-            vt.visit_expr(idx, self, vt);
           }
 
           _ => {
             // For other kinds of lvalues, no checks are required,
             // and any embedded expressions are actually rvalues
-            vt.visit_expr(expr, self, vt);
           }
        }
     }
diff --git a/src/rustc/middle/region.rs b/src/rustc/middle/region.rs
index 5b9a2e70106..579c096e634 100644
--- a/src/rustc/middle/region.rs
+++ b/src/rustc/middle/region.rs
@@ -254,6 +254,10 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
 
     let mut new_cx = cx;
     match expr.node {
+      // Calls or overloadable operators
+      // FIXME #3387
+      // ast::expr_index(*) | ast::expr_binary(*) |
+      // ast::expr_unary(*) |
       ast::expr_call(*) => {
         debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
                                                            cx.sess.intr()));
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index a96529fa01c..643e385cd17 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -14,8 +14,11 @@ use middle::resolve::DefMap;
 use back::abi;
 use std::map::hashmap;
 use dvec::DVec;
-
+use datum::*;
 use common::*;
+use expr::Dest;
+
+fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
 
 // An option identifying a branch (either a literal, a enum variant or a
 // range)
@@ -37,38 +40,25 @@ fn opt_eq(tcx: ty::ctxt, a: opt, b: opt) -> bool {
 }
 
 enum opt_result {
-    single_result(result),
-    range_result(result, result),
+    single_result(Result),
+    range_result(Result, Result),
 }
 fn trans_opt(bcx: block, o: opt) -> opt_result {
     let _icx = bcx.insn_ctxt("alt::trans_opt");
     let ccx = bcx.ccx();
     let mut bcx = bcx;
     match o {
-      lit(l) => {
-        match l.node {
-          ast::expr_vstore(@{node: ast::expr_lit(
-              @{node: ast::lit_str(s), _}), _},
-                           ast::vstore_uniq) => {
-            let strty = ty::mk_estr(bcx.tcx(), ty::vstore_uniq);
-            let cell = empty_dest_cell();
-            bcx = tvec::trans_estr(bcx, s, Some(ast::vstore_uniq),
-                                   by_val(cell));
-            add_clean_temp_immediate(bcx, *cell, strty);
-            return single_result(rslt(bcx, *cell));
-          }
-          _ => {
-            return single_result(trans_temp_expr(bcx, l));
-          }
+        lit(lit_expr) => {
+            let datumblock = expr::trans_to_datum(bcx, lit_expr);
+            return single_result(datumblock.to_result());
+        }
+        var(disr_val, _) => {
+            return single_result(rslt(bcx, C_int(ccx, disr_val)));
+        }
+        range(l1, l2) => {
+            return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
+                                rslt(bcx, consts::const_expr(ccx, l2)));
         }
-      }
-      var(disr_val, _) => {
-        return single_result(rslt(bcx, C_int(ccx, disr_val)));
-      }
-      range(l1, l2) => {
-        return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
-                         rslt(bcx, consts::const_expr(ccx, l2)));
-      }
     }
 }
 
@@ -140,7 +130,7 @@ fn expand_nested_bindings(bcx: block, m: match_, col: uint, val: ValueRef)
                                             ty: node_id_type(bcx,
                                                              br.pats[col].id)
                                          }}]),
-                                .. *br});
+                        ..*br});
           }
           _ => vec::push(result, br)
         }
@@ -173,7 +163,7 @@ fn enter_match(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef,
               }
               _ => br.bound
             };
-            vec::push(result, @{pats: pats, bound: bound,.. *br});
+            vec::push(result, @{pats: pats, bound: bound, ..*br});
           }
           None => ()
         }
@@ -229,11 +219,10 @@ fn enter_rec_or_struct(bcx: block, dm: DefMap, m: match_, col: uint,
           ast::pat_rec(fpats, _) | ast::pat_struct(_, fpats, _) => {
             let mut pats = ~[];
             for vec::each(fields) |fname| {
-                let mut pat = dummy;
-                for vec::each(fpats) |fpat| {
-                    if fpat.ident == fname { pat = fpat.pat; break; }
+                match fpats.find(|p| p.ident == fname) {
+                    None => vec::push(pats, dummy),
+                    Some(pat) => vec::push(pats, pat.pat)
                 }
-                vec::push(pats, pat);
             }
             Some(pats)
           }
@@ -327,38 +316,25 @@ fn extract_variant_args(bcx: block, pat_id: ast::node_id,
     return {vals: args, bcx: bcx};
 }
 
-fn collect_record_fields(m: match_, col: uint) -> ~[ast::ident] {
+fn collect_record_or_struct_fields(m: match_, col: uint) -> ~[ast::ident] {
     let mut fields: ~[ast::ident] = ~[];
     for vec::each(m) |br| {
         match br.pats[col].node {
-          ast::pat_rec(fs, _) => {
-            for vec::each(fs) |f| {
-                if !vec::any(fields, |x| f.ident == x) {
-                    vec::push(fields, f.ident);
-                }
-            }
-          }
+          ast::pat_rec(fs, _) => extend(&mut fields, fs),
+          ast::pat_struct(_, fs, _) => extend(&mut fields, fs),
           _ => ()
         }
     }
     return fields;
-}
 
-fn collect_struct_fields(m: match_, col: uint) -> ~[ast::ident] {
-    let mut fields: ~[ast::ident] = ~[];
-    for vec::each(m) |br| {
-        match br.pats[col].node {
-          ast::pat_struct(_, fs, _) => {
-            for vec::each(fs) |f| {
-                if !vec::any(fields, |x| f.ident == x) {
-                    vec::push(fields, f.ident);
-                }
+    fn extend(idents: &mut ~[ast::ident], field_pats: &[ast::field_pat]) {
+        for field_pats.each |field_pat| {
+            let field_ident = field_pat.ident;
+            if !vec::any(*idents, |x| x == field_ident) {
+                vec::push(*idents, field_ident);
             }
-          }
-          _ => ()
         }
     }
-    return fields;
 }
 
 fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) {
@@ -366,17 +342,17 @@ fn root_pats_as_necessary(bcx: block, m: match_, col: uint, val: ValueRef) {
         let pat_id = br.pats[col].id;
 
         match bcx.ccx().maps.root_map.find({id:pat_id, derefs:0u}) {
-          None => (),
-          Some(scope_id) => {
-            // Note: the scope_id will always be the id of the alt.  See the
-            // extended comment in rustc::middle::borrowck::preserve() for
-            // details (look for the case covering cat_discr).
-
-            let ty = node_id_type(bcx, pat_id);
-            let val = load_if_immediate(bcx, val, ty);
-            root_value(bcx, val, ty, scope_id);
-            return; // if we kept going, we'd only be rooting same value again
-          }
+            None => (),
+            Some(scope_id) => {
+                // Note: the scope_id will always be the id of the alt.  See
+                // the extended comment in rustc::middle::borrowck::preserve()
+                // for details (look for the case covering cat_discr).
+
+                let datum = Datum {val: val, ty: node_id_type(bcx, pat_id),
+                                   mode: ByRef, source: FromLvalue};
+                datum.root(bcx, scope_id);
+                return; // if we kept going, we'd only re-root the same value
+            }
         }
     }
 }
@@ -459,7 +435,10 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
     let _icx = bcx.insn_ctxt("alt::compile_submatch");
     let mut bcx = bcx;
     let tcx = bcx.tcx(), dm = tcx.def_map;
-    if m.len() == 0u { Br(bcx, option::get(chk)()); return; }
+    if m.len() == 0u {
+        Br(bcx, option::get(chk)());
+        return;
+    }
     if m[0].pats.len() == 0u {
         let data = m[0].data;
         match data.guard {
@@ -471,27 +450,28 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
 
             for data.id_map.each |key, val| {
                 let binding = assoc(key, m[0].bound).get();
-                let (llval, mode) = (binding.val, binding.mode);
-                let ty = binding.ty;
+                let datum = Datum {val: binding.val, ty: binding.ty,
+                                   mode: ByRef, source: FromLvalue};
 
-                if mode == ast::bind_by_value {
-                    let llty = type_of::type_of(bcx.fcx.ccx, ty);
+                if binding.mode == ast::bind_by_value {
+                    let llty = type_of::type_of(bcx.fcx.ccx, binding.ty);
                     let alloc = alloca(bcx, llty);
-                    bcx = copy_val(bcx, INIT, alloc,
-                                   load_if_immediate(bcx, llval, ty), ty);
+                    bcx = datum.copy_to(bcx, INIT, alloc);
                     bcx.fcx.lllocals.insert(val, local_mem(alloc));
-                    add_clean(bcx, alloc, ty);
-                } else if mode == ast::bind_by_move {
+                    add_clean(bcx, alloc, binding.ty);
+                } else if binding.mode == ast::bind_by_move {
                     fail ~"can't translate bind_by_move into a pattern guard";
                 } else {
-                    bcx.fcx.lllocals.insert(val, local_mem(llval));
+                    bcx.fcx.lllocals.insert(val, local_mem(datum.val));
                 }
             };
-            let {bcx: guard_cx, val} = {
+
+            let Result {bcx: guard_cx, val} = {
                 do with_scope_result(bcx, e.info(), ~"guard") |bcx| {
-                    trans_temp_expr(bcx, e)
+                    expr::trans_to_appropriate_llval(bcx, e)
                 }
             };
+
             bcx = do with_cond(guard_cx, Not(guard_cx, val)) |bcx| {
                 compile_submatch(bcx, vec::tail(m), vals, chk, exits);
                 bcx
@@ -525,58 +505,22 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
 
     root_pats_as_necessary(bcx, m, col, val);
 
-    let rec_fields = collect_record_fields(m, col);
-    // Separate path for extracting and binding record fields
+    let rec_fields = collect_record_or_struct_fields(m, col);
     if rec_fields.len() > 0 {
-        let fields = ty::get_fields(node_id_type(bcx, pat_id));
-        let mut rec_vals = ~[];
-        for vec::each(rec_fields) |field_name| {
-            let ix = option::get(ty::field_idx(field_name, fields));
-            vec::push(rec_vals, GEPi(bcx, val, [0u, ix]));
-        }
-        compile_submatch(bcx,
-                         enter_rec_or_struct(bcx, dm, m, col, rec_fields,
-                                             val),
-                         vec::append(rec_vals, vals_left),
-                         chk,
-                         exits);
-        return;
-    }
-
-    // Separate path for extracting and binding struct fields.
-    let struct_fields = collect_struct_fields(m, col);
-    if struct_fields.len() > 0 {
-        let class_id, class_fields;
-        match ty::get(node_id_type(bcx, pat_id)).struct {
-            ty::ty_class(cid, _) => {
-                class_id = cid;
-                class_fields = ty::lookup_class_fields(ccx.tcx, class_id);
-            }
-            _ => {
-                ccx.tcx.sess.bug(~"struct pattern didn't resolve to a \
-                                   struct");
+        let pat_ty = node_id_type(bcx, pat_id);
+        do expr::with_field_tys(tcx, pat_ty) |_has_dtor, field_tys| {
+            let mut rec_vals = ~[];
+            for vec::each(rec_fields) |field_name| {
+                let ix = ty::field_idx_strict(tcx, field_name, field_tys);
+                vec::push(rec_vals, GEPi(bcx, val, struct_field(ix)));
             }
+            compile_submatch(
+                bcx,
+                enter_rec_or_struct(bcx, dm, m, col, rec_fields, val),
+                vec::append(rec_vals, vals_left),
+                chk,
+                exits);
         }
-
-        // Index the class fields.
-        let field_map = std::map::uint_hash();
-        for class_fields.eachi |i, class_field| {
-            field_map.insert(class_field.ident, i);
-        }
-
-        // Fetch each field.
-        let mut struct_vals = ~[];
-        for struct_fields.each |field_name| {
-            let index = field_map.get(field_name);
-            let fldptr = base::get_struct_field(bcx, val, class_id, index);
-            vec::push(struct_vals, fldptr);
-        }
-        compile_submatch(bcx,
-                         enter_rec_or_struct(bcx, dm, m, col, struct_fields,
-                                             val),
-                         vec::append(struct_vals, vals_left),
-                         chk,
-                         exits);
         return;
     }
 
@@ -641,8 +585,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
                    else { compare };
           }
           range(_, _) => {
-            let pty = node_id_type(bcx, pat_id);
-            test_val = load_if_immediate(bcx, val, pty);
+            test_val = Load(bcx, val);
             kind = compare;
           }
         }
@@ -665,6 +608,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
     let exhaustive = option::is_none(chk) && defaults.len() == 0u;
     let len = opts.len();
     let mut i = 0u;
+
     // Compile subtrees for each option
     for vec::each(opts) |opt| {
         i += 1u;
@@ -685,20 +629,25 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
               }
               compare => {
                 let t = node_id_type(bcx, pat_id);
-                let {bcx: after_cx, val: matches} = {
+                let Result {bcx: after_cx, val: matches} = {
                     do with_scope_result(bcx, None, ~"compare_scope") |bcx| {
                         match trans_opt(bcx, opt) {
-                          single_result({bcx, val}) => {
-                            trans_compare(bcx, ast::eq, test_val, t, val, t)
-                          }
-                          range_result(
-                              {val: vbegin, _}, {bcx, val: vend}) => {
-                            let {bcx, val: llge} = trans_compare(
-                                bcx, ast::ge, test_val, t, vbegin, t);
-                            let {bcx, val: llle} = trans_compare(
-                                bcx, ast::le, test_val, t, vend, t);
-                            {bcx: bcx, val: And(bcx, llge, llle)}
-                          }
+                            single_result(
+                                Result {bcx, val}) =>
+                            {
+                                trans_compare(bcx, ast::eq, test_val,
+                                              t, val, t)
+                            }
+                            range_result(
+                                Result {val: vbegin, _},
+                                Result {bcx, val: vend}) =>
+                            {
+                                let Result {bcx, val: llge} = trans_compare(
+                                    bcx, ast::ge, test_val, t, vbegin, t);
+                                let Result {bcx, val: llle} = trans_compare(
+                                    bcx, ast::le, test_val, t, vend, t);
+                                rslt(bcx, And(bcx, llge, llle))
+                            }
                         }
                     }
                 };
@@ -805,19 +754,17 @@ fn make_pattern_bindings(bcx: block, phi_bindings: phi_bindings_list)
             }
             ast::bind_by_value | ast::bind_by_move => {
                 // by value: make a new temporary and copy the value out
-                let lltype = type_of::type_of(bcx.fcx.ccx, binding.ty);
-                let allocation = alloca(bcx, lltype);
-                let ty = binding.ty;
-                bcx = if binding.mode == ast::bind_by_value {
-                    copy_val(bcx, INIT, allocation,
-                             load_if_immediate(bcx, phi_val, ty), ty)
+                let phi_datum = Datum {val: phi_val, ty: binding.ty,
+                                       mode: ByRef, source: FromLvalue};
+                let scratch = scratch_datum(bcx, binding.ty, false);
+                if binding.mode == ast::bind_by_value {
+                    phi_datum.copy_to_datum(bcx, INIT, scratch);
                 } else {
-                    move_val(bcx, INIT, allocation,
-                             {bcx: bcx, val: phi_val, kind: lv_owned}, ty)
-                };
+                    phi_datum.move_to_datum(bcx, INIT, scratch);
+                }
                 bcx.fcx.lllocals.insert(binding.pat_id,
-                                        local_mem(allocation));
-                add_clean(bcx, allocation, ty);
+                                        local_mem(scratch.val));
+                add_clean(bcx, scratch.val, binding.ty);
             }
         }
     }
@@ -827,24 +774,31 @@ fn make_pattern_bindings(bcx: block, phi_bindings: phi_bindings_list)
 
 fn trans_alt(bcx: block,
              alt_expr: @ast::expr,
-             expr: @ast::expr,
+             discr_expr: @ast::expr,
              arms: ~[ast::arm],
-             dest: dest) -> block {
+             dest: Dest) -> block {
     let _icx = bcx.insn_ctxt("alt::trans_alt");
     do with_scope(bcx, alt_expr.info(), ~"alt") |bcx| {
-        trans_alt_inner(bcx, expr, arms, dest)
+        trans_alt_inner(bcx, discr_expr, arms, dest)
     }
 }
 
-fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm],
-                   dest: dest) -> block {
+fn trans_alt_inner(scope_cx: block,
+                   discr_expr: @ast::expr,
+                   arms: ~[ast::arm],
+                   dest: Dest) -> block {
     let _icx = scope_cx.insn_ctxt("alt::trans_alt_inner");
-    let bcx = scope_cx, tcx = bcx.tcx();
-    let mut bodies = ~[], matches = ~[];
+    let mut bcx = scope_cx;
+    let tcx = bcx.tcx();
 
-    let {bcx, val, _} = trans_temp_expr(bcx, expr);
-    if bcx.unreachable { return bcx; }
+    let discr_datum = unpack_datum!(bcx, {
+        expr::trans_to_datum(bcx, discr_expr)
+    });
+    if bcx.unreachable {
+        return bcx;
+    }
 
+    let mut bodies = ~[], matches = ~[];
     for vec::each(arms) |a| {
         let body = scope_block(bcx, a.body.info(), ~"case_body");
         let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]);
@@ -857,30 +811,22 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm],
         }
     }
 
-    fn mk_fail(bcx: block, sp: span, msg: ~str,
-                   done: @mut Option<BasicBlockRef>) -> BasicBlockRef {
-            match *done { Some(bb) => return bb, _ => () }
-            let fail_cx = sub_block(bcx, ~"case_fallthrough");
-            trans_fail(fail_cx, Some(sp), msg);
-            *done = Some(fail_cx.llbb);
-            return fail_cx.llbb;
-    }
-    let t = node_id_type(bcx, expr.id);
-    let mk_fail = { let fail_cx = @mut None;
-          // special case for uninhabited type
-          if ty::type_is_empty(tcx, t) {
-                  Some(|| mk_fail(scope_cx, expr.span,
+    let t = node_id_type(bcx, discr_expr.id);
+    let chk = {
+        if ty::type_is_empty(tcx, t) {
+            // Special case for empty types
+            let fail_cx = @mut None;
+            Some(|| mk_fail(scope_cx, discr_expr.span,
                             ~"scrutinizing value that can't exist", fail_cx))
-          }
-          else {
-              None
-          }
+        } else {
+            None
+        }
     };
     let mut exit_map = ~[];
-    let spilled = spill_if_immediate(bcx, val, t);
-    compile_submatch(bcx, matches, ~[spilled], mk_fail, exit_map);
+    let lldiscr = discr_datum.to_ref_llval(bcx);
+    compile_submatch(bcx, matches, ~[lldiscr], chk, exit_map);
 
-    let mut arm_cxs = ~[], arm_dests = ~[], i = 0u;
+    let mut arm_cxs = ~[], i = 0u;
     for vec::each(arms) |a| {
         let body_cx = bodies[i];
         let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]);
@@ -888,9 +834,8 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm],
             None => {}
             Some(phi_bindings) => {
                 let body_cx = make_pattern_bindings(body_cx, phi_bindings);
-                let arm_dest = dup_for_join(dest);
-                vec::push(arm_dests, arm_dest);
-                let mut arm_cx = trans_block(body_cx, a.body, arm_dest);
+                let mut arm_cx =
+                    controlflow::trans_block(body_cx, a.body, dest);
                 arm_cx = trans_block_cleanups(arm_cx,
                                               block_cleanups(body_cx));
                 vec::push(arm_cxs, arm_cx);
@@ -898,7 +843,16 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm],
         }
         i += 1u;
     }
-    join_returns(scope_cx, arm_cxs, arm_dests, dest)
+    return controlflow::join_blocks(scope_cx, arm_cxs);
+
+    fn mk_fail(bcx: block, sp: span, msg: ~str,
+               done: @mut Option<BasicBlockRef>) -> BasicBlockRef {
+        match *done { Some(bb) => return bb, _ => () }
+        let fail_cx = sub_block(bcx, ~"case_fallthrough");
+        controlflow::trans_fail(fail_cx, Some(sp), msg);
+        *done = Some(fail_cx.llbb);
+        return fail_cx.llbb;
+    }
 }
 
 // Not alt-related, but similar to the pattern-munging code above
@@ -910,88 +864,66 @@ fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef,
 
     // Necessary since bind_irrefutable_pat is called outside trans_alt
     match pat.node {
-      ast::pat_ident(_, _,inner) => {
-        if pat_is_variant(bcx.tcx().def_map, pat) { return bcx; }
-        if make_copy {
-            let ty = node_id_type(bcx, pat.id);
-            let llty = type_of::type_of(ccx, ty);
-            let alloc = alloca(bcx, llty);
-            bcx = copy_val(bcx, INIT, alloc,
-                                  load_if_immediate(bcx, val, ty), ty);
-            bcx.fcx.lllocals.insert(pat.id, local_mem(alloc));
-            add_clean(bcx, alloc, ty);
-        } else { bcx.fcx.lllocals.insert(pat.id, local_mem(val)); }
-        match inner {
-          Some(pat) => { bcx = bind_irrefutable_pat(bcx, pat, val, true); }
-          _ => ()
-        }
-      }
-      ast::pat_enum(_, sub) => {
-        let vdefs = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat.id));
-        let args = extract_variant_args(bcx, pat.id, vdefs, val);
-        let mut i = 0;
-        do option::iter(sub) |sub| { for vec::each(args.vals) |argval| {
-            bcx = bind_irrefutable_pat(bcx, sub[i], argval, make_copy);
-            i += 1;
-        }}
-      }
-      ast::pat_rec(fields, _) => {
-        let rec_fields = ty::get_fields(node_id_type(bcx, pat.id));
-        for vec::each(fields) |f| {
-            let ix = option::get(ty::field_idx(f.ident, rec_fields));
-            let fldptr = GEPi(bcx, val, [0u, ix]);
-            bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
-        }
+        ast::pat_ident(_, _,inner) => {
+            if pat_is_variant(bcx.tcx().def_map, pat) {
+                return bcx;
+            }
+
+            if make_copy {
+                let binding_ty = node_id_type(bcx, pat.id);
+                let datum = Datum {val: val, ty: binding_ty,
+                                   mode: ByRef, source: FromRvalue};
+                let scratch = scratch_datum(bcx, binding_ty, false);
+                datum.copy_to_datum(bcx, INIT, scratch);
+                bcx.fcx.lllocals.insert(pat.id, local_mem(scratch.val));
+                add_clean(bcx, scratch.val, binding_ty);
+            } else {
+                bcx.fcx.lllocals.insert(pat.id, local_mem(val));
+            }
+
+            for inner.each |inner_pat| {
+                bcx = bind_irrefutable_pat(bcx, inner_pat, val, true);
+            }
       }
-      ast::pat_struct(_, fields, _) => {
-        // Grab the class data that we care about.
-        let class_fields, class_id;
-        match ty::get(node_id_type(bcx, pat.id)).struct {
-            ty::ty_class(cid, _) => {
-                class_id = cid;
-                class_fields = ty::lookup_class_fields(ccx.tcx, class_id);
+        ast::pat_enum(_, sub_pats) => {
+            let pat_def = ccx.tcx.def_map.get(pat.id);
+            let vdefs = ast_util::variant_def_ids(pat_def);
+            let args = extract_variant_args(bcx, pat.id, vdefs, val);
+            for sub_pats.each |sub_pat| {
+                for vec::eachi(args.vals) |i, argval| {
+                    bcx = bind_irrefutable_pat(bcx, sub_pat[i],
+                                               argval, make_copy);
+                }
             }
-            _ => {
-                ccx.tcx.sess.span_bug(pat.span, ~"struct pattern didn't \
-                                                  resolve to a struct");
+        }
+        ast::pat_rec(fields, _) | ast::pat_struct(_, fields, _) => {
+            let tcx = bcx.tcx();
+            let pat_ty = node_id_type(bcx, pat.id);
+            do expr::with_field_tys(tcx, pat_ty) |_has_dtor, field_tys| {
+                for vec::each(fields) |f| {
+                    let ix = ty::field_idx_strict(tcx, f.ident, field_tys);
+                    let fldptr = GEPi(bcx, val, struct_field(ix));
+                    bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, make_copy);
+                }
             }
         }
-
-        // Index the class fields.
-        let field_map = std::map::uint_hash();
-        for class_fields.eachi |i, class_field| {
-            field_map.insert(class_field.ident, i);
+        ast::pat_tup(elems) => {
+            for vec::eachi(elems) |i, elem| {
+                let fldptr = GEPi(bcx, val, [0u, i]);
+                bcx = bind_irrefutable_pat(bcx, elem, fldptr, make_copy);
+            }
         }
-
-        // Fetch each field.
-        for fields.each |supplied_field| {
-            let index = field_map.get(supplied_field.ident);
-            let fldptr = base::get_struct_field(bcx, val, class_id, index);
-            bcx = bind_irrefutable_pat(bcx, supplied_field.pat, fldptr,
-                                       make_copy);
+        ast::pat_box(inner) => {
+            let llbox = Load(bcx, val);
+            let unboxed = GEPi(bcx, llbox, [0u, abi::box_field_body]);
+            bcx = bind_irrefutable_pat(bcx, inner, unboxed, true);
         }
-      }
-      ast::pat_tup(elems) => {
-        let mut i = 0u;
-        for vec::each(elems) |elem| {
-            let fldptr = GEPi(bcx, val, [0u, i]);
-            bcx = bind_irrefutable_pat(bcx, elem, fldptr, make_copy);
-            i += 1u;
+        ast::pat_uniq(inner) => {
+            let llbox = Load(bcx, val);
+            let unboxed = GEPi(bcx, llbox, [0u, abi::box_field_body]);
+            bcx = bind_irrefutable_pat(bcx, inner, unboxed, true);
         }
-      }
-      ast::pat_box(inner) => {
-        let llbox = Load(bcx, val);
-        let unboxed =
-            GEPi(bcx, llbox, [0u, abi::box_field_body]);
-        bcx = bind_irrefutable_pat(bcx, inner, unboxed, true);
-      }
-      ast::pat_uniq(inner) => {
-        let llbox = Load(bcx, val);
-        let unboxed =
-            GEPi(bcx, llbox, [0u, abi::box_field_body]);
-        bcx = bind_irrefutable_pat(bcx, inner, unboxed, true);
-      }
-      ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => ()
+        ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => ()
     }
     return bcx;
 }
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 88fc4fb3ff1..b74d89dc465 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -42,61 +42,18 @@ use metadata::common::link_meta;
 use util::ppaux;
 use util::ppaux::{ty_to_str, ty_to_short_str};
 use syntax::diagnostic::expect;
+use util::common::indenter;
 
 use build::*;
 use shape::*;
 use type_of::*;
 use common::*;
-use common::result;
 use syntax::ast_map::{path, path_mod, path_name};
 use syntax::parse::token::special_idents;
 
 use std::smallintmap;
 use option::{is_none, is_some};
 
-// Destinations
-
-// These are passed around by the code generating functions to track the
-// destination of a computation's value.
-
-enum dest {
-    by_val(@mut ValueRef),
-    save_in(ValueRef),
-    ignore,
-}
-
-impl dest : cmp::Eq {
-    pure fn eq(&&other: dest) -> bool {
-        match (self, other) {
-            (by_val(e0a), by_val(e0b)) => e0a == e0b,
-            (save_in(e0a), save_in(e0b)) => e0a == e0b,
-            (ignore, ignore) => true,
-            (by_val(*), _) => false,
-            (save_in(*), _) => false,
-            (ignore, _) => false,
-        }
-    }
-}
-
-fn dest_str(ccx: @crate_ctxt, d: dest) -> ~str {
-    match d {
-      by_val(v) => fmt!("by_val(%s)", val_str(ccx.tn, *v)),
-      save_in(v) => fmt!("save_in(%s)", val_str(ccx.tn, v)),
-      ignore => ~"ignore"
-    }
-}
-
-fn empty_dest_cell() -> @mut ValueRef {
-    return @mut llvm::LLVMGetUndef(T_nil());
-}
-
-fn dup_for_join(dest: dest) -> dest {
-    match dest {
-      by_val(_) => by_val(empty_dest_cell()),
-      _ => dest
-    }
-}
-
 struct icx_popper {
     let ccx: @crate_ctxt;
     new(ccx: @crate_ctxt) { self.ccx = ccx; }
@@ -133,54 +90,6 @@ impl fn_ctxt: get_insn_ctxt {
     }
 }
 
-fn join_returns(parent_cx: block, in_cxs: ~[block],
-                in_ds: ~[dest], out_dest: dest) -> block {
-    let out = sub_block(parent_cx, ~"join");
-    let mut reachable = false, i = 0u, phi = None;
-    for vec::each(in_cxs) |cx| {
-        if !cx.unreachable {
-            Br(cx, out.llbb);
-            reachable = true;
-            match in_ds[i] {
-              by_val(cell) => {
-                if option::is_none(phi) {
-                    phi = Some(EmptyPhi(out, val_ty(*cell)));
-                }
-                AddIncomingToPhi(option::get(phi), *cell, cx.llbb);
-              }
-              _ => ()
-            }
-        }
-        i += 1u;
-    }
-    if !reachable {
-        Unreachable(out);
-    } else {
-        match out_dest {
-          by_val(cell) => *cell = option::get(phi),
-          _ => ()
-        }
-    }
-    return out;
-}
-
-// Used to put an immediate value in a dest.
-fn store_in_dest(bcx: block, val: ValueRef, dest: dest) -> block {
-    match dest {
-      ignore => (),
-      by_val(cell) => *cell = val,
-      save_in(addr) => Store(bcx, val, addr)
-    }
-    return bcx;
-}
-
-fn get_dest_addr(dest: dest) -> ValueRef {
-    match dest {
-       save_in(a) => a,
-       _ => fail ~"get_dest_addr: not a save_in"
-    }
-}
-
 fn log_fn_time(ccx: @crate_ctxt, name: ~str, start: time::Timespec,
                end: time::Timespec) {
     let elapsed = 1000 * ((end.sec - start.sec) as int) +
@@ -188,7 +97,6 @@ fn log_fn_time(ccx: @crate_ctxt, name: ~str, start: time::Timespec,
     vec::push(*ccx.stats.fn_times, {ident: name, time: elapsed});
 }
 
-
 fn decl_fn(llmod: ModuleRef, name: ~str, cc: lib::llvm::CallConv,
            llty: TypeRef) -> ValueRef {
     let llfn: ValueRef = str::as_c_str(name, |buf| {
@@ -202,7 +110,6 @@ fn decl_cdecl_fn(llmod: ModuleRef, name: ~str, llty: TypeRef) -> ValueRef {
     return decl_fn(llmod, name, lib::llvm::CCallConv, llty);
 }
 
-
 // Only use this if you are going to actually define the function. It's
 // not valid to simply declare a function as internal.
 fn decl_internal_cdecl_fn(llmod: ModuleRef, name: ~str, llty: TypeRef) ->
@@ -255,17 +162,6 @@ fn trans_foreign_call(cx: block, externs: hashmap<~str, ValueRef>,
     return Call(cx, llforeign, call_args);
 }
 
-fn trans_free(cx: block, v: ValueRef) -> block {
-    let _icx = cx.insn_ctxt("trans_free");
-    trans_rtcall(cx, ~"free", ~[PointerCast(cx, v, T_ptr(T_i8()))], ignore)
-}
-
-fn trans_unique_free(cx: block, v: ValueRef) -> block {
-    let _icx = cx.insn_ctxt("trans_unique_free");
-    trans_rtcall(cx, ~"exchange_free", ~[PointerCast(cx, v, T_ptr(T_i8()))],
-                 ignore)
-}
-
 fn umax(cx: block, a: ValueRef, b: ValueRef) -> ValueRef {
     let _icx = cx.insn_ctxt("umax");
     let cond = ICmp(cx, lib::llvm::IntULT, a, b);
@@ -278,67 +174,6 @@ fn umin(cx: block, a: ValueRef, b: ValueRef) -> ValueRef {
     return Select(cx, cond, a, b);
 }
 
-fn alloca(cx: block, t: TypeRef) -> ValueRef {
-    alloca_maybe_zeroed(cx, t, false)
-}
-
-fn alloca_zeroed(cx: block, t: TypeRef) -> ValueRef {
-    alloca_maybe_zeroed(cx, t, true)
-}
-
-fn alloca_maybe_zeroed(cx: block, t: TypeRef, zero: bool) -> ValueRef {
-    let _icx = cx.insn_ctxt("alloca");
-    if cx.unreachable { return llvm::LLVMGetUndef(t); }
-    let initcx = raw_block(cx.fcx, false, cx.fcx.llstaticallocas);
-    let p = Alloca(initcx, t);
-    if zero { memzero(initcx, p, t); }
-    return p;
-}
-
-fn zero_mem(cx: block, llptr: ValueRef, t: ty::t) -> block {
-    let _icx = cx.insn_ctxt("zero_mem");
-    let bcx = cx;
-    let ccx = cx.ccx();
-    let llty = type_of(ccx, t);
-    memzero(bcx, llptr, llty);
-    return bcx;
-}
-
-// Always use this function instead of storing a zero constant to the memory
-// in question. If you store a zero constant, LLVM will drown in vreg
-// allocation for large data structures, and the generated code will be
-// awful. (A telltale sign of this is large quantities of
-// `mov [byte ptr foo],0` in the generated code.)
-fn memzero(cx: block, llptr: ValueRef, llty: TypeRef) {
-    let _icx = cx.insn_ctxt("memzero");
-    let ccx = cx.ccx();
-
-    let intrinsic_key;
-    match ccx.sess.targ_cfg.arch {
-        session::arch_x86 | session::arch_arm => {
-            intrinsic_key = ~"llvm.memset.p0i8.i32";
-        }
-        session::arch_x86_64 => {
-            intrinsic_key = ~"llvm.memset.p0i8.i64";
-        }
-    }
-
-    let llintrinsicfn = ccx.intrinsics.get(intrinsic_key);
-    let llptr = PointerCast(cx, llptr, T_ptr(T_i8()));
-    let llzeroval = C_u8(0);
-    let size = IntCast(cx, llsize_of(ccx, llty), ccx.int_type);
-    let align = C_i32(1i32);
-    let volatile = C_bool(false);
-    Call(cx, llintrinsicfn, ~[llptr, llzeroval, size, align, volatile]);
-}
-
-fn arrayalloca(cx: block, t: TypeRef, v: ValueRef) -> ValueRef {
-    let _icx = cx.insn_ctxt("arrayalloca");
-    if cx.unreachable { return llvm::LLVMGetUndef(t); }
-    return ArrayAlloca(
-        raw_block(cx.fcx, false, cx.fcx.llstaticallocas), t, v);
-}
-
 // Given a pointer p, returns a pointer sz(p) (i.e., inc'd by sz bytes).
 // The type of the returned pointer is always i8*.  If you care about the
 // return type, use bump_ptr().
@@ -396,7 +231,7 @@ fn opaque_box_body(bcx: block,
 // malloc_raw_dyn: allocates a box to contain a given type, but with a
 // potentially dynamic size.
 fn malloc_raw_dyn(bcx: block, t: ty::t, heap: heap,
-                  size: ValueRef) -> result {
+                  size: ValueRef) -> Result {
     let _icx = bcx.insn_ctxt("malloc_raw");
     let ccx = bcx.ccx();
 
@@ -411,20 +246,36 @@ fn malloc_raw_dyn(bcx: block, t: ty::t, heap: heap,
 
     // Get the tydesc for the body:
     let static_ti = get_tydesc(ccx, t);
-    lazily_emit_all_tydesc_glue(ccx, static_ti);
+    glue::lazily_emit_all_tydesc_glue(ccx, static_ti);
 
     // Allocate space:
     let tydesc = PointerCast(bcx, static_ti.tydesc, T_ptr(T_i8()));
     let rval = alloca_zeroed(bcx, T_ptr(T_i8()));
-    let bcx = trans_rtcall(bcx, rtcall, ~[tydesc, size], save_in(rval));
-    let retval = {bcx: bcx, val: PointerCast(bcx, Load(bcx, rval), llty)};
-    return retval;
+    let bcx = callee::trans_rtcall(bcx, rtcall, ~[tydesc, size],
+                                   expr::SaveIn(rval));
+    return rslt(bcx, PointerCast(bcx, Load(bcx, rval), llty));
+}
+
+/**
+* Get the type of a box in the default address space.
+*
+* Shared box pointers live in address space 1 so the GC strategy can find
+* them. Before taking a pointer to the inside of a box it should be cast into
+* address space 0. Otherwise the resulting (non-box) pointer will be in the
+* wrong address space and thus be the wrong type.
+*/
+fn non_gc_box_cast(bcx: block, val: ValueRef) -> ValueRef {
+    debug!("non_gc_box_cast");
+    add_comment(bcx, ~"non_gc_box_cast");
+    assert(llvm::LLVMGetPointerAddressSpace(val_ty(val)) == gc_box_addrspace);
+    let non_gc_t = T_ptr(llvm::LLVMGetElementType(val_ty(val)));
+    PointerCast(bcx, val, non_gc_t)
 }
 
 // malloc_raw: expects an unboxed type and returns a pointer to
 // enough space for a box of that type.  This includes a rust_opaque_box
 // header.
-fn malloc_raw(bcx: block, t: ty::t, heap: heap) -> result {
+fn malloc_raw(bcx: block, t: ty::t, heap: heap) -> Result {
     malloc_raw_dyn(bcx, t, heap, llsize_of(bcx.ccx(), type_of(bcx.ccx(), t)))
 }
 
@@ -433,7 +284,7 @@ fn malloc_raw(bcx: block, t: ty::t, heap: heap) -> result {
 fn malloc_general_dyn(bcx: block, t: ty::t, heap: heap, size: ValueRef)
     -> {bcx: block, box: ValueRef, body: ValueRef} {
     let _icx = bcx.insn_ctxt("malloc_general");
-    let {bcx: bcx, val: llbox} = malloc_raw_dyn(bcx, t, heap, size);
+    let Result {bcx: bcx, val: llbox} = malloc_raw_dyn(bcx, t, heap, size);
     let non_gc_box = non_gc_box_cast(bcx, llbox);
     let body = GEPi(bcx, non_gc_box, [0u, abi::box_field_body]);
     return {bcx: bcx, box: llbox, body: body};
@@ -464,7 +315,7 @@ fn get_tydesc(ccx: @crate_ctxt, t: ty::t) -> @tydesc_info {
       Some(inf) => inf,
       _ => {
         ccx.stats.n_static_tydescs += 1u;
-        let inf = declare_tydesc(ccx, t);
+        let inf = glue::declare_tydesc(ccx, t);
         ccx.tydescs.insert(t, inf);
         inf
       }
@@ -527,391 +378,6 @@ fn note_unique_llvm_symbol(ccx: @crate_ctxt, sym: ~str) {
     ccx.all_llvm_symbols.insert(sym, ());
 }
 
-// Chooses the addrspace for newly declared types.
-fn declare_tydesc_addrspace(ccx: @crate_ctxt, t: ty::t) -> addrspace {
-    if !ty::type_needs_drop(ccx.tcx, t) {
-        return default_addrspace;
-    } else if ty::type_is_immediate(t) {
-        // For immediate types, we don't actually need an addrspace, because
-        // e.g. boxed types include pointers to their contents which are
-        // already correctly tagged with addrspaces.
-        return default_addrspace;
-    } else {
-        return ccx.next_addrspace();
-    }
-}
-
-// Generates the declaration for (but doesn't emit) a type descriptor.
-fn declare_tydesc(ccx: @crate_ctxt, t: ty::t) -> @tydesc_info {
-    let _icx = ccx.insn_ctxt("declare_tydesc");
-    // If emit_tydescs already ran, then we shouldn't be creating any new
-    // tydescs.
-    assert !ccx.finished_tydescs;
-
-    let llty = type_of(ccx, t);
-
-    if ccx.sess.count_type_sizes() {
-        io::println(fmt!("%u\t%s",
-                         llsize_of_real(ccx, llty),
-                         ty_to_str(ccx.tcx, t)));
-    }
-
-    let llsize = llsize_of(ccx, llty);
-    let llalign = llalign_of(ccx, llty);
-    let addrspace = declare_tydesc_addrspace(ccx, t);
-    //XXX this triggers duplicate LLVM symbols
-    let name = if false /*ccx.sess.opts.debuginfo*/ {
-        mangle_internal_name_by_type_only(ccx, t, ~"tydesc")
-    } else { mangle_internal_name_by_seq(ccx, ~"tydesc") };
-    note_unique_llvm_symbol(ccx, name);
-    log(debug, fmt!("+++ declare_tydesc %s %s", ty_to_str(ccx.tcx, t), name));
-    let gvar = str::as_c_str(name, |buf| {
-        llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type, buf)
-    });
-    let inf =
-        @{ty: t,
-          tydesc: gvar,
-          size: llsize,
-          align: llalign,
-          addrspace: addrspace,
-          mut take_glue: None,
-          mut drop_glue: None,
-          mut free_glue: None,
-          mut visit_glue: None};
-    log(debug, ~"--- declare_tydesc " + ppaux::ty_to_str(ccx.tcx, t));
-    return inf;
-}
-
-type glue_helper = fn@(block, ValueRef, ty::t);
-
-fn declare_generic_glue(ccx: @crate_ctxt, t: ty::t, llfnty: TypeRef,
-                        name: ~str) -> ValueRef {
-    let _icx = ccx.insn_ctxt("declare_generic_glue");
-    let name = name;
-    let mut fn_nm;
-    //XXX this triggers duplicate LLVM symbols
-    if false /*ccx.sess.opts.debuginfo*/ {
-        fn_nm = mangle_internal_name_by_type_only(ccx, t, (~"glue_" + name));
-    } else {
-        fn_nm = mangle_internal_name_by_seq(ccx, (~"glue_" + name));
-    }
-    note_unique_llvm_symbol(ccx, fn_nm);
-    let llfn = decl_cdecl_fn(ccx.llmod, fn_nm, llfnty);
-    set_glue_inlining(llfn, t);
-    return llfn;
-}
-
-fn make_generic_glue_inner(ccx: @crate_ctxt, t: ty::t,
-                           llfn: ValueRef, helper: glue_helper) -> ValueRef {
-    let _icx = ccx.insn_ctxt("make_generic_glue_inner");
-    let fcx = new_fn_ctxt(ccx, ~[], llfn, None);
-    lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage);
-    ccx.stats.n_glues_created += 1u;
-    // All glue functions take values passed *by alias*; this is a
-    // requirement since in many contexts glue is invoked indirectly and
-    // the caller has no idea if it's dealing with something that can be
-    // passed by value.
-    //
-    // llfn is expected be declared to take a parameter of the appropriate
-    // type, so we don't need to explicitly cast the function parameter.
-
-    let bcx = top_scope_block(fcx, None);
-    let lltop = bcx.llbb;
-    let llrawptr0 = llvm::LLVMGetParam(llfn, 3u as c_uint);
-    helper(bcx, llrawptr0, t);
-    finish_fn(fcx, lltop);
-    return llfn;
-}
-
-fn make_generic_glue(ccx: @crate_ctxt, t: ty::t, llfn: ValueRef,
-                     helper: glue_helper, name: ~str)
-    -> ValueRef {
-    let _icx = ccx.insn_ctxt("make_generic_glue");
-    if !ccx.sess.trans_stats() {
-        return make_generic_glue_inner(ccx, t, llfn, helper);
-    }
-
-    let start = time::get_time();
-    let llval = make_generic_glue_inner(ccx, t, llfn, helper);
-    let end = time::get_time();
-    log_fn_time(ccx, ~"glue " + name + ~" " + ty_to_short_str(ccx.tcx, t),
-                start, end);
-    return llval;
-}
-
-fn emit_tydescs(ccx: @crate_ctxt) {
-    let _icx = ccx.insn_ctxt("emit_tydescs");
-    // As of this point, allow no more tydescs to be created.
-    ccx.finished_tydescs = true;
-    for ccx.tydescs.each |key, val| {
-        let glue_fn_ty = T_ptr(T_generic_glue_fn(ccx));
-        let ti = val;
-
-        // Each of the glue functions needs to be cast to a generic type
-        // before being put into the tydesc because we only have a singleton
-        // tydesc type. Then we'll recast each function to its real type when
-        // calling it.
-        let take_glue =
-            match copy ti.take_glue {
-              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
-              Some(v) => {
-                ccx.stats.n_real_glues += 1u;
-                llvm::LLVMConstPointerCast(v, glue_fn_ty)
-              }
-            };
-        let drop_glue =
-            match copy ti.drop_glue {
-              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
-              Some(v) => {
-                ccx.stats.n_real_glues += 1u;
-                llvm::LLVMConstPointerCast(v, glue_fn_ty)
-              }
-            };
-        let free_glue =
-            match copy ti.free_glue {
-              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
-              Some(v) => {
-                ccx.stats.n_real_glues += 1u;
-                llvm::LLVMConstPointerCast(v, glue_fn_ty)
-              }
-            };
-        let visit_glue =
-            match copy ti.visit_glue {
-              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
-              Some(v) => {
-                ccx.stats.n_real_glues += 1u;
-                llvm::LLVMConstPointerCast(v, glue_fn_ty)
-              }
-            };
-
-        let shape = shape_of(ccx, key);
-        let shape_tables =
-            llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables,
-                                       T_ptr(T_i8()));
-
-        let tydesc =
-            C_named_struct(ccx.tydesc_type,
-                           ~[ti.size, // size
-                             ti.align, // align
-                             take_glue, // take_glue
-                             drop_glue, // drop_glue
-                             free_glue, // free_glue
-                             visit_glue, // visit_glue
-                             C_shape(ccx, shape), // shape
-                             shape_tables]); // shape_tables
-
-        let gvar = ti.tydesc;
-        llvm::LLVMSetInitializer(gvar, tydesc);
-        llvm::LLVMSetGlobalConstant(gvar, True);
-        lib::llvm::SetLinkage(gvar, lib::llvm::InternalLinkage);
-
-        // Index tydesc by addrspace.
-        if ti.addrspace > gc_box_addrspace {
-            let llty = T_ptr(ccx.tydesc_type);
-            let addrspace_name = #fmt("_gc_addrspace_metadata_%u",
-                                      ti.addrspace as uint);
-            let addrspace_gvar = str::as_c_str(addrspace_name, |buf| {
-                llvm::LLVMAddGlobal(ccx.llmod, llty, buf)
-            });
-            lib::llvm::SetLinkage(addrspace_gvar, lib::llvm::InternalLinkage);
-            llvm::LLVMSetInitializer(addrspace_gvar, gvar);
-        }
-    };
-}
-
-fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) {
-    let _icx = bcx.insn_ctxt("make_take_glue");
-    // NB: v is a *pointer* to type t here, not a direct value.
-    let bcx = match ty::get(t).struct {
-      ty::ty_box(_) | ty::ty_opaque_box |
-      ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => {
-        incr_refcnt_of_boxed(bcx, Load(bcx, v)); bcx
-      }
-      ty::ty_uniq(_) => {
-        let {bcx, val} = uniq::duplicate(bcx, Load(bcx, v), t);
-        Store(bcx, val, v);
-        bcx
-      }
-      ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => {
-        let {bcx, val} = tvec::duplicate_uniq(bcx, Load(bcx, v), t);
-        Store(bcx, val, v);
-        bcx
-      }
-      ty::ty_evec(_, ty::vstore_slice(_))
-      | ty::ty_estr(ty::vstore_slice(_)) => {
-        bcx
-      }
-      ty::ty_fn(_) => {
-        closure::make_fn_glue(bcx, v, t, take_ty)
-      }
-      ty::ty_trait(_, _, _) => {
-        let llbox = Load(bcx, GEPi(bcx, v, [0u, 1u]));
-        incr_refcnt_of_boxed(bcx, llbox);
-        bcx
-      }
-      ty::ty_opaque_closure_ptr(ck) => {
-        closure::make_opaque_cbox_take_glue(bcx, ck, v)
-      }
-      _ if ty::type_is_structural(t) => {
-        iter_structural_ty(bcx, v, t, take_ty)
-      }
-      _ => bcx
-    };
-
-    build_return(bcx);
-}
-
-fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) {
-    let _icx = cx.insn_ctxt("incr_refcnt_of_boxed");
-    let ccx = cx.ccx();
-    maybe_validate_box(cx, box_ptr);
-    let rc_ptr = GEPi(cx, box_ptr, [0u, abi::box_field_refcnt]);
-    let rc = Load(cx, rc_ptr);
-    let rc = Add(cx, rc, C_int(ccx, 1));
-    Store(cx, rc, rc_ptr);
-}
-
-fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) {
-    let _icx = bcx.insn_ctxt("make_visit_glue");
-    let mut bcx = bcx;
-    let ty_visitor_name = special_idents::ty_visitor;
-    assert bcx.ccx().tcx.intrinsic_defs.contains_key(ty_visitor_name);
-    let (trait_id, ty) = bcx.ccx().tcx.intrinsic_defs.get(ty_visitor_name);
-    let v = PointerCast(bcx, v, T_ptr(type_of::type_of(bcx.ccx(), ty)));
-    bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, trait_id);
-    build_return(bcx);
-}
-
-
-fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
-    // NB: v0 is an *alias* of type t here, not a direct value.
-    let _icx = bcx.insn_ctxt("make_free_glue");
-    let ccx = bcx.ccx();
-    let bcx = match ty::get(t).struct {
-      ty::ty_box(body_mt) => {
-        let v = Load(bcx, v);
-        let body = GEPi(bcx, v, [0u, abi::box_field_body]);
-        // Cast away the addrspace of the box pointer.
-        let body = PointerCast(bcx, body, T_ptr(type_of(ccx, body_mt.ty)));
-        let bcx = drop_ty(bcx, body, body_mt.ty);
-        trans_free(bcx, v)
-      }
-      ty::ty_opaque_box => {
-        let v = Load(bcx, v);
-        let td = Load(bcx, GEPi(bcx, v, [0u, abi::box_field_tydesc]));
-        let valptr = GEPi(bcx, v, [0u, abi::box_field_body]);
-        // Generate code that, dynamically, indexes into the
-        // tydesc and calls the drop glue that got set dynamically
-        call_tydesc_glue_full(bcx, valptr, td, abi::tydesc_field_drop_glue,
-                              None);
-        trans_free(bcx, v)
-      }
-      ty::ty_uniq(*) => {
-        uniq::make_free_glue(bcx, v, t)
-      }
-      ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) |
-      ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => {
-        make_free_glue(bcx, v,
-                       tvec::expand_boxed_vec_ty(bcx.tcx(), t));
-        return;
-      }
-      ty::ty_fn(_) => {
-        closure::make_fn_glue(bcx, v, t, free_ty)
-      }
-      ty::ty_opaque_closure_ptr(ck) => {
-        closure::make_opaque_cbox_free_glue(bcx, ck, v)
-      }
-      ty::ty_class(did, ref substs) => {
-        // Call the dtor if there is one
-        do option::map_default(ty::ty_dtor(bcx.tcx(), did), bcx) |dt_id| {
-            trans_class_drop(bcx, v, dt_id, did, substs)
-        }
-      }
-      _ => bcx
-    };
-    build_return(bcx);
-}
-
-fn trans_class_drop(bcx: block, v0: ValueRef, dtor_did: ast::def_id,
-                    class_did: ast::def_id,
-                    substs: &ty::substs) -> block {
-  let drop_flag = GEPi(bcx, v0, [0u, 0u]);
-    do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| {
-    let mut bcx = cx;
-      // We have to cast v0
-     let classptr = GEPi(bcx, v0, [0u, 1u]);
-     // Find and call the actual destructor
-     let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did, class_did, substs.tps);
-     // The second argument is the "self" argument for drop
-     let params = lib::llvm::fn_ty_param_tys
-         (llvm::LLVMGetElementType
-          (llvm::LLVMTypeOf(dtor_addr)));
-     // Class dtors have no explicit args, so the params should just consist
-     // of the output pointer and the environment (self)
-     assert(params.len() == 2u);
-     let self_arg = PointerCast(bcx, v0, params[1u]);
-     let args = ~[bcx.fcx.llretptr, self_arg];
-     Call(bcx, dtor_addr, args);
-     // Drop the fields
-     for vec::eachi(ty::class_items_as_mutable_fields(bcx.tcx(), class_did,
-                                                      substs))
-         |i, fld| {
-        let llfld_a = GEPi(bcx, classptr, [0u, i]);
-        bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
-     }
-     Store(bcx, C_u8(0u), drop_flag);
-     bcx
-  }
-}
-
-
-fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
-    // NB: v0 is an *alias* of type t here, not a direct value.
-    let _icx = bcx.insn_ctxt("make_drop_glue");
-    let ccx = bcx.ccx();
-    let bcx = match ty::get(t).struct {
-      ty::ty_box(_) | ty::ty_opaque_box |
-      ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) => {
-        decr_refcnt_maybe_free(bcx, Load(bcx, v0), t)
-      }
-      ty::ty_uniq(_) |
-      ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => {
-        free_ty(bcx, v0, t)
-      }
-      ty::ty_unboxed_vec(_) => {
-        tvec::make_drop_glue_unboxed(bcx, v0, t)
-      }
-      ty::ty_class(did, ref substs) => {
-        let tcx = bcx.tcx();
-        match ty::ty_dtor(tcx, did) {
-          Some(dtor) => {
-            trans_class_drop(bcx, v0, dtor, did, substs)
-          }
-          None => {
-            // No dtor? Just the default case
-            iter_structural_ty(bcx, v0, t, drop_ty)
-          }
-        }
-      }
-      ty::ty_fn(_) => {
-        closure::make_fn_glue(bcx, v0, t, drop_ty)
-      }
-      ty::ty_trait(_, _, _) => {
-        let llbox = Load(bcx, GEPi(bcx, v0, [0u, 1u]));
-        decr_refcnt_maybe_free(bcx, llbox, ty::mk_opaque_box(ccx.tcx))
-      }
-      ty::ty_opaque_closure_ptr(ck) => {
-        closure::make_opaque_cbox_drop_glue(bcx, ck, v0)
-      }
-      _ => {
-        if ty::type_needs_drop(ccx.tcx, t) &&
-            ty::type_is_structural(t) {
-            iter_structural_ty(bcx, v0, t, drop_ty)
-        } else { bcx }
-      }
-    };
-    build_return(bcx);
-}
 
 fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id,
                 parent_id: ast::def_id, substs: ~[ty::t])
@@ -919,10 +385,10 @@ fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id,
     let _icx = ccx.insn_ctxt("trans_res_dtor");
     if (substs.len() > 0u) {
         let did = if did.crate != ast::local_crate {
-            maybe_instantiate_inline(ccx, did)
+            inline::maybe_instantiate_inline(ccx, did)
         } else { did };
         assert did.crate == ast::local_crate;
-        monomorphic_fn(ccx, did, substs, None, None).val
+        monomorphize::monomorphic_fn(ccx, did, substs, None, None).val
     } else if did.crate == ast::local_crate {
         get_item_val(ccx, did.node)
     } else {
@@ -936,31 +402,6 @@ fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id,
     }
 }
 
-fn maybe_validate_box(_cx: block, _box_ptr: ValueRef) {
-    // Uncomment this when debugging annoying use-after-free
-    // bugs.  But do not commit with this uncommented!  Big performance hit.
-
-    // let cx = _cx, box_ptr = _box_ptr;
-    // let ccx = cx.ccx();
-    // warn_not_to_commit(ccx, "validate_box() is uncommented");
-    // let raw_box_ptr = PointerCast(cx, box_ptr, T_ptr(T_i8()));
-    // Call(cx, ccx.upcalls.validate_box, ~[raw_box_ptr]);
-}
-
-fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t) -> block {
-    let _icx = bcx.insn_ctxt("decr_refcnt_maybe_free");
-    let ccx = bcx.ccx();
-    maybe_validate_box(bcx, box_ptr);
-
-    do with_cond(bcx, IsNotNull(bcx, box_ptr)) |bcx| {
-        let rc_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_refcnt]);
-        let rc = Sub(bcx, Load(bcx, rc_ptr), C_int(ccx, 1));
-        Store(bcx, rc, rc_ptr);
-        let zero_test = ICmp(bcx, lib::llvm::IntEQ, C_int(ccx, 0), rc);
-        with_cond(bcx, zero_test, |bcx| free_ty_immediate(bcx, box_ptr, t))
-    }
-}
-
 // Structural comparison: a rather involved form of glue.
 fn maybe_name_value(cx: @crate_ctxt, v: ValueRef, s: ~str) {
     if cx.sess.opts.save_temps {
@@ -972,27 +413,28 @@ fn maybe_name_value(cx: @crate_ctxt, v: ValueRef, s: ~str) {
 // Used only for creating scalar comparison glue.
 enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, }
 
-
 fn compare_scalar_types(cx: block, lhs: ValueRef, rhs: ValueRef,
-                        t: ty::t, op: ast::binop) -> result {
+                        t: ty::t, op: ast::binop) -> Result {
     let f = |a| compare_scalar_values(cx, lhs, rhs, a, op);
 
     match ty::get(t).struct {
-      ty::ty_nil => return rslt(cx, f(nil_type)),
-      ty::ty_bool | ty::ty_ptr(_) => return rslt(cx, f(unsigned_int)),
-      ty::ty_int(_) => return rslt(cx, f(signed_int)),
-      ty::ty_uint(_) => return rslt(cx, f(unsigned_int)),
-      ty::ty_float(_) => return rslt(cx, f(floating_point)),
-      ty::ty_type => {
-        return rslt(trans_fail(cx, None,
-                            ~"attempt to compare values of type type"),
-                 C_nil());
-      }
-      _ => {
-        // Should never get here, because t is scalar.
-        cx.sess().bug(~"non-scalar type passed to \
-                                 compare_scalar_types");
-      }
+        ty::ty_nil => rslt(cx, f(nil_type)),
+        ty::ty_bool | ty::ty_ptr(_) => rslt(cx, f(unsigned_int)),
+        ty::ty_int(_) => rslt(cx, f(signed_int)),
+        ty::ty_uint(_) => rslt(cx, f(unsigned_int)),
+        ty::ty_float(_) => rslt(cx, f(floating_point)),
+        ty::ty_type => {
+            rslt(
+                controlflow::trans_fail(
+                    cx, None,
+                    ~"attempt to compare values of type type"),
+                C_nil())
+        }
+        _ => {
+            // Should never get here, because t is scalar.
+            cx.sess().bug(~"non-scalar type passed to \
+                            compare_scalar_types")
+        }
     }
 }
 
@@ -1102,11 +544,13 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
     */
     let mut cx = cx;
     match ty::get(t).struct {
-      ty::ty_rec(fields) => {
-        for vec::eachi(fields) |i, fld| {
-            let llfld_a = GEPi(cx, av, [0u, i]);
-            cx = f(cx, llfld_a, fld.mt.ty);
-        }
+      ty::ty_rec(*) | ty::ty_class(*) => {
+          do expr::with_field_tys(cx.tcx(), t) |_has_dtor, field_tys| {
+              for vec::eachi(field_tys) |i, field_ty| {
+                  let llfld_a = GEPi(cx, av, struct_field(i));
+                  cx = f(cx, llfld_a, field_ty.mt.ty);
+              }
+          }
       }
       ty::ty_estr(ty::vstore_fixed(_)) |
       ty::ty_evec(_, ty::vstore_fixed(_)) => {
@@ -1156,505 +600,13 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
         }
         return next_cx;
       }
-      ty::ty_class(did, ref substs) => {
-          // Take the drop bit into account
-          let classptr = if is_some(ty::ty_dtor(cx.tcx(), did)) {
-                  GEPi(cx, av, [0u, 1u])
-              }
-          else { av };
-        for vec::eachi(ty::class_items_as_mutable_fields(cx.tcx(), did,
-                                                         substs))
-            |i, fld| {
-               let llfld_a = GEPi(cx, classptr, [0u, i]);
-               cx = f(cx, llfld_a, fld.mt.ty);
-           }
-      }
       _ => cx.sess().unimpl(~"type in iter_structural_ty")
     }
     return cx;
 }
 
-fn lazily_emit_all_tydesc_glue(ccx: @crate_ctxt,
-                               static_ti: @tydesc_info) {
-    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, static_ti);
-    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_drop_glue, static_ti);
-    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_free_glue, static_ti);
-    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_visit_glue, static_ti);
-}
-
-fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint,
-                           ti: @tydesc_info) {
-    let _icx = ccx.insn_ctxt("lazily_emit_tydesc_glue");
-    let llfnty = type_of_glue_fn(ccx, ti.ty);
-    if field == abi::tydesc_field_take_glue {
-        match ti.take_glue {
-          Some(_) => (),
-          None => {
-            debug!("+++ lazily_emit_tydesc_glue TAKE %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"take");
-            ti.take_glue = Some(glue_fn);
-            make_generic_glue(ccx, ti.ty, glue_fn, make_take_glue, ~"take");
-            debug!("--- lazily_emit_tydesc_glue TAKE %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-          }
-        }
-    } else if field == abi::tydesc_field_drop_glue {
-        match ti.drop_glue {
-          Some(_) => (),
-          None => {
-            debug!("+++ lazily_emit_tydesc_glue DROP %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"drop");
-            ti.drop_glue = Some(glue_fn);
-            make_generic_glue(ccx, ti.ty, glue_fn, make_drop_glue, ~"drop");
-            debug!("--- lazily_emit_tydesc_glue DROP %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-          }
-        }
-    } else if field == abi::tydesc_field_free_glue {
-        match ti.free_glue {
-          Some(_) => (),
-          None => {
-            debug!("+++ lazily_emit_tydesc_glue FREE %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"free");
-            ti.free_glue = Some(glue_fn);
-            make_generic_glue(ccx, ti.ty, glue_fn, make_free_glue, ~"free");
-            debug!("--- lazily_emit_tydesc_glue FREE %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-          }
-        }
-    } else if field == abi::tydesc_field_visit_glue {
-        match ti.visit_glue {
-          Some(_) => (),
-          None => {
-            debug!("+++ lazily_emit_tydesc_glue VISIT %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"visit");
-            ti.visit_glue = Some(glue_fn);
-            make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, ~"visit");
-            debug!("--- lazily_emit_tydesc_glue VISIT %s",
-                   ppaux::ty_to_str(ccx.tcx, ti.ty));
-          }
-        }
-    }
-}
-
-// See [Note-arg-mode]
-fn call_tydesc_glue_full(++bcx: block, v: ValueRef, tydesc: ValueRef,
-                         field: uint, static_ti: Option<@tydesc_info>) {
-    let _icx = bcx.insn_ctxt("call_tydesc_glue_full");
-    let ccx = bcx.ccx();
-    // NB: Don't short-circuit even if this block is unreachable because
-    // GC-based cleanup needs to the see that the roots are live.
-    let no_lpads =
-        ccx.sess.opts.debugging_opts & session::no_landing_pads != 0;
-    if bcx.unreachable && !no_lpads { return; }
-
-    let static_glue_fn = match static_ti {
-      None => None,
-      Some(sti) => {
-        lazily_emit_tydesc_glue(ccx, field, sti);
-        if field == abi::tydesc_field_take_glue {
-            sti.take_glue
-        } else if field == abi::tydesc_field_drop_glue {
-            sti.drop_glue
-        } else if field == abi::tydesc_field_free_glue {
-            sti.free_glue
-        } else if field == abi::tydesc_field_visit_glue {
-            sti.visit_glue
-        } else {
-            None
-        }
-      }
-    };
-
-    // When available, use static type info to give glue the right type.
-    let static_glue_fn = match static_ti {
-      None => None,
-      Some(sti) => {
-        match static_glue_fn {
-          None => None,
-          Some(sgf) => Some(
-              PointerCast(bcx, sgf, T_ptr(type_of_glue_fn(ccx, sti.ty))))
-        }
-      }
-    };
-
-    // When static type info is available, avoid casting parameter because the
-    // function already has the right type. Otherwise cast to generic pointer.
-    let llrawptr = if is_none(static_ti) || is_none(static_glue_fn) {
-        PointerCast(bcx, v, T_ptr(T_i8()))
-    } else {
-        v
-    };
-
-    let llfn = {
-        match static_glue_fn {
-          None => {
-            // Select out the glue function to call from the tydesc
-            let llfnptr = GEPi(bcx, tydesc, [0u, field]);
-            Load(bcx, llfnptr)
-          }
-          Some(sgf) => sgf
-        }
-    };
-
-    Call(bcx, llfn, ~[C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())),
-                      C_null(T_ptr(T_ptr(bcx.ccx().tydesc_type))), llrawptr]);
-}
-
-// See [Note-arg-mode]
-fn call_tydesc_glue(++cx: block, v: ValueRef, t: ty::t, field: uint)
-    -> block {
-    let _icx = cx.insn_ctxt("call_tydesc_glue");
-    let ti = get_tydesc(cx.ccx(), t);
-    call_tydesc_glue_full(cx, v, ti.tydesc, field, Some(ti));
-    return cx;
-}
-
-fn call_cmp_glue(bcx: block, lhs: ValueRef, rhs: ValueRef, t: ty::t,
-                 llop: ValueRef) -> ValueRef {
-    // We can't use call_tydesc_glue_full() and friends here because compare
-    // glue has a special signature.
-    let _icx = bcx.insn_ctxt("call_cmp_glue");
-
-    let lllhs = spill_if_immediate(bcx, lhs, t);
-    let llrhs = spill_if_immediate(bcx, rhs, t);
-
-    let llrawlhsptr = BitCast(bcx, lllhs, T_ptr(T_i8()));
-    let llrawrhsptr = BitCast(bcx, llrhs, T_ptr(T_i8()));
-    let lltydesc = get_tydesc_simple(bcx.ccx(), t);
-
-    let llfn = bcx.ccx().upcalls.cmp_type;
-
-    let llcmpresultptr = alloca(bcx, T_i1());
-    Call(bcx, llfn, ~[llcmpresultptr, lltydesc,
-                      llrawlhsptr, llrawrhsptr, llop]);
-    return Load(bcx, llcmpresultptr);
-}
-
-fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block {
-    // NB: v is an *alias* of type t here, not a direct value.
-    let _icx = cx.insn_ctxt("take_ty");
-    if ty::type_needs_drop(cx.tcx(), t) {
-        return call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue);
-    }
-    return cx;
-}
-
-fn drop_ty(cx: block, v: ValueRef, t: ty::t) -> block {
-    // NB: v is an *alias* of type t here, not a direct value.
-    let _icx = cx.insn_ctxt("drop_ty");
-    if ty::type_needs_drop(cx.tcx(), t) {
-        return call_tydesc_glue(cx, v, t, abi::tydesc_field_drop_glue);
-    }
-    return cx;
-}
-
-fn drop_ty_root(bcx: block, v: ValueRef, rooted: bool, t: ty::t) -> block {
-    if rooted {
-        // NB: v is a raw ptr to an addrspace'd ptr to the value.
-        let v = PointerCast(bcx, Load(bcx, v), T_ptr(type_of(bcx.ccx(), t)));
-        drop_ty(bcx, v, t)
-    } else {
-        drop_ty(bcx, v, t)
-    }
-}
-
-fn drop_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block {
-    let _icx = bcx.insn_ctxt("drop_ty_immediate");
-    match ty::get(t).struct {
-      ty::ty_uniq(_) |
-      ty::ty_evec(_, ty::vstore_uniq) |
-      ty::ty_estr(ty::vstore_uniq) => {
-        free_ty_immediate(bcx, v, t)
-      }
-      ty::ty_box(_) | ty::ty_opaque_box |
-      ty::ty_evec(_, ty::vstore_box) |
-      ty::ty_estr(ty::vstore_box) => {
-        decr_refcnt_maybe_free(bcx, v, t)
-      }
-      _ => bcx.tcx().sess.bug(~"drop_ty_immediate: non-box ty")
-    }
-}
-
-fn take_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> result {
-    let _icx = bcx.insn_ctxt("take_ty_immediate");
-    match ty::get(t).struct {
-      ty::ty_box(_) | ty::ty_opaque_box |
-      ty::ty_evec(_, ty::vstore_box) |
-      ty::ty_estr(ty::vstore_box) => {
-        incr_refcnt_of_boxed(bcx, v);
-        rslt(bcx, v)
-      }
-      ty::ty_uniq(_) => {
-        uniq::duplicate(bcx, v, t)
-      }
-      ty::ty_evec(_, ty::vstore_uniq) |
-      ty::ty_estr(ty::vstore_uniq) => {
-        tvec::duplicate_uniq(bcx, v, t)
-      }
-      _ => rslt(bcx, v)
-    }
-}
-
-fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block {
-    // NB: v is an *alias* of type t here, not a direct value.
-    let _icx = cx.insn_ctxt("free_ty");
-    if ty::type_needs_drop(cx.tcx(), t) {
-        return call_tydesc_glue(cx, v, t, abi::tydesc_field_free_glue);
-    }
-    return cx;
-}
-
-fn free_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block {
-    let _icx = bcx.insn_ctxt("free_ty_immediate");
-    match ty::get(t).struct {
-      ty::ty_uniq(_) |
-      ty::ty_evec(_, ty::vstore_uniq) |
-      ty::ty_estr(ty::vstore_uniq) |
-      ty::ty_box(_) | ty::ty_opaque_box |
-      ty::ty_evec(_, ty::vstore_box) |
-      ty::ty_estr(ty::vstore_box) |
-      ty::ty_opaque_closure_ptr(_) => {
-        let vp = alloca_zeroed(bcx, type_of(bcx.ccx(), t));
-        Store(bcx, v, vp);
-        free_ty(bcx, vp, t)
-      }
-      _ => bcx.tcx().sess.bug(~"free_ty_immediate: non-box ty")
-    }
-}
-
-fn call_memmove(cx: block, dst: ValueRef, src: ValueRef,
-                n_bytes: ValueRef) {
-    // FIXME (Related to #1645, I think?): Provide LLVM with better
-    // alignment information when the alignment is statically known (it must
-    // be nothing more than a constant int, or LLVM complains -- not even a
-    // constant element of a tydesc works).
-    let _icx = cx.insn_ctxt("call_memmove");
-    let ccx = cx.ccx();
-    let key = match ccx.sess.targ_cfg.arch {
-      session::arch_x86 | session::arch_arm => ~"llvm.memmove.p0i8.p0i8.i32",
-      session::arch_x86_64 => ~"llvm.memmove.p0i8.p0i8.i64"
-    };
-    let memmove = ccx.intrinsics.get(key);
-    let src_ptr = PointerCast(cx, src, T_ptr(T_i8()));
-    let dst_ptr = PointerCast(cx, dst, T_ptr(T_i8()));
-    let size = IntCast(cx, n_bytes, ccx.int_type);
-    let align = C_i32(1i32);
-    let volatile = C_bool(false);
-    Call(cx, memmove, ~[dst_ptr, src_ptr, size, align, volatile]);
-}
-
-fn memmove_ty(bcx: block, dst: ValueRef, src: ValueRef, t: ty::t) {
-    let _icx = bcx.insn_ctxt("memmove_ty");
-    let ccx = bcx.ccx();
-    if ty::type_is_structural(t) {
-        let llsz = llsize_of(ccx, type_of(ccx, t));
-        call_memmove(bcx, dst, src, llsz);
-    } else {
-        Store(bcx, Load(bcx, src), dst);
-    }
-}
-
-enum copy_action { INIT, DROP_EXISTING, }
-
-impl copy_action : cmp::Eq {
-    pure fn eq(&&other: copy_action) -> bool {
-        match (self, other) {
-            (INIT, INIT) => true,
-            (DROP_EXISTING, DROP_EXISTING) => true,
-            (INIT, _) => false,
-            (DROP_EXISTING, _) => false,
-        }
-    }
-}
-
-// These are the types that are passed by pointer.
-fn type_is_structural_or_param(t: ty::t) -> bool {
-    if ty::type_is_structural(t) { return true; }
-    match ty::get(t).struct {
-      ty::ty_param(*) => return true,
-      _ => return false
-    }
-}
-
-fn copy_val(cx: block, action: copy_action, dst: ValueRef,
-            src: ValueRef, t: ty::t) -> block {
-    let _icx = cx.insn_ctxt("copy_val");
-    if action == DROP_EXISTING &&
-        (type_is_structural_or_param(t) ||
-         ty::type_is_unique(t)) {
-        let dstcmp = load_if_immediate(cx, dst, t);
-        let cast = PointerCast(cx, dstcmp, val_ty(src));
-        // Self-copy check
-        do with_cond(cx, ICmp(cx, lib::llvm::IntNE, cast, src)) |bcx| {
-            copy_val_no_check(bcx, action, dst, src, t)
-        }
-    } else {
-        copy_val_no_check(cx, action, dst, src, t)
-    }
-}
-
-fn copy_val_no_check(bcx: block, action: copy_action, dst: ValueRef,
-                     src: ValueRef, t: ty::t) -> block {
-    let _icx = bcx.insn_ctxt("copy_val_no_check");
-    let ccx = bcx.ccx();
-    let mut bcx = bcx;
-    if ty::type_is_scalar(t) || ty::type_is_region_ptr(t) {
-        Store(bcx, src, dst);
-        return bcx;
-    }
-    if ty::type_is_nil(t) || ty::type_is_bot(t) { return bcx; }
-    if ty::type_is_boxed(t) || ty::type_is_unique(t) {
-        if action == DROP_EXISTING { bcx = drop_ty(bcx, dst, t); }
-        Store(bcx, src, dst);
-        return take_ty(bcx, dst, t);
-    }
-    if type_is_structural_or_param(t) {
-        if action == DROP_EXISTING { bcx = drop_ty(bcx, dst, t); }
-        memmove_ty(bcx, dst, src, t);
-        return take_ty(bcx, dst, t);
-    }
-    ccx.sess.bug(~"unexpected type in trans::copy_val_no_check: " +
-                     ppaux::ty_to_str(ccx.tcx, t));
-}
-
-
-// This works like copy_val, except that it deinitializes the source.
-// Since it needs to zero out the source, src also needs to be an lval.
-// FIXME (#839): We always zero out the source. Ideally we would detect the
-// case where a variable is always deinitialized by block exit and thus
-// doesn't need to be dropped.
-fn move_val(cx: block, action: copy_action, dst: ValueRef,
-            src: lval_result, t: ty::t) -> block {
-
-    let _icx = cx.insn_ctxt("move_val");
-    let mut src_val = src.val;
-    let tcx = cx.tcx();
-    let mut cx = cx;
-    if ty::type_is_scalar(t) || ty::type_is_region_ptr(t) {
-        if src.kind == lv_owned { src_val = Load(cx, src_val); }
-        Store(cx, src_val, dst);
-        return cx;
-    } else if ty::type_is_nil(t) || ty::type_is_bot(t) {
-        return cx;
-    } else if ty::type_is_boxed(t) || ty::type_is_unique(t) {
-        if src.kind == lv_owned { src_val = Load(cx, src_val); }
-        if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); }
-        Store(cx, src_val, dst);
-        if src.kind == lv_owned { return zero_mem(cx, src.val, t); }
-        // If we're here, it must be a temporary.
-        revoke_clean(cx, src_val);
-        return cx;
-    } else if type_is_structural_or_param(t) {
-        if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); }
-        memmove_ty(cx, dst, src_val, t);
-        if src.kind == lv_owned { return zero_mem(cx, src_val, t); }
-        // If we're here, it must be a temporary.
-        revoke_clean(cx, src_val);
-        return cx;
-    }
-    cx.sess().bug(~"unexpected type in trans::move_val: " +
-                  ppaux::ty_to_str(tcx, t));
-}
-
-fn store_temp_expr(cx: block, action: copy_action, dst: ValueRef,
-                   src: lval_result, t: ty::t, last_use: bool)
-    -> block {
-    let _icx = cx.insn_ctxt("trans_temp_expr");
-    // Lvals in memory are not temporaries. Copy them.
-    if src.kind != lv_temporary && !last_use {
-        let v = if src.kind == lv_owned {
-                    load_if_immediate(cx, src.val, t)
-                } else {
-                    src.val
-                };
-        return copy_val(cx, action, dst, v, t);
-    }
-    return move_val(cx, action, dst, src, t);
-}
-
-fn trans_lit(cx: block, e: @ast::expr, lit: ast::lit, dest: dest) -> block {
-    let _icx = cx.insn_ctxt("trans_lit");
-    if dest == ignore { return cx; }
-    match lit.node {
-        ast::lit_str(s) => tvec::trans_estr(cx, s, None, dest),
-        _ => store_in_dest(cx, consts::const_lit(cx.ccx(), e, lit), dest)
-    }
-}
-
-fn trans_boxed_expr(bcx: block, contents: @ast::expr,
-                    t: ty::t, heap: heap,
-                    dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_boxed_expr");
-    let {bcx, box, body} = malloc_general(bcx, t, heap);
-    add_clean_free(bcx, box, heap);
-    let bcx = trans_expr_save_in(bcx, contents, body);
-    revoke_clean(bcx, box);
-    return store_in_dest(bcx, box, dest);
-}
-
-fn trans_unary(bcx: block, op: ast::unop, e: @ast::expr,
-               un_expr: @ast::expr, dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_unary");
-    // Check for user-defined method call
-    match bcx.ccx().maps.method_map.find(un_expr.id) {
-      Some(mentry) => {
-        let fty = node_id_type(bcx, un_expr.callee_id);
-        return trans_call_inner(
-            bcx, un_expr.info(), fty,
-            expr_ty(bcx, un_expr),
-            |bcx| impl::trans_method_callee(bcx, un_expr.callee_id, e,
-                                            mentry),
-            arg_exprs(~[]), dest);
-      }
-      _ => ()
-    }
-
-    if dest == ignore { return trans_expr(bcx, e, ignore); }
-    let e_ty = expr_ty(bcx, e);
-    match op {
-      ast::not => {
-        let {bcx, val} = trans_temp_expr(bcx, e);
-        store_in_dest(bcx, Not(bcx, val), dest)
-      }
-      ast::neg => {
-        let {bcx, val} = trans_temp_expr(bcx, e);
-        let llneg = if ty::type_is_fp(e_ty) {
-            FNeg(bcx, val)
-        } else { Neg(bcx, val) };
-        store_in_dest(bcx, llneg, dest)
-      }
-      ast::box(_) => {
-        trans_boxed_expr(bcx, e, e_ty, heap_shared, dest)
-      }
-      ast::uniq(_) => {
-        trans_boxed_expr(bcx, e, e_ty, heap_exchange, dest)
-      }
-      ast::deref => {
-        bcx.sess().bug(~"deref expressions should have been \
-                               translated using trans_lval(), not \
-                               trans_unary()")
-      }
-    }
-}
-
-fn trans_addr_of(cx: block, e: @ast::expr, dest: dest) -> block {
-    let _icx = cx.insn_ctxt("trans_addr_of");
-    let mut {bcx, val, kind} = trans_temp_lval(cx, e);
-    let ety = expr_ty(cx, e);
-    let is_immediate = ty::type_is_immediate(ety);
-    if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm {
-        val = do_spill(bcx, val, ety);
-    }
-    return store_in_dest(bcx, val, dest);
-}
-
 fn trans_compare(cx: block, op: ast::binop, lhs: ValueRef,
-                 _lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result {
+                 _lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> Result {
     let _icx = cx.insn_ctxt("trans_compare");
     if ty::type_is_scalar(rhs_t) {
       let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, op);
@@ -1671,7 +623,7 @@ fn trans_compare(cx: block, op: ast::binop, lhs: ValueRef,
         }
     };
 
-    let cmpval = call_cmp_glue(cx, lhs, rhs, rhs_t, llop);
+    let cmpval = glue::call_cmp_glue(cx, lhs, rhs, rhs_t, llop);
 
     // Invert the result if necessary.
     match op {
@@ -1741,393 +693,14 @@ fn fail_if_zero(cx: block, span: span, divmod: ast::binop,
       }
     };
     do with_cond(cx, is_zero) |bcx| {
-        trans_fail(bcx, Some(span), text)
-    }
-}
-
-// Important to get types for both lhs and rhs, because one might be _|_
-// and the other not.
-fn trans_eager_binop(cx: block, span: span, op: ast::binop, lhs: ValueRef,
-                     lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t, dest: dest)
-    -> block {
-    let mut cx = cx;
-    let _icx = cx.insn_ctxt("trans_eager_binop");
-    if dest == ignore { return cx; }
-    let intype = {
-        if ty::type_is_bot(lhs_t) { rhs_t }
-        else { lhs_t }
-    };
-    let is_float = ty::type_is_fp(intype);
-
-    let rhs = cast_shift_expr_rhs(cx, op, lhs, rhs);
-
-    let mut cx = cx;
-    let val = match op {
-      ast::add => {
-        if is_float { FAdd(cx, lhs, rhs) }
-        else { Add(cx, lhs, rhs) }
-      }
-      ast::subtract => {
-        if is_float { FSub(cx, lhs, rhs) }
-        else { Sub(cx, lhs, rhs) }
-      }
-      ast::mul => {
-        if is_float { FMul(cx, lhs, rhs) }
-        else { Mul(cx, lhs, rhs) }
-      }
-      ast::div => {
-        if is_float {
-            FDiv(cx, lhs, rhs)
-        } else {
-            // Only zero-check integers; fp /0 is NaN
-            cx = fail_if_zero(cx, span, op, rhs, rhs_t);
-            if ty::type_is_signed(intype) {
-                SDiv(cx, lhs, rhs)
-            } else {
-                UDiv(cx, lhs, rhs)
-            }
-        }
-      }
-      ast::rem => {
-        if is_float {
-            FRem(cx, lhs, rhs)
-        } else {
-            // Only zero-check integers; fp %0 is NaN
-            cx = fail_if_zero(cx, span, op, rhs, rhs_t);
-            if ty::type_is_signed(intype) {
-                SRem(cx, lhs, rhs)
-            } else {
-                URem(cx, lhs, rhs)
-            }
-        }
-      }
-      ast::bitor => Or(cx, lhs, rhs),
-      ast::bitand => And(cx, lhs, rhs),
-      ast::bitxor => Xor(cx, lhs, rhs),
-      ast::shl => Shl(cx, lhs, rhs),
-      ast::shr => {
-        if ty::type_is_signed(intype) {
-            AShr(cx, lhs, rhs)
-        } else { LShr(cx, lhs, rhs) }
-      }
-      _ => {
-        let cmpr = trans_compare(cx, op, lhs, lhs_t, rhs, rhs_t);
-        cx = cmpr.bcx;
-        cmpr.val
-      }
-    };
-    return store_in_dest(cx, val, dest);
-}
-
-fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop,
-                   dst: @ast::expr, src: @ast::expr) -> block {
-    debug!("%s", expr_to_str(ex, bcx.tcx().sess.parse_sess.interner));
-    let _icx = bcx.insn_ctxt("trans_assign_op");
-    let t = expr_ty(bcx, src);
-    let lhs_res = trans_lval(bcx, dst);
-    assert (lhs_res.kind == lv_owned);
-
-    // A user-defined operator method
-    match bcx.ccx().maps.method_map.find(ex.id) {
-      Some(origin) => {
-        let bcx = lhs_res.bcx;
-        debug!("user-defined method callee_id: %s",
-               ast_map::node_id_to_str(bcx.tcx().items, ex.callee_id,
-                                       bcx.sess().parse_sess.interner));
-        let fty = node_id_type(bcx, ex.callee_id);
-
-        let dty = expr_ty(bcx, dst);
-        let target = alloc_ty(bcx, dty);
-
-        let bcx = trans_call_inner(
-            bcx, ex.info(), fty,
-            expr_ty(bcx, ex),
-            |bcx| {
-                // FIXME (#2528): provide the already-computed address, not
-                // the expr.
-                impl::trans_method_callee(bcx, ex.callee_id, dst, origin)
-            },
-            arg_exprs(~[src]), save_in(target));
-
-        return move_val(bcx, DROP_EXISTING, lhs_res.val,
-                     {bcx: bcx, val: target, kind: lv_owned},
-                     dty);
-      }
-      _ => ()
+        controlflow::trans_fail(bcx, Some(span), text)
     }
-
-    let {bcx, val: rhs_val} = trans_temp_expr(lhs_res.bcx, src);
-    return trans_eager_binop(bcx, ex.span,
-                          op, Load(bcx, lhs_res.val), t, rhs_val, t,
-                          save_in(lhs_res.val));
 }
 
-fn root_value(bcx: block, val: ValueRef, ty: ty::t,
-              scope_id: ast::node_id) {
-    let _icx = bcx.insn_ctxt("root_value");
-
-    if bcx.sess().trace() {
-        trans_trace(
-            bcx, None,
-            fmt!("preserving until end of scope %d", scope_id));
-    }
-
-    let root_loc = alloca_zeroed(bcx, type_of(bcx.ccx(), ty));
-    copy_val(bcx, INIT, root_loc, val, ty);
-    add_root_cleanup(bcx, scope_id, root_loc, ty);
-}
-
-// autoderefs the value `v`, either as many times as we can (if `max ==
-// uint::max_value`) or `max` times.
-fn autoderef(cx: block, e_id: ast::node_id,
-             v: ValueRef, t: ty::t,
-             max: uint) -> result_t {
-    let _icx = cx.insn_ctxt("autoderef");
-    let mut v1: ValueRef = v;
-    let mut t1: ty::t = t;
-    let ccx = cx.ccx();
-    let mut derefs = 0u;
-    while derefs < max {
-        debug!("autoderef(e_id=%d, v1=%s, t1=%s, derefs=%u)",
-               e_id, val_str(ccx.tn, v1), ppaux::ty_to_str(ccx.tcx, t1),
-               derefs);
-
-        // root the autoderef'd value, if necessary:
-        derefs += 1u;
-        match ccx.maps.root_map.find({id:e_id, derefs:derefs}) {
-          None => (),
-          Some(scope_id) => {
-            root_value(cx, v1, t1, scope_id);
-          }
-        }
-
-        match ty::get(t1).struct {
-          ty::ty_box(mt) => {
-            let body = GEPi(cx, v1, [0u, abi::box_field_body]);
-            t1 = mt.ty;
-
-            // Since we're changing levels of box indirection, we may have
-            // to cast this pointer, since statically-sized enum types have
-            // different types depending on whether they're behind a box
-            // or not.
-            let llty = type_of(ccx, t1);
-            v1 = PointerCast(cx, body, T_ptr(llty));
-          }
-          ty::ty_uniq(_) => {
-            let derefed = uniq::autoderef(cx, v1, t1);
-            t1 = derefed.t;
-            v1 = derefed.v;
-          }
-          ty::ty_rptr(_, mt) => {
-            t1 = mt.ty;
-            v1 = v;
-          }
-          ty::ty_enum(did, ref substs) => {
-            let variants = ty::enum_variants(ccx.tcx, did);
-            if (*variants).len() != 1u || variants[0].args.len() != 1u {
-                break;
-            }
-            t1 = ty::subst(ccx.tcx, substs, variants[0].args[0]);
-            v1 = PointerCast(cx, v1, T_ptr(type_of(ccx, t1)));
-          }
-          _ => break
-        }
-        v1 = load_if_immediate(cx, v1, t1);
-    }
-
-    // either we were asked to deref a specific number of times, in which case
-    // we should have, or we asked to deref as many times as we can
-    assert derefs == max || max == uint::max_value;
-
-    return {bcx: cx, val: v1, ty: t1};
-}
-
-// refinement types would obviate the need for this
-enum lazy_binop_ty { lazy_and, lazy_or }
-
-fn trans_lazy_binop(bcx: block, op: lazy_binop_ty, a: @ast::expr,
-                    b: @ast::expr, dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_lazy_binop");
-    let {bcx: past_lhs, val: lhs} = {
-        do with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
-            trans_temp_expr(bcx, a)
-        }
-    };
-    if past_lhs.unreachable { return past_lhs; }
-    let join = sub_block(bcx, ~"join"), before_rhs = sub_block(bcx, ~"rhs");
-
-    match op {
-      lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb),
-      lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb)
-    }
-    let {bcx: past_rhs, val: rhs} = {
-        do with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| {
-            trans_temp_expr(bcx, b)
-        }
-    };
-
-    if past_rhs.unreachable { return store_in_dest(join, lhs, dest); }
-    Br(past_rhs, join.llbb);
-    let phi =
-        Phi(join, T_bool(), ~[lhs, rhs], ~[past_lhs.llbb, past_rhs.llbb]);
-    return store_in_dest(join, phi, dest);
-}
-
-fn trans_binary(bcx: block, op: ast::binop, lhs: @ast::expr,
-                rhs: @ast::expr, dest: dest, ex: @ast::expr) -> block {
-    let _icx = bcx.insn_ctxt("trans_binary");
-    // User-defined operators
-    match bcx.ccx().maps.method_map.find(ex.id) {
-      Some(origin) => {
-        let fty = node_id_type(bcx, ex.callee_id);
-        return trans_call_inner(
-            bcx, ex.info(), fty,
-            expr_ty(bcx, ex),
-            |bcx| {
-                impl::trans_method_callee(bcx, ex.callee_id, lhs, origin)
-            },
-            arg_exprs(~[rhs]), dest);
-      }
-      _ => ()
-    }
-
-    // First couple cases are lazy:
-    match op {
-      ast::and => {
-        return trans_lazy_binop(bcx, lazy_and, lhs, rhs, dest);
-      }
-      ast::or => {
-        return trans_lazy_binop(bcx, lazy_or, lhs, rhs, dest);
-      }
-      _ => {
-        // Remaining cases are eager:
-        let lhs_res = trans_temp_expr(bcx, lhs);
-        let rhs_res = trans_temp_expr(lhs_res.bcx, rhs);
-        return trans_eager_binop(rhs_res.bcx, ex.span,
-                              op, lhs_res.val,
-                              expr_ty(bcx, lhs), rhs_res.val,
-                              expr_ty(bcx, rhs), dest);
-      }
-    }
-}
-
-fn trans_if(cx: block, cond: @ast::expr, thn: ast::blk,
-            els: Option<@ast::expr>, dest: dest)
-    -> block {
-    let _icx = cx.insn_ctxt("trans_if");
-    let {bcx, val: cond_val} = trans_temp_expr(cx, cond);
-
-    let then_dest = dup_for_join(dest);
-    let else_dest = dup_for_join(dest);
-    let then_cx = scope_block(bcx, thn.info(), ~"then");
-    let else_cx = scope_block(bcx, els.info(), ~"else");
-    CondBr(bcx, cond_val, then_cx.llbb, else_cx.llbb);
-    let then_bcx = trans_block(then_cx, thn, then_dest);
-    let then_bcx = trans_block_cleanups(then_bcx, block_cleanups(then_cx));
-    // Calling trans_block directly instead of trans_expr
-    // because trans_expr will create another scope block
-    // context for the block, but we've already got the
-    // 'else' context
-    let else_bcx = match els {
-      Some(elexpr) => {
-        match elexpr.node {
-          ast::expr_if(_, _, _) => {
-            let elseif_blk = ast_util::block_from_expr(elexpr);
-            trans_block(else_cx, elseif_blk, else_dest)
-          }
-          ast::expr_block(blk) => {
-            trans_block(else_cx, blk, else_dest)
-          }
-          // would be nice to have a constraint on ifs
-          _ => cx.tcx().sess.bug(~"strange alternative in if")
-        }
-      }
-      _ => else_cx
-    };
-    let else_bcx = trans_block_cleanups(else_bcx, block_cleanups(else_cx));
-    return join_returns(cx,
-                     ~[then_bcx, else_bcx], ~[then_dest, else_dest], dest);
-}
-
-fn trans_while(cx: block, cond: @ast::expr, body: ast::blk)
-    -> block {
-    let _icx = cx.insn_ctxt("trans_while");
-    let next_cx = sub_block(cx, ~"while next");
-    let loop_cx = loop_scope_block(cx, next_cx, ~"`while`", body.info());
-    let cond_cx = scope_block(loop_cx, cond.info(), ~"while loop cond");
-    let body_cx = scope_block(loop_cx, body.info(), ~"while loop body");
-    Br(cx, loop_cx.llbb);
-    Br(loop_cx, cond_cx.llbb);
-    let cond_res = trans_temp_expr(cond_cx, cond);
-    let cond_bcx = trans_block_cleanups(cond_res.bcx,
-                                        block_cleanups(cond_cx));
-    CondBr(cond_bcx, cond_res.val, body_cx.llbb, next_cx.llbb);
-    let body_end = trans_block(body_cx, body, ignore);
-    cleanup_and_Br(body_end, body_cx, cond_cx.llbb);
-    return next_cx;
-}
-
-fn trans_loop(cx:block, body: ast::blk) -> block {
-    let _icx = cx.insn_ctxt("trans_loop");
-    let next_cx = sub_block(cx, ~"next");
-    let body_cx = loop_scope_block(cx, next_cx, ~"`loop`", body.info());
-    let body_end = trans_block(body_cx, body, ignore);
-    cleanup_and_Br(body_end, body_cx, body_cx.llbb);
-    Br(cx, body_cx.llbb);
-    return next_cx;
-}
-
-enum lval_kind {
-    lv_temporary, //< Temporary value passed by value if of immediate type
-    lv_owned,     //< Non-temporary value passed by pointer
-    lv_owned_imm, //< Non-temporary value passed by value
-}
-
-impl lval_kind : cmp::Eq {
-    pure fn eq(&&other: lval_kind) -> bool {
-        match (self, other) {
-            (lv_temporary, lv_temporary) => true,
-            (lv_owned, lv_owned) => true,
-            (lv_owned_imm, lv_owned_imm) => true,
-            (lv_temporary, _) => false,
-            (lv_owned, _) => false,
-            (lv_owned_imm, _) => false,
-        }
-    }
-}
-
-type local_var_result = {val: ValueRef, kind: lval_kind};
-type lval_result = {bcx: block, val: ValueRef, kind: lval_kind};
-enum callee_env {
-    null_env,
-    is_closure,
-    self_env(ValueRef, ty::t, Option<ValueRef>, ast::rmode),
-}
-type lval_maybe_callee = {bcx: block,
-                          val: ValueRef,
-                          kind: lval_kind,
-                          env: callee_env};
-
 fn null_env_ptr(bcx: block) -> ValueRef {
     C_null(T_opaque_box_ptr(bcx.ccx()))
 }
 
-fn lval_from_local_var(bcx: block, r: local_var_result) -> lval_result {
-    return { bcx: bcx, val: r.val, kind: r.kind };
-}
-
-fn lval_owned(bcx: block, val: ValueRef) -> lval_result {
-    return {bcx: bcx, val: val, kind: lv_owned};
-}
-fn lval_temp(bcx: block, val: ValueRef) -> lval_result {
-    return {bcx: bcx, val: val, kind: lv_temporary};
-}
-
-fn lval_no_env(bcx: block, val: ValueRef, kind: lval_kind)
-    -> lval_maybe_callee {
-    return {bcx: bcx, val: val, kind: kind, env: is_closure};
-}
-
 fn trans_external_path(ccx: @crate_ctxt, did: ast::def_id, t: ty::t)
     -> ValueRef {
     let name = csearch::get_symbol(ccx.sess.cstore, did);
@@ -2144,436 +717,6 @@ fn trans_external_path(ccx: @crate_ctxt, did: ast::def_id, t: ty::t)
     };
 }
 
-fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option<ty::t> {
-    // FIXME[mono] could do this recursively. is that worthwhile? (#2529)
-    match ty::get(ty).struct {
-      ty::ty_box(*) => {
-        Some(ty::mk_opaque_box(tcx))
-      }
-      ty::ty_fn(ref fty) => {
-        Some(ty::mk_fn(tcx, {purity: ast::impure_fn,
-                             proto: fty.proto,
-                             bounds: @~[],
-                             inputs: ~[],
-                             output: ty::mk_nil(tcx),
-                             ret_style: ast::return_val}))
-      }
-      ty::ty_trait(_, _, _) => {
-        Some(ty::mk_fn(tcx, {purity: ast::impure_fn,
-                             proto: ty::proto_vstore(ty::vstore_box),
-                             bounds: @~[],
-                             inputs: ~[],
-                             output: ty::mk_nil(tcx),
-                             ret_style: ast::return_val}))
-      }
-      ty::ty_ptr(_) => Some(ty::mk_uint(tcx)),
-      _ => None
-    }
-}
-
-fn make_mono_id(ccx: @crate_ctxt, item: ast::def_id, substs: ~[ty::t],
-                vtables: Option<typeck::vtable_res>,
-                param_uses: Option<~[type_use::type_uses]>) -> mono_id {
-    let precise_param_ids = match vtables {
-      Some(vts) => {
-        let bounds = ty::lookup_item_type(ccx.tcx, item).bounds;
-        let mut i = 0u;
-        vec::map2(*bounds, substs, |bounds, subst| {
-            let mut v = ~[];
-            for vec::each(*bounds) |bound| {
-                match bound {
-                  ty::bound_trait(_) => {
-                    vec::push(v, impl::vtable_id(ccx, vts[i]));
-                    i += 1u;
-                  }
-                  _ => ()
-                }
-            }
-            (subst, if v.len() > 0u { Some(v) } else { None })
-        })
-      }
-      None => {
-        vec::map(substs, |subst| (subst, None))
-      }
-    };
-    let param_ids = match param_uses {
-      Some(uses) => {
-        vec::map2(precise_param_ids, uses, |id, uses| {
-            match id {
-                (a, b@Some(_)) => mono_precise(a, b),
-              (subst, None) => {
-                if uses == 0u { mono_any }
-                else if uses == type_use::use_repr &&
-                        !ty::type_needs_drop(ccx.tcx, subst) {
-                    let llty = type_of(ccx, subst);
-                    let size = shape::llsize_of_real(ccx, llty);
-                    let align = shape::llalign_of_pref(ccx, llty);
-                    // Special value for nil to prevent problems with undef
-                    // return pointers.
-                    if size == 1u && ty::type_is_nil(subst) {
-                        mono_repr(0u, 0u)
-                    } else { mono_repr(size, align) }
-                } else { mono_precise(subst, None) }
-              }
-            }
-        })
-      }
-      None => precise_param_ids.map(|x| { let (a, b) = x;
-                mono_precise(a, b) })
-    };
-    @{def: item, params: param_ids}
-}
-
-fn monomorphic_fn(ccx: @crate_ctxt, fn_id: ast::def_id,
-                  real_substs: ~[ty::t],
-                  vtables: Option<typeck::vtable_res>,
-                  ref_id: Option<ast::node_id>)
-    -> {val: ValueRef, must_cast: bool} {
-    let _icx = ccx.insn_ctxt("monomorphic_fn");
-    let mut must_cast = false;
-    let substs = vec::map(real_substs, |t| {
-        match normalize_for_monomorphization(ccx.tcx, t) {
-          Some(t) => { must_cast = true; t }
-          None => t
-        }
-    });
-
-    for real_substs.each() |s| { assert !ty::type_has_params(s); }
-    for substs.each() |s| { assert !ty::type_has_params(s); }
-    let param_uses = type_use::type_uses_for(ccx, fn_id, substs.len());
-    let hash_id = make_mono_id(ccx, fn_id, substs, vtables, Some(param_uses));
-    if vec::any(hash_id.params,
-                |p| match p { mono_precise(_, _) => false, _ => true }) {
-        must_cast = true;
-    }
-
-    #debug["monomorphic_fn(fn_id=%? (%s), real_substs=%?, substs=%?, \
-           hash_id = %?",
-           fn_id, ty::item_path_str(ccx.tcx, fn_id),
-           real_substs.map(|s| ty_to_str(ccx.tcx, s)),
-           substs.map(|s| ty_to_str(ccx.tcx, s)), hash_id];
-
-    match ccx.monomorphized.find(hash_id) {
-      Some(val) => {
-        debug!("leaving monomorphic fn %s",
-               ty::item_path_str(ccx.tcx, fn_id));
-        return {val: val, must_cast: must_cast};
-      }
-      None => ()
-    }
-
-    let tpt = ty::lookup_item_type(ccx.tcx, fn_id);
-    let mut llitem_ty = tpt.ty;
-
-    let map_node = session::expect(ccx.sess, ccx.tcx.items.find(fn_id.node),
-     || fmt!("While monomorphizing %?, couldn't find it in the item map \
-        (may have attempted to monomorphize an item defined in a different \
-        crate?)", fn_id));
-    // Get the path so that we can create a symbol
-    let (pt, name, span) = match map_node {
-      ast_map::node_item(i, pt) => (pt, i.ident, i.span),
-      ast_map::node_variant(v, enm, pt) => (pt, v.node.name, enm.span),
-      ast_map::node_method(m, _, pt) => (pt, m.ident, m.span),
-      ast_map::node_foreign_item(i, ast::foreign_abi_rust_intrinsic, pt)
-      => (pt, i.ident, i.span),
-      ast_map::node_foreign_item(*) => {
-        // Foreign externs don't have to be monomorphized.
-        return {val: get_item_val(ccx, fn_id.node),
-                must_cast: true};
-      }
-      ast_map::node_ctor(nm, _, ct, _, pt) => (pt, nm, ct.span),
-      ast_map::node_dtor(_, dtor, _, pt) =>
-          (pt, special_idents::dtor, dtor.span),
-      ast_map::node_trait_method(*) => {
-        ccx.tcx.sess.bug(~"Can't monomorphize a trait method")
-      }
-      ast_map::node_expr(*) => {
-        ccx.tcx.sess.bug(~"Can't monomorphize an expr")
-      }
-      ast_map::node_stmt(*) => {
-        ccx.tcx.sess.bug(~"Can't monomorphize a stmt")
-      }
-      ast_map::node_export(*) => {
-          ccx.tcx.sess.bug(~"Can't monomorphize an export")
-      }
-      ast_map::node_arg(*) => ccx.tcx.sess.bug(~"Can't monomorphize an arg"),
-      ast_map::node_block(*) => {
-          ccx.tcx.sess.bug(~"Can't monomorphize a block")
-      }
-      ast_map::node_local(*) => {
-          ccx.tcx.sess.bug(~"Can't monomorphize a local")
-      }
-    };
-    let mono_ty = ty::subst_tps(ccx.tcx, substs, llitem_ty);
-    let llfty = type_of_fn_from_ty(ccx, mono_ty);
-
-    let depth = option::get_default(ccx.monomorphizing.find(fn_id), 0u);
-    // Random cut-off -- code that needs to instantiate the same function
-    // recursively more than ten times can probably safely be assumed to be
-    // causing an infinite expansion.
-    if depth > 10u {
-        ccx.sess.span_fatal(
-            span, ~"overly deep expansion of inlined function");
-    }
-    ccx.monomorphizing.insert(fn_id, depth + 1u);
-
-    let pt = vec::append(*pt,
-                         ~[path_name(ccx.names(ccx.sess.str_of(name)))]);
-    let s = mangle_exported_name(ccx, pt, mono_ty);
-
-    let mk_lldecl = || {
-        let lldecl = decl_internal_cdecl_fn(ccx.llmod, s, llfty);
-        ccx.monomorphized.insert(hash_id, lldecl);
-        lldecl
-    };
-
-    let psubsts = Some({tys: substs, vtables: vtables, bounds: tpt.bounds});
-    let lldecl = match map_node {
-      ast_map::node_item(i@@{node: ast::item_fn(decl, _, _, body), _}, _) => {
-        let d = mk_lldecl();
-        set_inline_hint_if_appr(i.attrs, d);
-        trans_fn(ccx, pt, decl, body, d, no_self, psubsts, fn_id.node);
-        d
-      }
-      ast_map::node_item(*) => {
-          ccx.tcx.sess.bug(~"Can't monomorphize this kind of item")
-      }
-      ast_map::node_foreign_item(i, _, _) => {
-          let d = mk_lldecl();
-          foreign::trans_intrinsic(ccx, d, i, pt, option::get(psubsts),
-                                ref_id);
-          d
-      }
-      ast_map::node_variant(v, enum_item, _) => {
-        let tvs = ty::enum_variants(ccx.tcx, local_def(enum_item.id));
-        let this_tv = option::get(vec::find(*tvs, |tv| {
-            tv.id.node == fn_id.node}));
-        let d = mk_lldecl();
-        set_inline_hint(d);
-        match v.node.kind {
-            ast::tuple_variant_kind(args) => {
-                trans_enum_variant(ccx, enum_item.id, v, args,
-                                   this_tv.disr_val, (*tvs).len() == 1u,
-                                   psubsts, d);
-            }
-            ast::struct_variant_kind(_) =>
-                ccx.tcx.sess.bug(~"can't monomorphize struct variants"),
-            ast::enum_variant_kind(_) =>
-                ccx.tcx.sess.bug(~"can't monomorphize enum variants")
-        }
-        d
-      }
-      ast_map::node_method(mth, _, _) => {
-        let d = mk_lldecl();
-        set_inline_hint_if_appr(mth.attrs, d);
-        impl::trans_method(ccx, pt, mth, psubsts, d);
-        d
-      }
-      ast_map::node_ctor(_, tps, ctor, parent_id, _) => {
-        // ctors don't have attrs, at least not right now
-        let d = mk_lldecl();
-        let tp_tys = ty::ty_params_to_tys(ccx.tcx, tps);
-        trans_class_ctor(ccx, pt, ctor.node.dec, ctor.node.body, d,
-               option::get_default(psubsts,
-                        {tys:tp_tys, vtables: None, bounds: @~[]}),
-                         fn_id.node, parent_id, ctor.span);
-        d
-      }
-      ast_map::node_dtor(_, dtor, _, pt) => {
-        let parent_id = match ty::ty_to_def_id(ty::node_id_to_type(ccx.tcx,
-                                              dtor.node.self_id)) {
-                Some(did) => did,
-                None      => ccx.sess.span_bug(dtor.span, ~"Bad self ty in \
-                                                            dtor")
-        };
-        trans_class_dtor(ccx, *pt, dtor.node.body,
-          dtor.node.id, psubsts, Some(hash_id), parent_id)
-      }
-      // Ugh -- but this ensures any new variants won't be forgotten
-      ast_map::node_expr(*) |
-      ast_map::node_stmt(*) |
-      ast_map::node_trait_method(*) |
-      ast_map::node_export(*) |
-      ast_map::node_arg(*) |
-      ast_map::node_block(*) |
-      ast_map::node_local(*) => {
-        ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node))
-      }
-    };
-    ccx.monomorphizing.insert(fn_id, depth);
-
-    debug!("leaving monomorphic fn %s", ty::item_path_str(ccx.tcx, fn_id));
-    {val: lldecl, must_cast: must_cast}
-}
-
-fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id)
-    -> ast::def_id {
-    let _icx = ccx.insn_ctxt("maybe_instantiate_inline");
-    match ccx.external.find(fn_id) {
-      Some(Some(node_id)) => {
-        // Already inline
-        debug!("maybe_instantiate_inline(%s): already inline as node id %d",
-               ty::item_path_str(ccx.tcx, fn_id), node_id);
-        local_def(node_id)
-      }
-      Some(None) => fn_id, // Not inlinable
-      None => { // Not seen yet
-        match csearch::maybe_get_item_ast(
-            ccx.tcx, fn_id,
-            |a,b,c,d| {
-                astencode::decode_inlined_item(a, b, ccx.maps, c, d)
-            }) {
-
-          csearch::not_found => {
-            ccx.external.insert(fn_id, None);
-            fn_id
-          }
-          csearch::found(ast::ii_item(item)) => {
-            ccx.external.insert(fn_id, Some(item.id));
-            trans_item(ccx, *item);
-            local_def(item.id)
-          }
-          csearch::found(ast::ii_ctor(ctor, _, _, _)) => {
-            ccx.external.insert(fn_id, Some(ctor.node.id));
-            local_def(ctor.node.id)
-          }
-          csearch::found(ast::ii_foreign(item)) => {
-            ccx.external.insert(fn_id, Some(item.id));
-            local_def(item.id)
-          }
-          csearch::found_parent(parent_id, ast::ii_item(item)) => {
-            ccx.external.insert(parent_id, Some(item.id));
-            let mut my_id = 0;
-            match item.node {
-              ast::item_enum(_, _) => {
-                let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
-                let vs_there = ty::enum_variants(ccx.tcx, parent_id);
-                do vec::iter2(*vs_here, *vs_there) |here, there| {
-                    if there.id == fn_id { my_id = here.id.node; }
-                    ccx.external.insert(there.id, Some(here.id.node));
-                }
-              }
-              _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \
-                    non-enum parent")
-            }
-            trans_item(ccx, *item);
-            local_def(my_id)
-          }
-          csearch::found_parent(_, _) => {
-              ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \
-               with a non-item parent");
-          }
-          csearch::found(ast::ii_method(impl_did, mth)) => {
-            ccx.external.insert(fn_id, Some(mth.id));
-            let {bounds: impl_bnds, region_param: _, ty: impl_ty} =
-                ty::lookup_item_type(ccx.tcx, impl_did);
-            if (*impl_bnds).len() + mth.tps.len() == 0u {
-                let llfn = get_item_val(ccx, mth.id);
-                let path = vec::append(
-                    ty::item_path(ccx.tcx, impl_did),
-                    ~[path_name(mth.ident)]);
-                trans_fn(ccx, path, mth.decl, mth.body,
-                         llfn, impl_self(impl_ty), None, mth.id);
-            }
-            local_def(mth.id)
-          }
-          csearch::found(ast::ii_dtor(dtor, _, _, _)) => {
-              ccx.external.insert(fn_id, Some(dtor.node.id));
-              local_def(dtor.node.id)
-          }
-        }
-      }
-    }
-}
-
-fn lval_static_fn(bcx: block, fn_id: ast::def_id, id: ast::node_id)
-    -> lval_maybe_callee {
-    let _icx = bcx.insn_ctxt("lval_static_fn");
-    let vts = option::map(bcx.ccx().maps.vtable_map.find(id), |vts| {
-        impl::resolve_vtables_in_fn_ctxt(bcx.fcx, vts)
-    });
-    lval_static_fn_inner(bcx, fn_id, id, node_id_type_params(bcx, id), vts)
-}
-
-fn lval_static_fn_inner(bcx: block, fn_id: ast::def_id, id: ast::node_id,
-                        tys: ~[ty::t], vtables: Option<typeck::vtable_res>)
-    -> lval_maybe_callee {
-    let _icx = bcx.insn_ctxt("lval_static_fn_inner");
-    let ccx = bcx.ccx(), tcx = ccx.tcx;
-    let tpt = ty::lookup_item_type(tcx, fn_id);
-
-    // Check whether this fn has an inlined copy and, if so, redirect fn_id to
-    // the local id of the inlined copy.
-    let fn_id = if fn_id.crate != ast::local_crate {
-        maybe_instantiate_inline(ccx, fn_id)
-    } else { fn_id };
-
-    let must_monomorphise = {
-        let local_with_type_params =
-            fn_id.crate == ast::local_crate && tys.len() > 0u;
-
-        // Intrinsic functions should always be monomorphised. In particular,
-        // if we see an intrinsic that is inlined from a different crate, we
-        // want to reemit the intrinsic instead of trying to call it in the
-        // other crate.
-        let rust_intrinsic = if fn_id.crate == ast::local_crate {
-
-            let map_node = session::expect(
-                ccx.sess,
-                ccx.tcx.items.find(fn_id.node),
-                || fmt!("local item should be in ast map"));
-
-            match map_node {
-              ast_map::node_foreign_item(
-                  _, ast::foreign_abi_rust_intrinsic, _) => true,
-              _ => false
-            }
-        } else {
-            false
-        };
-
-        local_with_type_params || rust_intrinsic
-    };
-
-    if must_monomorphise {
-        let mut {val, must_cast} =
-            monomorphic_fn(ccx, fn_id, tys, vtables, Some(id));
-        if must_cast {
-            val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty(
-                ccx, node_id_type(bcx, id))));
-        }
-        return {bcx: bcx, val: val, kind: lv_owned, env: null_env};
-    }
-
-    let mut val = if fn_id.crate == ast::local_crate {
-        // Internal reference.
-        get_item_val(ccx, fn_id.node)
-    } else {
-        // External reference.
-        trans_external_path(ccx, fn_id, tpt.ty)
-    };
-    if tys.len() > 0u {
-        val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty(
-            ccx, node_id_type(bcx, id))));
-    }
-
-    match ty::get(tpt.ty).struct {
-      ty::ty_fn(fn_ty) => {
-        match fn_ty.purity {
-          ast::extern_fn => {
-            // Extern functions are just opaque pointers
-            let val = PointerCast(bcx, val, T_ptr(T_i8()));
-            return lval_no_env(bcx, val, lv_owned_imm);
-          }
-          _ => { /* fall through */ }
-        }
-      }
-      _ => { /* fall through */ }
-    }
-
-    return {bcx: bcx, val: val, kind: lv_owned, env: null_env};
-}
-
 fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef {
     let _icx = ccx.insn_ctxt("lookup_discriminant");
     match ccx.discrims.find(vid) {
@@ -2593,855 +736,6 @@ fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef {
     }
 }
 
-// This shouldn't exist. We should cast self *once*, but right now this
-// conflicts with default methods.  (FIXME: #2794)
-fn cast_self(cx: block, slf: val_self_data) -> ValueRef {
-    PointerCast(cx, slf.v, T_ptr(type_of(cx.ccx(), slf.t)))
-}
-
-fn trans_local_var(cx: block, def: ast::def) -> local_var_result {
-    let _icx = cx.insn_ctxt("trans_local_var");
-    fn take_local(table: hashmap<ast::node_id, local_val>,
-                  id: ast::node_id) -> local_var_result {
-        match table.find(id) {
-          Some(local_mem(v)) => {val: v, kind: lv_owned},
-          Some(local_imm(v)) => {val: v, kind: lv_owned_imm},
-          None => fail(fmt!("take_local: internal error, \
-                             found no entry for %?", id))
-        }
-    }
-    match def {
-      ast::def_upvar(nid, _, _, _) => {
-        assert (cx.fcx.llupvars.contains_key(nid));
-        return { val: cx.fcx.llupvars.get(nid), kind: lv_owned };
-      }
-      ast::def_arg(nid, _) => {
-        assert (cx.fcx.llargs.contains_key(nid));
-        return take_local(cx.fcx.llargs, nid);
-      }
-      ast::def_local(nid, _) | ast::def_binding(nid, _) => {
-        assert (cx.fcx.lllocals.contains_key(nid));
-        return take_local(cx.fcx.lllocals, nid);
-      }
-      ast::def_self(_) => {
-        let slf = match copy cx.fcx.llself {
-          Some(s) => cast_self(cx, s),
-          None => cx.sess().bug(~"trans_local_var: reference to self \
-                                 out of context")
-        };
-        return {val: slf, kind: lv_owned};
-      }
-      _ => {
-        cx.sess().unimpl(fmt!("unsupported def type in trans_local_var: %?",
-                              def));
-      }
-    }
-}
-
-fn trans_path(cx: block, id: ast::node_id)
-    -> lval_maybe_callee {
-    let _icx = cx.insn_ctxt("trans_path");
-    match cx.tcx().def_map.find(id) {
-      None => cx.sess().bug(~"trans_path: unbound node ID"),
-      Some(df) => {
-          return trans_var(cx, df, id);
-      }
-    }
-}
-
-fn trans_var(cx: block, def: ast::def, id: ast::node_id)-> lval_maybe_callee {
-    let _icx = cx.insn_ctxt("trans_var");
-    let ccx = cx.ccx();
-    match def {
-      ast::def_fn(did, _) => {
-        return lval_static_fn(cx, did, id);
-      }
-      ast::def_static_method(did, _) => {
-        return impl::trans_static_method_callee(cx, did, id);
-      }
-      ast::def_variant(tid, vid) => {
-        if ty::enum_variant_with_id(ccx.tcx, tid, vid).args.len() > 0u {
-            // N-ary variant.
-            return lval_static_fn(cx, vid, id);
-        } else {
-            // Nullary variant.
-            let enum_ty = node_id_type(cx, id);
-            let llenumptr = alloc_ty(cx, enum_ty);
-            let lldiscrimptr = GEPi(cx, llenumptr, [0u, 0u]);
-            let lldiscrim_gv = lookup_discriminant(ccx, vid);
-            let lldiscrim = Load(cx, lldiscrim_gv);
-            Store(cx, lldiscrim, lldiscrimptr);
-            return lval_no_env(cx, llenumptr, lv_temporary);
-        }
-      }
-      ast::def_const(did) => {
-        if did.crate == ast::local_crate {
-            return lval_no_env(cx, get_item_val(ccx, did.node), lv_owned);
-        } else {
-            let tp = node_id_type(cx, id);
-            let val = trans_external_path(ccx, did, tp);
-            return lval_no_env(cx, load_if_immediate(cx, val, tp),
-                            lv_owned_imm);
-        }
-      }
-      _ => {
-        let loc = trans_local_var(cx, def);
-        return lval_no_env(cx, loc.val, loc.kind);
-      }
-    }
-}
-
-fn trans_rec_field(bcx: block, base: @ast::expr,
-                   field: ast::ident) -> lval_result {
-    let _icx = bcx.insn_ctxt("trans_rec_field");
-    let {bcx, val} = trans_temp_expr(bcx, base);
-    let {bcx, val, ty} =
-        autoderef(bcx, base.id, val, expr_ty(bcx, base),
-                  uint::max_value);
-    trans_rec_field_inner(bcx, val, ty, field, base.span)
-}
-
-fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t,
-                         field: ast::ident, sp: span) -> lval_result {
-    let mut llderef = false;
-    let fields = match ty::get(ty).struct {
-       ty::ty_rec(fs) => fs,
-       ty::ty_class(did, ref substs) => {
-         if option::is_some(ty::ty_dtor(bcx.tcx(), did)) {
-           llderef = true;
-         }
-         ty::class_items_as_mutable_fields(bcx.tcx(), did, substs)
-       }
-       // Constraint?
-       _ => bcx.tcx().sess.span_bug(sp, ~"trans_rec_field:\
-                 base expr has non-record type")
-    };
-    // seems wrong? Doesn't take into account the field
-    // sizes
-
-    let ix = field_idx_strict(bcx.tcx(), sp, field, fields);
-
-    debug!("val = %s ix = %u", bcx.val_str(val), ix);
-
-    /* self is a class with a dtor, which means we
-       have to select out the object itself
-       (If any other code does the same thing, that's
-       a bug */
-    let val = if llderef {
-        GEPi(bcx, GEPi(bcx, val, [0u, 1u]), [0u, ix])
-    }
-    else { GEPi(bcx, val, [0u, ix]) };
-
-    return {bcx: bcx, val: val, kind: lv_owned};
-}
-
-
-fn trans_index(cx: block, ex: @ast::expr, base: @ast::expr,
-               idx: @ast::expr) -> lval_result {
-    let _icx = cx.insn_ctxt("trans_index");
-    let base_ty = expr_ty(cx, base);
-    let exp = trans_temp_expr(cx, base);
-    let lv = autoderef(exp.bcx, base.id, exp.val, base_ty, uint::max_value);
-    let ix = trans_temp_expr(lv.bcx, idx);
-    let v = lv.val;
-    let bcx = ix.bcx;
-    let ccx = cx.ccx();
-
-    // Cast to an LLVM integer. Rust is less strict than LLVM in this regard.
-    let ix_size = llsize_of_real(cx.ccx(), val_ty(ix.val));
-    let int_size = llsize_of_real(cx.ccx(), ccx.int_type);
-    let ix_val = if ix_size < int_size {
-        if ty::type_is_signed(expr_ty(cx, idx)) {
-            SExt(bcx, ix.val, ccx.int_type)
-        } else { ZExt(bcx, ix.val, ccx.int_type) }
-    } else if ix_size > int_size {
-        Trunc(bcx, ix.val, ccx.int_type)
-    } else {
-        ix.val
-    };
-
-    let unit_ty = node_id_type(cx, ex.id);
-    let llunitty = type_of(ccx, unit_ty);
-    let unit_sz = llsize_of(ccx, llunitty);
-    maybe_name_value(cx.ccx(), unit_sz, ~"unit_sz");
-    let scaled_ix = Mul(bcx, ix_val, unit_sz);
-    maybe_name_value(cx.ccx(), scaled_ix, ~"scaled_ix");
-
-    let mut (base, len) = tvec::get_base_and_len(bcx, v, base_ty);
-
-    if ty::type_is_str(base_ty) {
-        len = Sub(bcx, len, C_uint(bcx.ccx(), 1u));
-    }
-
-    debug!("trans_index: base %s", val_str(bcx.ccx().tn, base));
-    debug!("trans_index: len %s", val_str(bcx.ccx().tn, len));
-
-    let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len);
-    let bcx = do with_cond(bcx, bounds_check) |bcx| {
-        // fail: bad bounds check.
-        trans_fail(bcx, Some(ex.span), ~"bounds check")
-    };
-    let elt = InBoundsGEP(bcx, base, ~[ix_val]);
-    return lval_owned(bcx, PointerCast(bcx, elt, T_ptr(llunitty)));
-}
-
-fn expr_is_borrowed(bcx: block, e: @ast::expr) -> bool {
-    bcx.tcx().borrowings.contains_key(e.id)
-}
-
-fn expr_is_lval(bcx: block, e: @ast::expr) -> bool {
-    let ccx = bcx.ccx();
-    ty::expr_is_lval(ccx.maps.method_map, e)
-}
-
-fn trans_callee(bcx: block, e: @ast::expr) -> lval_maybe_callee {
-    let _icx = bcx.insn_ctxt("trans_callee");
-    match e.node {
-      ast::expr_path(_) => return trans_path(bcx, e.id),
-      ast::expr_field(base, _, _) => {
-        // Lval means this is a record field, so not a method
-        if !expr_is_lval(bcx, e) {
-            match bcx.ccx().maps.method_map.find(e.id) {
-              Some(origin) => { // An impl method
-                return impl::trans_method_callee(bcx, e.id, base, origin);
-              }
-              _ => {
-                bcx.ccx().sess.span_bug(e.span, ~"trans_callee: weird expr");
-              }
-            }
-        }
-      }
-      _ => ()
-    }
-    let lv = trans_temp_lval(bcx, e);
-    return lval_no_env(lv.bcx, lv.val, lv.kind);
-}
-
-// Use this when you know you are compiling an lval.
-// The additional bool returned indicates whether it's mem (that is
-// represented as an alloca or heap, hence needs a 'load' to be used as an
-// immediate).
-fn trans_lval(cx: block, e: @ast::expr) -> lval_result {
-    return match cx.ccx().maps.root_map.find({id:e.id, derefs:0u}) {
-      // No need to root this lvalue.
-      None => unrooted(cx, e),
-
-      // Lvalue must remain rooted until exit of `scope_id`.  See
-      // add_root_cleanup() for comments on why this works the way it does.
-      Some(scope_id) => {
-        let lv = unrooted(cx, e);
-
-        if !cx.sess().no_asm_comments() {
-            add_comment(cx, fmt!("preserving until end of scope %d",
-                                 scope_id));
-        }
-
-        let _icx = lv.bcx.insn_ctxt("root_value_lval");
-        let ty = expr_ty(lv.bcx, e);
-        let root_loc = alloca_zeroed(lv.bcx, type_of(cx.ccx(), ty));
-        let bcx = store_temp_expr(lv.bcx, INIT, root_loc, lv, ty, false);
-        add_root_cleanup(bcx, scope_id, root_loc, ty);
-        {bcx: bcx,.. lv}
-      }
-    };
-
-    fn unrooted(cx: block, e: @ast::expr) -> lval_result {
-        let _icx = cx.insn_ctxt("trans_lval");
-        match e.node {
-          ast::expr_path(_) => {
-            let v = trans_path(cx, e.id);
-            return lval_maybe_callee_to_lval(v, e.span);
-          }
-          ast::expr_field(base, ident, _) => {
-            return trans_rec_field(cx, base, ident);
-          }
-          ast::expr_index(base, idx) => {
-            return trans_index(cx, e, base, idx);
-          }
-          ast::expr_unary(ast::deref, base) => {
-            let ccx = cx.ccx();
-            let sub = trans_temp_expr(cx, base);
-            let t = expr_ty(cx, base);
-            let val = match ty::get(t).struct {
-              ty::ty_box(_) => {
-                let non_gc_val = non_gc_box_cast(sub.bcx, sub.val);
-                GEPi(sub.bcx, non_gc_val, [0u, abi::box_field_body])
-              }
-              ty::ty_uniq(_) => {
-                let non_gc_val = non_gc_box_cast(sub.bcx, sub.val);
-                GEPi(sub.bcx, non_gc_val, [0u, abi::box_field_body])
-              }
-              ty::ty_enum(_, _) => {
-                let ety = expr_ty(cx, e);
-                let ellty = T_ptr(type_of(ccx, ety));
-                PointerCast(sub.bcx, sub.val, ellty)
-              }
-              ty::ty_ptr(_) | ty::ty_rptr(_,_) => sub.val,
-              _ => cx.sess().impossible_case(e.span, #fmt("unary operand \
-                may not have type %s", cx.ty_to_str(t)))
-            };
-            return lval_owned(sub.bcx, val);
-          }
-          _ => cx.sess().span_bug(e.span, ~"non-lval in trans_lval")
-        }
-    }
-}
-
-/**
- * Get the type of a box in the default address space.
- *
- * Shared box pointers live in address space 1 so the GC strategy can find
- * them. Before taking a pointer to the inside of a box it should be cast into
- * address space 0. Otherwise the resulting (non-box) pointer will be in the
- * wrong address space and thus be the wrong type.
- */
-fn non_gc_box_cast(cx: block, val: ValueRef) -> ValueRef {
-    debug!("non_gc_box_cast");
-    add_comment(cx, ~"non_gc_box_cast");
-    assert(llvm::LLVMGetPointerAddressSpace(val_ty(val)) == gc_box_addrspace);
-    let non_gc_t = T_ptr(llvm::LLVMGetElementType(val_ty(val)));
-    PointerCast(cx, val, non_gc_t)
-}
-
-fn lval_maybe_callee_to_lval(c: lval_maybe_callee, sp: span) -> lval_result {
-    match c.env {
-      self_env(*) => {
-        c.bcx.sess().span_bug(sp, ~"implicitly binding method call");
-      }
-      is_closure => { {bcx: c.bcx, val: c.val, kind: c.kind} }
-      null_env => {
-        let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
-        let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
-                                       null_env_ptr(c.bcx));
-        {bcx: c.bcx, val: llfn, kind: lv_temporary}
-      }
-    }
-}
-
-fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef,
-            llsrc: ValueRef, signed: bool) -> ValueRef {
-    let _icx = bcx.insn_ctxt("int_cast");
-    let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype);
-    let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype);
-    return if dstsz == srcsz {
-        BitCast(bcx, llsrc, lldsttype)
-    } else if srcsz > dstsz {
-        TruncOrBitCast(bcx, llsrc, lldsttype)
-    } else if signed {
-        SExtOrBitCast(bcx, llsrc, lldsttype)
-    } else { ZExtOrBitCast(bcx, llsrc, lldsttype) };
-}
-
-fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef,
-              llsrc: ValueRef) -> ValueRef {
-    let _icx = bcx.insn_ctxt("float_cast");
-    let srcsz = lib::llvm::float_width(llsrctype);
-    let dstsz = lib::llvm::float_width(lldsttype);
-    return if dstsz > srcsz {
-        FPExt(bcx, llsrc, lldsttype)
-    } else if srcsz > dstsz {
-        FPTrunc(bcx, llsrc, lldsttype)
-    } else { llsrc };
-}
-
-enum cast_kind {
-    cast_pointer,
-    cast_integral,
-    cast_float,
-    cast_enum,
-    cast_other,
-}
-
-impl cast_kind : cmp::Eq {
-    pure fn eq(&&other: cast_kind) -> bool {
-        match (self, other) {
-            (cast_pointer, cast_pointer) => true,
-            (cast_integral, cast_integral) => true,
-            (cast_float, cast_float) => true,
-            (cast_enum, cast_enum) => true,
-            (cast_other, cast_other) => true,
-            (cast_pointer, _) => false,
-            (cast_integral, _) => false,
-            (cast_float, _) => false,
-            (cast_enum, _) => false,
-            (cast_other, _) => false,
-        }
-    }
-}
-
-fn cast_type_kind(t: ty::t) -> cast_kind {
-    match ty::get(t).struct {
-      ty::ty_float(*)   => cast_float,
-      ty::ty_ptr(*)     => cast_pointer,
-      ty::ty_rptr(*)    => cast_pointer,
-      ty::ty_int(*)     => cast_integral,
-      ty::ty_uint(*)    => cast_integral,
-      ty::ty_bool       => cast_integral,
-      ty::ty_enum(*)    => cast_enum,
-      _                 => cast_other
-    }
-}
-
-
-fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id,
-              dest: dest) -> block {
-    let _icx = cx.insn_ctxt("trans_cast");
-    let ccx = cx.ccx();
-    let t_out = node_id_type(cx, id);
-    match ty::get(t_out).struct {
-      ty::ty_trait(_, _, _) => return impl::trans_cast(cx, e, id, dest),
-      _ => ()
-    }
-    let e_res = trans_temp_expr(cx, e);
-    let ll_t_in = val_ty(e_res.val);
-    let t_in = expr_ty(cx, e);
-    let ll_t_out = type_of(ccx, t_out);
-
-    let k_in = cast_type_kind(t_in);
-    let k_out = cast_type_kind(t_out);
-    let s_in = k_in == cast_integral && ty::type_is_signed(t_in);
-
-    let newval =
-        match {in: k_in, out: k_out} {
-          {in: cast_integral, out: cast_integral} => {
-            int_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val, s_in)
-          }
-          {in: cast_float, out: cast_float} => {
-            float_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val)
-          }
-          {in: cast_integral, out: cast_float} => {
-            if s_in {
-                SIToFP(e_res.bcx, e_res.val, ll_t_out)
-            } else { UIToFP(e_res.bcx, e_res.val, ll_t_out) }
-          }
-          {in: cast_float, out: cast_integral} => {
-            if ty::type_is_signed(t_out) {
-                FPToSI(e_res.bcx, e_res.val, ll_t_out)
-            } else { FPToUI(e_res.bcx, e_res.val, ll_t_out) }
-          }
-          {in: cast_integral, out: cast_pointer} => {
-            IntToPtr(e_res.bcx, e_res.val, ll_t_out)
-          }
-          {in: cast_pointer, out: cast_integral} => {
-            PtrToInt(e_res.bcx, e_res.val, ll_t_out)
-          }
-          {in: cast_pointer, out: cast_pointer} => {
-            PointerCast(e_res.bcx, e_res.val, ll_t_out)
-          }
-          {in: cast_enum, out: cast_integral} |
-          {in: cast_enum, out: cast_float} => {
-            let cx = e_res.bcx;
-            let llenumty = T_opaque_enum_ptr(ccx);
-            let av_enum = PointerCast(cx, e_res.val, llenumty);
-            let lldiscrim_a_ptr = GEPi(cx, av_enum, [0u, 0u]);
-            let lldiscrim_a = Load(cx, lldiscrim_a_ptr);
-            match k_out {
-              cast_integral => int_cast(e_res.bcx, ll_t_out,
-                                        val_ty(lldiscrim_a),
-                                        lldiscrim_a, true),
-              cast_float => SIToFP(e_res.bcx, lldiscrim_a, ll_t_out),
-              _ => ccx.sess.bug(~"translating unsupported cast.")
-            }
-          }
-          _ => ccx.sess.bug(~"translating unsupported cast.")
-        };
-    return store_in_dest(e_res.bcx, newval, dest);
-}
-
-fn trans_loop_body(bcx: block, id: ast::node_id,
-                   decl: ast::fn_decl, body: ast::blk,
-                   proto: ty::fn_proto, cap: ast::capture_clause,
-                   ret_flag: Option<ValueRef>,
-                   dest: dest) -> block {
-    closure::trans_expr_fn(bcx, proto, decl, body, id,
-                           cap, Some(ret_flag), dest)
-}
-
-// temp_cleanups: cleanups that should run only if failure occurs before the
-// call takes place:
-fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
-                  &temp_cleanups: ~[ValueRef], ret_flag: Option<ValueRef>,
-                  derefs: uint)
-    -> result {
-    let _icx = cx.insn_ctxt("trans_arg_expr");
-    let ccx = cx.ccx();
-    debug!("+++ trans_arg_expr on %s", expr_to_str(e, ccx.sess.intr()));
-    let e_ty = expr_ty(cx, e);
-    let is_bot = ty::type_is_bot(e_ty);
-
-    // translate the arg expr as an lvalue
-    let lv = match ret_flag {
-      // If there is a ret_flag, this *must* be a loop body
-      Some(_) => match e.node {
-          ast::expr_loop_body(blk@@{node:
-                  ast::expr_fn_block(decl, body, cap),_}) => {
-            let scratch = alloc_ty(cx, expr_ty(cx, blk));
-            let proto = match ty::get(expr_ty(cx, e)).struct {
-                ty::ty_fn({proto, _}) => proto,
-                _ => cx.sess().impossible_case(e.span, ~"Loop body has \
-                       non-fn ty")
-            };
-            let bcx = trans_loop_body(cx, blk.id, decl, body, proto,
-                                      cap, ret_flag, save_in(scratch));
-            {bcx: bcx, val: scratch, kind: lv_temporary}
-        }
-        _ => cx.sess().impossible_case(e.span, ~"ret_flag with non-loop-\
-              body expr")
-      },
-      None => {
-        trans_temp_lval(cx, e)
-      }
-    };
-
-    // auto-deref value as required (this only applies to method
-    // call receivers) of method
-    debug!("   pre-deref value: %s", val_str(lv.bcx.ccx().tn, lv.val));
-    let {lv, e_ty} = if derefs == 0u {
-      {lv: lv, e_ty: e_ty}
-    } else {
-      let {bcx, val} = lval_result_to_result(lv, e_ty);
-      let {bcx, val, ty: e_ty} =
-          autoderef(bcx, e.id, val, e_ty, derefs);
-      {lv: {bcx: bcx, val: val, kind: lv_temporary},
-       e_ty: e_ty}
-    };
-
-    // borrow value (convert from @T to &T and so forth)
-    debug!("   pre-adaptation value: %s", val_str(lv.bcx.ccx().tn, lv.val));
-    let {lv, ty: e_ty} = adapt_borrowed_value(lv, e, e_ty);
-    let mut bcx = lv.bcx;
-    let mut val = lv.val;
-    debug!("   adapted value: %s", val_str(bcx.ccx().tn, val));
-
-    // finally, deal with the various modes
-    let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode);
-    if is_bot {
-        // For values of type _|_, we generate an
-        // "undef" value, as such a value should never
-        // be inspected. It's important for the value
-        // to have type lldestty (the callee's expected type).
-        val = llvm::LLVMGetUndef(lldestty);
-    } else {
-        match arg_mode {
-          ast::by_ref | ast::by_mutbl_ref => {
-            // Ensure that the value is spilled into memory:
-            if lv.kind != lv_owned && ty::type_is_immediate(e_ty) {
-                val = do_spill_noroot(bcx, val);
-            }
-          }
-
-          ast::by_val => {
-            // Ensure that the value is not spilled into memory:
-            if lv.kind == lv_owned || !ty::type_is_immediate(e_ty) {
-                val = Load(bcx, val);
-            }
-          }
-
-          ast::by_copy | ast::by_move => {
-            // Ensure that an owned copy of the value is in memory:
-            let alloc = alloc_ty(bcx, e_ty);
-            let move_out = arg_mode == ast::by_move ||
-                ccx.maps.last_use_map.contains_key(e.id);
-            if lv.kind == lv_temporary { revoke_clean(bcx, val); }
-            if lv.kind == lv_owned || !ty::type_is_immediate(e_ty) {
-                memmove_ty(bcx, alloc, val, e_ty);
-                if move_out && ty::type_needs_drop(ccx.tcx, e_ty) {
-                    bcx = zero_mem(bcx, val, e_ty);
-                }
-            } else { Store(bcx, val, alloc); }
-            val = alloc;
-            if lv.kind != lv_temporary && !move_out {
-                bcx = take_ty(bcx, val, e_ty);
-            }
-
-            // In the event that failure occurs before the call actually
-            // happens, have to cleanup this copy:
-            add_clean_temp_mem(bcx, val, e_ty);
-            vec::push(temp_cleanups, val);
-          }
-        }
-    }
-
-    if !is_bot && arg.ty != e_ty || ty::type_has_params(arg.ty) {
-        debug!("   casting from %s", val_str(bcx.ccx().tn, val));
-        val = PointerCast(bcx, val, lldestty);
-    }
-
-    debug!("--- trans_arg_expr passing %s", val_str(bcx.ccx().tn, val));
-    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.
-//
-// NB: "e" has already been translated; do not translate it again. If you do,
-// this will cause problems with autoderef and method receivers (bug #3357).
-fn adapt_borrowed_value(lv: lval_result,
-                        e: @ast::expr,
-                        e_ty: ty::t) -> {lv: lval_result,
-                                         ty: ty::t} {
-    let bcx = lv.bcx;
-    if !expr_is_borrowed(bcx, e) {
-        return {lv:lv, ty:e_ty};
-    }
-
-    match ty::get(e_ty).struct {
-      ty::ty_uniq(mt) | ty::ty_box(mt) => {
-        let box_ptr = load_value_from_lval_result(lv, e_ty);
-        let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]);
-        let rptr_ty = ty::mk_rptr(bcx.tcx(), ty::re_static, mt);
-        return {lv: lval_temp(bcx, body_ptr), ty: rptr_ty};
-      }
-
-      ty::ty_estr(_) | ty::ty_evec(_, _) => {
-        let ccx = bcx.ccx();
-        let val = match lv.kind {
-          lv_temporary => lv.val,
-          lv_owned => load_if_immediate(bcx, lv.val, e_ty),
-          lv_owned_imm => lv.val
-        };
-
-        let unit_ty = ty::sequence_element_type(ccx.tcx, e_ty);
-        let llunit_ty = type_of(ccx, unit_ty);
-        let (base, len) = tvec::get_base_and_len(bcx, val, e_ty);
-        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 {lv: lval_temp(bcx, p), ty: slice_ty};
-      }
-
-      _ => {
-        // Just take a reference. This is basically like trans_addr_of.
-        let mut {bcx, val, kind} = lv;
-        let is_immediate = ty::type_is_immediate(e_ty);
-        if (kind == lv_temporary && is_immediate) || kind == lv_owned_imm {
-            val = do_spill(bcx, val, e_ty);
-        }
-        return {lv: {bcx: bcx, val: val, kind: lv_temporary},
-                ty: ty::mk_rptr(bcx.tcx(), ty::re_static,
-                                {ty: e_ty, mutbl: ast::m_imm})};
-      }
-    }
-}
-
-enum call_args {
-    arg_exprs(~[@ast::expr]),
-    arg_vals(~[ValueRef])
-}
-
-// NB: must keep 4 fns in sync:
-//
-//  - type_of_fn
-//  - create_llargs_for_fn_args.
-//  - new_fn_ctxt
-//  - trans_args
-fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t,
-              dest: dest, ret_flag: Option<ValueRef>)
-    -> {bcx: block, args: ~[ValueRef], retslot: ValueRef} {
-    let _icx = cx.insn_ctxt("trans_args");
-    let mut temp_cleanups = ~[];
-    let arg_tys = ty::ty_fn_args(fn_ty);
-    let mut llargs: ~[ValueRef] = ~[];
-
-    let ccx = cx.ccx();
-    let mut bcx = cx;
-
-    let retty = ty::ty_fn_ret(fn_ty);
-    // Arg 0: Output pointer.
-    let llretslot = match dest {
-      ignore => {
-        if ty::type_is_nil(retty) {
-            llvm::LLVMGetUndef(T_ptr(T_nil()))
-        } else { alloc_ty(bcx, retty) }
-      }
-      save_in(dst) => dst,
-      by_val(_) => alloc_ty(bcx, retty)
-    };
-
-    vec::push(llargs, llretslot);
-
-    // Arg 1: Env (closure-bindings / self value)
-    vec::push(llargs, llenv);
-
-    // ... then explicit args.
-
-    // First we figure out the caller's view of the types of the arguments.
-    // This will be needed if this is a generic call, because the callee has
-    // to cast her view of the arguments to the caller's view.
-    match args {
-      arg_exprs(es) => {
-        let llarg_tys = type_of_explicit_args(ccx, arg_tys);
-        let last = es.len() - 1u;
-        do vec::iteri(es) |i, e| {
-            let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i],
-                                   e, temp_cleanups, if i == last { ret_flag }
-                                   else { None }, 0u);
-            bcx = r.bcx;
-            vec::push(llargs, r.val);
-        }
-      }
-      arg_vals(vs) => {
-        vec::push_all(llargs, vs);
-      }
-    }
-
-    // now that all arguments have been successfully built, we can revoke any
-    // temporary cleanups, as they are only needed if argument construction
-    // should fail (for example, cleanup of copy mode args).
-    do vec::iter(temp_cleanups) |c| {
-        revoke_clean(bcx, c)
-    }
-
-    return {bcx: bcx,
-         args: llargs,
-         retslot: llretslot};
-}
-
-fn trans_call(in_cx: block, call_ex: @ast::expr, f: @ast::expr,
-              args: call_args, id: ast::node_id, dest: dest)
-    -> block {
-    let _icx = in_cx.insn_ctxt("trans_call");
-    trans_call_inner(
-        in_cx, call_ex.info(), expr_ty(in_cx, f), node_id_type(in_cx, id),
-        |cx| trans_callee(cx, f), args, dest)
-}
-
-fn body_contains_ret(body: ast::blk) -> bool {
-    let cx = {mut found: false};
-    visit::visit_block(body, cx, visit::mk_vt(@{
-        visit_item: |_i, _cx, _v| { },
-        visit_expr: |e: @ast::expr, cx: {mut found: bool}, v| {
-            if !cx.found {
-                match e.node {
-                  ast::expr_ret(_) => cx.found = true,
-                  _ => visit::visit_expr(e, cx, v),
-                }
-            }
-        } ,.. *visit::default_visitor()
-    }));
-    cx.found
-}
-
-// See [Note-arg-mode]
-fn trans_call_inner(
-    ++in_cx: block,
-    call_info: Option<node_info>,
-    fn_expr_ty: ty::t,
-    ret_ty: ty::t,
-    get_callee: fn(block) -> lval_maybe_callee,
-    args: call_args,
-    dest: dest) -> block {
-
-    do with_scope(in_cx, call_info, ~"call") |cx| {
-        let ret_in_loop = match args {
-          arg_exprs(args) => {
-            args.len() > 0u && match vec::last(args).node {
-              ast::expr_loop_body(@{
-                node: ast::expr_fn_block(_, body, _),
-                _
-              }) =>  body_contains_ret(body),
-              _ => false
-            }
-          }
-          _ => false
-        };
-
-        let f_res = get_callee(cx);
-        let mut bcx = f_res.bcx;
-        let ccx = cx.ccx();
-        let ret_flag = if ret_in_loop {
-            let flag = alloca(bcx, T_bool());
-            Store(bcx, C_bool(false), flag);
-            Some(flag)
-        } else { None };
-
-        let mut faddr = f_res.val;
-        let llenv = match f_res.env {
-          null_env => {
-            llvm::LLVMGetUndef(T_opaque_box_ptr(ccx))
-          }
-          self_env(e, _, _, _) => {
-            PointerCast(bcx, e, T_opaque_box_ptr(ccx))
-          }
-          is_closure => {
-            // It's a closure. Have to fetch the elements
-            if f_res.kind == lv_owned {
-                faddr = load_if_immediate(bcx, faddr, fn_expr_ty);
-            }
-            let pair = faddr;
-            faddr = GEPi(bcx, pair, [0u, abi::fn_field_code]);
-            faddr = Load(bcx, faddr);
-            let llclosure = GEPi(bcx, pair, [0u, abi::fn_field_box]);
-            Load(bcx, llclosure)
-          }
-        };
-
-        let args_res = {
-            trans_args(bcx, llenv, args, fn_expr_ty, dest, ret_flag)
-        };
-        bcx = args_res.bcx;
-        let mut llargs = args_res.args;
-
-        let llretslot = args_res.retslot;
-
-        // Now that the arguments have finished evaluating, we need to revoke
-        // the cleanup for the self argument, if it exists
-        match f_res.env {
-          self_env(e, _, _, ast::by_copy) => revoke_clean(bcx, e),
-          _ => (),
-        }
-
-        /* If the block is terminated,
-        then one or more of the args has
-        type _|_. Since that means it diverges, the code
-        for the call itself is unreachable. */
-        bcx = invoke(bcx, faddr, llargs);
-        match dest {
-          ignore => {
-            if llvm::LLVMIsUndef(llretslot) != lib::llvm::True {
-                bcx = drop_ty(bcx, llretslot, ret_ty);
-            }
-          }
-          save_in(_) => { } // Already saved by callee
-          by_val(cell) => {
-            *cell = Load(bcx, llretslot);
-          }
-        }
-        if ty::type_is_bot(ret_ty) {
-            Unreachable(bcx);
-        } else if ret_in_loop {
-            bcx = do with_cond(bcx, Load(bcx, option::get(ret_flag))) |bcx| {
-                do option::iter(copy bcx.fcx.loop_ret) |lret| {
-                    Store(bcx, C_bool(true), lret.flagptr);
-                    Store(bcx, C_bool(false), bcx.fcx.llretptr);
-                }
-                cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
-                Unreachable(bcx);
-                bcx
-            }
-        }
-        bcx
-    }
-}
-
 fn invoke(bcx: block, llfn: ValueRef, llargs: ~[ValueRef]) -> block {
     let _icx = bcx.insn_ctxt("invoke_");
     if bcx.unreachable { return bcx; }
@@ -3571,282 +865,6 @@ fn get_landing_pad(bcx: block) -> BasicBlockRef {
     return pad_bcx.llbb;
 }
 
-fn trans_tup(bcx: block, elts: ~[@ast::expr], dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_tup");
-    let mut bcx = bcx;
-    let addr = match dest {
-      ignore => {
-        for vec::each(elts) |ex| { bcx = trans_expr(bcx, ex, ignore); }
-        return bcx;
-      }
-      save_in(pos) => pos,
-      _ => bcx.tcx().sess.bug(~"trans_tup: weird dest")
-    };
-    let mut temp_cleanups = ~[];
-    for vec::eachi(elts) |i, e| {
-        let dst = GEPi(bcx, addr, [0u, i]);
-        let e_ty = expr_ty(bcx, e);
-        bcx = trans_expr_save_in(bcx, e, dst);
-        add_clean_temp_mem(bcx, dst, e_ty);
-        vec::push(temp_cleanups, dst);
-    }
-    for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, cleanup); }
-    return bcx;
-}
-
-fn trans_rec(bcx: block, fields: ~[ast::field],
-             base: Option<@ast::expr>, id: ast::node_id,
-             // none = ignore; some(x) = save_in(x)
-             dest: Option<ValueRef>) -> block {
-    let _icx = bcx.insn_ctxt("trans_rec");
-    let t = node_id_type(bcx, id);
-    let mut bcx = bcx;
-    let addr = match dest {
-      None => {
-        for vec::each(fields) |fld| {
-            bcx = trans_expr(bcx, fld.node.expr, ignore);
-        }
-        return bcx;
-      }
-      Some(pos) => pos
-    };
-
-    let ty_fields = match ty::get(t).struct {
-        ty::ty_rec(f) => f,
-        _ => bcx.sess().bug(~"trans_rec: record has non-record type")
-    };
-
-    let mut temp_cleanups = ~[];
-    for fields.each |fld| {
-        let ix = option::get(vec::position(ty_fields,
-            |ft| ft.ident == fld.node.ident));
-        let dst = GEPi(bcx, addr, [0u, ix]);
-        bcx = trans_expr_save_in(bcx, fld.node.expr, dst);
-        add_clean_temp_mem(bcx, dst, ty_fields[ix].mt.ty);
-        vec::push(temp_cleanups, dst);
-    }
-    match base {
-      Some(bexp) => {
-        let {bcx: cx, val: base_val} = trans_temp_expr(bcx, bexp);
-        bcx = cx;
-        // Copy over inherited fields
-        for ty_fields.eachi |i, tf| {
-            if !vec::any(fields, |f| f.node.ident == tf.ident) {
-                let dst = GEPi(bcx, addr, [0u, i]);
-                let base = GEPi(bcx, base_val, [0u, i]);
-                let val = load_if_immediate(bcx, base, tf.mt.ty);
-                bcx = copy_val(bcx, INIT, dst, val, tf.mt.ty);
-            }
-        }
-      }
-      None => ()
-    };
-
-    // Now revoke the cleanups as we pass responsibility for the data
-    // structure on to the caller
-    for temp_cleanups.each |cleanup| { revoke_clean(bcx, cleanup); }
-    return bcx;
-}
-
-// If the class has a destructor, our GEP is a little more
-// complicated.
-fn get_struct_field(block_context: block, dest_address: ValueRef,
-                    class_id: ast::def_id, index: uint) -> ValueRef {
-    if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
-        return GEPi(block_context,
-                    GEPi(block_context, dest_address, [0, 1]),
-                    [0, index]);
-    }
-    return GEPi(block_context, dest_address, [0, index]);
-}
-
-fn trans_struct(block_context: block, span: span, fields: ~[ast::field],
-                base: Option<@ast::expr>, id: ast::node_id, dest: dest)
-             -> block {
-
-    let _instruction_context = block_context.insn_ctxt("trans_struct");
-    let mut block_context = block_context;
-    let type_context = block_context.ccx().tcx;
-
-    let struct_type = node_id_type(block_context, id);
-
-    // Get the address to store the structure into. If there is no address,
-    // just translate each field and be done with it.
-    let dest_address;
-    match dest {
-        ignore => {
-            for fields.each |field| {
-                block_context = trans_expr(block_context,
-                                           field.node.expr,
-                                           ignore);
-            }
-            return block_context;
-        }
-        save_in(destination_address) => {
-            dest_address = destination_address;
-        }
-        by_val(_) => {
-            type_context.sess.span_bug(span, ~"didn't expect by_val");
-        }
-    }
-
-    // Get the class ID and its fields.
-    let class_fields, class_id, substitutions;
-    match ty::get(struct_type).struct {
-        ty::ty_class(existing_class_id, ref existing_substitutions) => {
-            class_id = existing_class_id;
-            substitutions = existing_substitutions;
-            class_fields = ty::lookup_class_fields(type_context, class_id);
-        }
-        _ => {
-            type_context.sess.span_bug(span, ~"didn't resolve to a struct");
-        }
-    }
-
-    // Add the drop flag if necessary.
-    if ty::ty_dtor(block_context.tcx(), class_id).is_some() {
-        let llflagptr = GEPi(block_context, dest_address, [0, 0]);
-        Store(block_context, C_u8(1), llflagptr);
-    }
-
-    // Now translate each field.
-    let mut temp_cleanups = ~[];
-    for fields.each |field| {
-        let mut found = None;
-        for class_fields.eachi |i, class_field| {
-            if class_field.ident == field.node.ident {
-                found = Some((i, class_field.id));
-                break;
-            }
-        }
-
-        let index, field_id;
-        match found {
-            Some((found_index, found_field_id)) => {
-                index = found_index;
-                field_id = found_field_id;
-            }
-            None => {
-                type_context.sess.span_bug(span, ~"unknown field");
-            }
-        }
-
-        let dest = get_struct_field(block_context, dest_address, class_id,
-                                    index);
-
-        block_context = trans_expr_save_in(block_context,
-                                           field.node.expr,
-                                           dest);
-
-        let field_type = ty::lookup_field_type(type_context, class_id,
-                                               field_id, substitutions);
-        add_clean_temp_mem(block_context, dest, field_type);
-        vec::push(temp_cleanups, dest);
-    }
-
-    match base {
-        Some(base_expr) => {
-            let { bcx: bcx, val: llbasevalue } =
-                trans_temp_expr(block_context, base_expr);
-            block_context = bcx;
-
-            // Copy over inherited fields.
-            for class_fields.eachi |i, class_field| {
-                let exists = do vec::any(fields) |provided_field| {
-                   provided_field.node.ident == class_field.ident
-                };
-                if exists {
-                    again;
-                }
-                let lldestfieldvalue = get_struct_field(block_context,
-                                                        dest_address,
-                                                        class_id,
-                                                        i);
-                let llbasefieldvalue = GEPi(block_context,
-                                            llbasevalue,
-                                            [0, i]);
-                let field_type = ty::lookup_field_type(block_context.tcx(),
-                                                       class_id,
-                                                       class_field.id,
-                                                       substitutions);
-                let llbasefieldvalue = load_if_immediate(block_context,
-                                                         llbasefieldvalue,
-                                                         field_type);
-                block_context = copy_val(block_context, INIT,
-                                         lldestfieldvalue, llbasefieldvalue,
-                                         field_type);
-            }
-        }
-        None => ()
-    }
-
-    // Now revoke the cleanups, as we pass responsibility for the data
-    // structure onto the caller.
-    for temp_cleanups.each |temp_cleanup| {
-        revoke_clean(block_context, temp_cleanup);
-    }
-
-    block_context
-}
-
-// Store the result of an expression in the given memory location, ensuring
-// that nil or bot expressions get ignore rather than save_in as destination.
-fn trans_expr_save_in(bcx: block, e: @ast::expr, dest: ValueRef)
-    -> block {
-    let t = expr_ty(bcx, e);
-    let do_ignore = ty::type_is_bot(t) || ty::type_is_nil(t);
-    return trans_expr(bcx, e, if do_ignore { ignore } else { save_in(dest) });
-}
-
-// Call this to compile an expression that you need as an intermediate value,
-// and you want to know whether you're dealing with an lval or not (the kind
-// field in the returned struct). For non-intermediates, use trans_expr or
-// trans_expr_save_in. For intermediates where you don't care about lval-ness,
-// use trans_temp_expr.
-fn trans_temp_lval(bcx: block, e: @ast::expr) -> lval_result {
-    let _icx = bcx.insn_ctxt("trans_temp_lval");
-    let mut bcx = bcx;
-    if expr_is_lval(bcx, e) {
-        return trans_lval(bcx, e);
-    } else {
-        let ty = expr_ty(bcx, e);
-        if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
-            bcx = trans_expr(bcx, e, ignore);
-            return {bcx: bcx, val: C_nil(), kind: lv_temporary};
-        } else if ty::type_is_immediate(ty) {
-            let cell = empty_dest_cell();
-            bcx = trans_expr(bcx, e, by_val(cell));
-            add_clean_temp_immediate(bcx, *cell, ty);
-            return {bcx: bcx, val: *cell, kind: lv_temporary};
-        } else {
-            let scratch = alloc_ty(bcx, ty);
-            let bcx = trans_expr_save_in(bcx, e, scratch);
-            add_clean_temp_mem(bcx, scratch, ty);
-            return {bcx: bcx, val: scratch, kind: lv_temporary};
-        }
-    }
-}
-
-// Use only for intermediate values. See trans_expr and trans_expr_save_in for
-// expressions that must 'end up somewhere' (or get ignored).
-fn trans_temp_expr(bcx: block, e: @ast::expr) -> result {
-    let _icx = bcx.insn_ctxt("trans_temp_expr");
-    lval_result_to_result(trans_temp_lval(bcx, e), expr_ty(bcx, e))
-}
-
-fn load_value_from_lval_result(lv: lval_result, ty: ty::t) -> ValueRef {
-    match lv.kind {
-      lv_temporary => lv.val,
-      lv_owned => load_if_immediate(lv.bcx, lv.val, ty),
-      lv_owned_imm => lv.val
-    }
-}
-
-fn lval_result_to_result(lv: lval_result, ty: ty::t) -> result {
-    let val = load_value_from_lval_result(lv, ty);
-    {bcx: lv.bcx, val: val}
-}
-
 // Arranges for the value found in `*root_loc` to be dropped once the scope
 // associated with `scope_id` exits.  This is used to keep boxes live when
 // there are extant region pointers pointing at the interior.
@@ -3886,297 +904,6 @@ fn add_root_cleanup(bcx: block, scope_id: ast::node_id,
     }
 }
 
-// Translate an expression, with the dest argument deciding what happens with
-// the result. Invariants:
-// - exprs returning nil or bot always get dest=ignore
-// - exprs with non-immediate type never get dest=by_val
-fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_expr");
-    debuginfo::update_source_pos(bcx, e.span);
-
-    if expr_is_lval(bcx, e) {
-        return lval_to_dps(bcx, e, dest);
-    }
-
-    return match bcx.ccx().maps.root_map.find({id:e.id, derefs:0u}) {
-      None => unrooted(bcx, e, dest),
-      Some(scope_id) => {
-        debug!("expression %d found in root map with scope %d",
-               e.id, scope_id);
-
-        let ty = expr_ty(bcx, e);
-        let root_loc = alloca_zeroed(bcx, type_of(bcx.ccx(), ty));
-        let bcx = unrooted(bcx, e, save_in(root_loc));
-
-        if !bcx.sess().no_asm_comments() {
-            add_comment(bcx, fmt!("preserving until end of scope %d",
-                                  scope_id));
-        }
-
-        let _icx = bcx.insn_ctxt("root_value_expr");
-        add_root_cleanup(bcx, scope_id, root_loc, ty);
-        let lv = {bcx: bcx, val: root_loc, kind: lv_owned};
-        lval_result_to_dps(lv, ty, false, dest)
-      }
-    };
-
-    fn unrooted(bcx: block, e: @ast::expr, dest: dest) -> block {
-        let tcx = bcx.tcx();
-        match e.node {
-          ast::expr_if(cond, thn, els) => {
-            return trans_if(bcx, cond, thn, els, dest);
-          }
-          ast::expr_match(expr, arms) => {
-            return alt::trans_alt(bcx, e, expr, arms, dest);
-          }
-          ast::expr_block(blk) => {
-            return do with_scope(bcx, blk.info(), ~"block-expr body") |bcx| {
-                trans_block(bcx, blk, dest)
-            };
-          }
-          ast::expr_rec(args, base) => {
-              let d = match dest {
-                  ignore => None,
-                  save_in(p) => Some(p),
-                  _ => bcx.sess().impossible_case(e.span,
-                        "trans_expr::unrooted: can't pass a record by val")
-              };
-            return trans_rec(bcx, args, base, e.id, d);
-          }
-          ast::expr_struct(_, fields, base) => {
-            return trans_struct(bcx, e.span, fields, base, e.id, dest);
-          }
-          ast::expr_tup(args) => { return trans_tup(bcx, args, dest); }
-          ast::expr_vstore(e, v) => {
-            return tvec::trans_vstore(bcx, e, v, dest);
-          }
-          ast::expr_lit(lit) => return trans_lit(bcx, e, *lit, dest),
-          ast::expr_vec(args, _) => {
-            return tvec::trans_evec(bcx, tvec::individual_evec(args),
-                                    ast::vstore_fixed(None), e.id, dest);
-          }
-          ast::expr_repeat(element, count_expr, _) => {
-            let count = ty::eval_repeat_count(bcx.tcx(), count_expr, e.span);
-            return tvec::trans_evec(bcx, tvec::repeating_evec(element, count),
-                                    ast::vstore_fixed(None), e.id, dest);
-          }
-          ast::expr_binary(op, lhs, rhs) => {
-            return trans_binary(bcx, op, lhs, rhs, dest, e);
-          }
-          ast::expr_unary(op, x) => {
-            assert op != ast::deref; // lvals are handled above
-            return trans_unary(bcx, op, x, e, dest);
-          }
-          ast::expr_addr_of(_, x) => { return trans_addr_of(bcx, x, dest); }
-          ast::expr_fn(proto, decl, body, cap_clause) => {
-            // Don't use this function for anything real. Use the one in
-            // astconv instead.
-            fn ast_proto_to_proto_simple(ast_proto: ast::proto)
-                                      -> ty::fn_proto {
-                match ast_proto {
-                    ast::proto_bare =>
-                        ty::proto_bare,
-                    ast::proto_uniq =>
-                        ty::proto_vstore(ty::vstore_uniq),
-                    ast::proto_box =>
-                        ty::proto_vstore(ty::vstore_box),
-                    ast::proto_block =>
-                        ty::proto_vstore(ty::vstore_slice(ty::re_static))
-                }
-            }
-
-            // XXX: This syntax should be reworked a bit (in the parser I
-            // guess?); @fn() { ... } won't work.
-            return closure::trans_expr_fn(bcx,
-                                          ast_proto_to_proto_simple(proto),
-                                          decl, body, e.id, cap_clause, None,
-                                          dest);
-          }
-          ast::expr_fn_block(decl, body, cap_clause) => {
-            match ty::get(expr_ty(bcx, e)).struct {
-              ty::ty_fn({proto, _}) => {
-                debug!("translating fn_block %s with type %s",
-                       expr_to_str(e, tcx.sess.intr()),
-                       ppaux::ty_to_str(tcx, expr_ty(bcx, e)));
-                return closure::trans_expr_fn(bcx, proto, decl, body,
-                                           e.id, cap_clause, None, dest);
-              }
-              _ =>  bcx.sess().impossible_case(e.span, "fn_block has \
-                         body with a non-fn type")
-            }
-          }
-          ast::expr_loop_body(blk) => {
-              match ty::get(expr_ty(bcx, e)).struct {
-                  ty::ty_fn({proto, _}) => {
-                      match blk.node {
-                          ast::expr_fn_block(decl, body, cap) =>
-                            return trans_loop_body(bcx, blk.id, decl, body,
-                                                   proto, cap, None, dest),
-                          _ => bcx.sess().impossible_case(e.span, "loop_body \
-                                 has the wrong kind of contents")
-                      }
-
-                  }
-                  _ => bcx.sess().impossible_case(e.span, "loop_body has \
-                         body with a non-fn type")
-              }
-          }
-          ast::expr_do_body(blk) => {
-            return trans_expr(bcx, blk, dest);
-          }
-          ast::expr_copy(a) | ast::expr_unary_move(a) => {
-            if !expr_is_lval(bcx, a) {
-                return trans_expr(bcx, a, dest);
-            }
-            else { return lval_to_dps(bcx, a, dest); }
-          }
-          ast::expr_cast(val, _) => return trans_cast(bcx, val, e.id, dest),
-          ast::expr_call(f, args, _) => {
-            return trans_call(bcx, e, f, arg_exprs(args), e.id, dest);
-          }
-          ast::expr_field(base, _, _) => {
-            if dest == ignore { return trans_expr(bcx, base, ignore); }
-            let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e);
-            let lv = lval_maybe_callee_to_lval(callee, e.span);
-            revoke_clean(lv.bcx, lv.val);
-            memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty);
-            return lv.bcx;
-          }
-          ast::expr_index(base, idx) => {
-            // If it is here, it's not an lval, so this is a user-defined
-            // index op
-            let origin = bcx.ccx().maps.method_map.get(e.id);
-            let fty = node_id_type(bcx, e.callee_id);
-            return trans_call_inner(
-                bcx, e.info(), fty,
-                expr_ty(bcx, e),
-                |bcx| impl::trans_method_callee(bcx, e.callee_id, base,
-                                                origin),
-                arg_exprs(~[idx]), dest);
-          }
-
-          // These return nothing
-          ast::expr_break(label_opt) => {
-            assert dest == ignore;
-            if label_opt.is_some() {
-                bcx.tcx().sess.span_unimpl(e.span, ~"labeled break");
-            }
-            return trans_break(bcx);
-          }
-          ast::expr_again(label_opt) => {
-            assert dest == ignore;
-            if label_opt.is_some() {
-                bcx.tcx().sess.span_unimpl(e.span, ~"labeled again");
-            }
-            return trans_cont(bcx);
-          }
-          ast::expr_ret(ex) => {
-            assert dest == ignore;
-            return trans_ret(bcx, ex);
-          }
-          ast::expr_fail(expr) => {
-            assert dest == ignore;
-            return trans_fail_expr(bcx, Some(e.span), expr);
-          }
-          ast::expr_log(_, lvl, a) => {
-            assert dest == ignore;
-            return trans_log(e, lvl, bcx, a);
-          }
-          ast::expr_assert(a) => {
-            assert dest == ignore;
-            return trans_check_expr(bcx, e, a, ~"Assertion");
-          }
-          ast::expr_while(cond, body) => {
-            assert dest == ignore;
-            return trans_while(bcx, cond, body);
-          }
-          ast::expr_loop(body, _) => {
-            assert dest == ignore;
-            return trans_loop(bcx, body);
-          }
-          ast::expr_assign(dst, src) => {
-            assert dest == ignore;
-            let src_r = trans_temp_lval(bcx, src);
-            let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst);
-            assert kind == lv_owned;
-            let is_last_use =
-                bcx.ccx().maps.last_use_map.contains_key(src.id);
-            return store_temp_expr(bcx, DROP_EXISTING, addr, src_r,
-                                expr_ty(bcx, src), is_last_use);
-          }
-          ast::expr_move(dst, src) => {
-            // FIXME: calculate copy init-ness in typestate. (#839)
-            assert dest == ignore;
-            let src_r = trans_temp_lval(bcx, src);
-            let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst);
-            assert kind == lv_owned;
-            return move_val(bcx, DROP_EXISTING, addr, src_r,
-                         expr_ty(bcx, src));
-          }
-          ast::expr_swap(dst, src) => {
-            assert dest == ignore;
-            let lhs_res = trans_lval(bcx, dst);
-            assert lhs_res.kind == lv_owned;
-            let rhs_res = trans_lval(lhs_res.bcx, src);
-            let t = expr_ty(bcx, src);
-            let tmp_alloc = alloc_ty(rhs_res.bcx, t);
-            // Swap through a temporary.
-            let bcx = move_val(rhs_res.bcx, INIT, tmp_alloc, lhs_res, t);
-            let bcx = move_val(bcx, INIT, lhs_res.val, rhs_res, t);
-            return move_val(bcx, INIT, rhs_res.val,
-                         lval_owned(bcx, tmp_alloc), t);
-          }
-          ast::expr_assign_op(op, dst, src) => {
-            assert dest == ignore;
-            return trans_assign_op(bcx, e, op, dst, src);
-          }
-          _ => {
-            bcx.tcx().sess.span_bug(e.span, ~"trans_expr reached \
-                                             fall-through case");
-          }
-        }
-    }
-}
-
-fn lval_to_dps(bcx: block, e: @ast::expr, dest: dest) -> block {
-    let last_use_map = bcx.ccx().maps.last_use_map;
-    let ty = expr_ty(bcx, e);
-    let lv = trans_lval(bcx, e);
-    let last_use = (lv.kind == lv_owned && last_use_map.contains_key(e.id));
-    debug!("is last use (%s) = %b, %d", expr_to_str(e, bcx.ccx().sess.intr()),
-           last_use, lv.kind as int);
-    lval_result_to_dps(lv, ty, last_use, dest)
-}
-
-fn lval_result_to_dps(lv: lval_result, ty: ty::t,
-                      last_use: bool, dest: dest) -> block {
-    let mut {bcx, val, kind} = lv;
-    let ccx = bcx.ccx();
-    match dest {
-      by_val(cell) => {
-        if kind == lv_temporary {
-            revoke_clean(bcx, val);
-            *cell = val;
-        } else if last_use {
-            *cell = Load(bcx, val);
-            if ty::type_needs_drop(ccx.tcx, ty) {
-                bcx = zero_mem(bcx, val, ty);
-            }
-        } else {
-            if kind == lv_owned { val = Load(bcx, val); }
-            let {bcx: cx, val} = take_ty_immediate(bcx, val, ty);
-            *cell = val;
-            bcx = cx;
-        }
-      }
-      save_in(loc) => {
-        bcx = store_temp_expr(bcx, INIT, loc, lv, ty, last_use);
-      }
-      ignore => ()
-    }
-    return bcx;
-}
-
 fn do_spill(bcx: block, v: ValueRef, t: ty::t) -> ValueRef {
     if ty::type_is_bot(t) {
         return C_null(T_ptr(T_i8()));
@@ -4209,99 +936,6 @@ fn load_if_immediate(cx: block, v: ValueRef, t: ty::t) -> ValueRef {
     return v;
 }
 
-fn trans_log(log_ex: @ast::expr, lvl: @ast::expr,
-             bcx: block, e: @ast::expr) -> block {
-    let _icx = bcx.insn_ctxt("trans_log");
-    let ccx = bcx.ccx();
-    if ty::type_is_bot(expr_ty(bcx, lvl)) {
-       return trans_expr(bcx, lvl, ignore);
-    }
-
-    let modpath = vec::append(
-        ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))],
-        vec::filter(bcx.fcx.path, |e|
-            match e { path_mod(_) => true, _ => false }
-        ));
-    let modname = path_str(ccx.sess, modpath);
-
-    let global = if ccx.module_data.contains_key(modname) {
-        ccx.module_data.get(modname)
-    } else {
-        let s = link::mangle_internal_name_by_path_and_seq(
-            ccx, modpath, ~"loglevel");
-        let global = str::as_c_str(s, |buf| {
-            llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf)
-        });
-        llvm::LLVMSetGlobalConstant(global, False);
-        llvm::LLVMSetInitializer(global, C_null(T_i32()));
-        lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage);
-        ccx.module_data.insert(modname, global);
-        global
-    };
-    let current_level = Load(bcx, global);
-    let {bcx, val: level} = {
-        do with_scope_result(bcx, lvl.info(), ~"level") |bcx| {
-            trans_temp_expr(bcx, lvl)
-        }
-    };
-
-    do with_cond(bcx, ICmp(bcx, lib::llvm::IntUGE, current_level, level))
-        |bcx| {
-        do with_scope(bcx, log_ex.info(), ~"log") |bcx| {
-            let {bcx, val, _} = trans_temp_expr(bcx, e);
-            let e_ty = expr_ty(bcx, e);
-            let tydesc = get_tydesc_simple(ccx, e_ty);
-            // Call the polymorphic log function.
-            let val = spill_if_immediate(bcx, val, e_ty);
-            let val = PointerCast(bcx, val, T_ptr(T_i8()));
-            Call(bcx, ccx.upcalls.log_type, ~[tydesc, val, level]);
-            bcx
-        }
-    }
-}
-
-fn trans_check_expr(bcx: block, chk_expr: @ast::expr,
-                    pred_expr: @ast::expr, s: ~str) -> block {
-    let _icx = bcx.insn_ctxt("trans_check_expr");
-    let expr_str = s + ~" " + expr_to_str(pred_expr, bcx.ccx().sess.intr())
-        + ~" failed";
-    let {bcx, val} = {
-        do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| {
-            trans_temp_expr(bcx, pred_expr)
-        }
-    };
-    do with_cond(bcx, Not(bcx, val)) |bcx| {
-        trans_fail(bcx, Some(pred_expr.span), expr_str)
-    }
-}
-
-fn trans_fail_expr(bcx: block, sp_opt: Option<span>,
-                   fail_expr: Option<@ast::expr>) -> block {
-    let _icx = bcx.insn_ctxt("trans_fail_expr");
-    let mut bcx = bcx;
-    match fail_expr {
-      Some(expr) => {
-        let ccx = bcx.ccx(), tcx = ccx.tcx;
-        let expr_res = trans_temp_expr(bcx, expr);
-        let e_ty = expr_ty(bcx, expr);
-        bcx = expr_res.bcx;
-
-        if ty::type_is_str(e_ty) {
-            let body = tvec::get_bodyptr(bcx, expr_res.val);
-            let data = tvec::get_dataptr(bcx, body);
-            return trans_fail_value(bcx, sp_opt, data);
-        } else if bcx.unreachable || ty::type_is_bot(e_ty) {
-            return bcx;
-        } else {
-            bcx.sess().span_bug(
-                expr.span, ~"fail called with unsupported type " +
-                ppaux::ty_to_str(tcx, e_ty));
-        }
-      }
-      _ => return trans_fail(bcx, sp_opt, ~"explicit failure")
-    }
-}
-
 fn trans_trace(bcx: block, sp_opt: Option<span>, trace_str: ~str) {
     if !bcx.sess().trace() { return; }
     let _icx = bcx.insn_ctxt("trans_trace");
@@ -4326,123 +960,6 @@ fn trans_trace(bcx: block, sp_opt: Option<span>, trace_str: ~str) {
     Call(bcx, ccx.upcalls.trace, args);
 }
 
-fn trans_fail(bcx: block, sp_opt: Option<span>, fail_str: ~str) ->
-    block {
-    let _icx = bcx.insn_ctxt("trans_fail");
-    let V_fail_str = C_cstr(bcx.ccx(), fail_str);
-    return trans_fail_value(bcx, sp_opt, V_fail_str);
-}
-
-fn trans_fail_value(bcx: block, sp_opt: Option<span>,
-                    V_fail_str: ValueRef) -> block {
-    let _icx = bcx.insn_ctxt("trans_fail_value");
-    let ccx = bcx.ccx();
-    let {V_filename, V_line} = match sp_opt {
-      Some(sp) => {
-        let sess = bcx.sess();
-        let loc = codemap::lookup_char_pos(sess.parse_sess.cm, sp.lo);
-        {V_filename: C_cstr(bcx.ccx(), loc.file.name),
-         V_line: loc.line as int}
-      }
-      None => {
-        {V_filename: C_cstr(bcx.ccx(), ~"<runtime>"),
-         V_line: 0}
-      }
-    };
-    let V_str = PointerCast(bcx, V_fail_str, T_ptr(T_i8()));
-    let V_filename = PointerCast(bcx, V_filename, T_ptr(T_i8()));
-    let args = ~[V_str, V_filename, C_int(ccx, V_line)];
-    let bcx = trans_rtcall(bcx, ~"fail", args, ignore);
-    Unreachable(bcx);
-    return bcx;
-}
-
-fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: dest)
-    -> block {
-    let did = bcx.ccx().rtcalls[name];
-    let fty = if did.crate == ast::local_crate {
-        ty::node_id_to_type(bcx.ccx().tcx, did.node)
-    } else {
-        csearch::get_type(bcx.ccx().tcx, did).ty
-    };
-    let rty = ty::ty_fn_ret(fty);
-    return trans_call_inner(
-        bcx, None, fty, rty,
-        |bcx| lval_static_fn_inner(bcx, did, 0, ~[], None),
-        arg_vals(args), dest);
-}
-
-fn trans_break_cont(bcx: block, to_end: bool)
-    -> block {
-    let _icx = bcx.insn_ctxt("trans_break_cont");
-    // Locate closest loop block, outputting cleanup as we go.
-    let mut unwind = bcx;
-    let mut target;
-    loop {
-        match unwind.kind {
-          block_scope({loop_break: Some(brk), _}) => {
-            target = if to_end {
-                brk
-            } else {
-                unwind
-            };
-            break;
-          }
-          _ => ()
-        }
-        unwind = match unwind.parent {
-          Some(cx) => cx,
-          // This is a return from a loop body block
-          None => {
-            Store(bcx, C_bool(!to_end), bcx.fcx.llretptr);
-            cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
-            Unreachable(bcx);
-            return bcx;
-          }
-        };
-    }
-    cleanup_and_Br(bcx, unwind, target.llbb);
-    Unreachable(bcx);
-    return bcx;
-}
-
-fn trans_break(cx: block) -> block {
-    return trans_break_cont(cx, true);
-}
-
-fn trans_cont(cx: block) -> block {
-    return trans_break_cont(cx, false);
-}
-
-fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block {
-    let _icx = bcx.insn_ctxt("trans_ret");
-    let mut bcx = bcx;
-    let retptr = match copy bcx.fcx.loop_ret {
-      Some({flagptr, retptr}) => {
-        // This is a loop body return. Must set continue flag (our retptr)
-        // to false, return flag to true, and then store the value in the
-        // parent's retptr.
-        Store(bcx, C_bool(true), flagptr);
-        Store(bcx, C_bool(false), bcx.fcx.llretptr);
-        match e {
-          Some(x) => PointerCast(bcx, retptr,
-                                 T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))),
-          None => retptr
-        }
-      }
-      None => bcx.fcx.llretptr
-    };
-    match e {
-      Some(x) => {
-        bcx = trans_expr_save_in(bcx, x, retptr);
-      }
-      _ => ()
-    }
-    cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
-    Unreachable(bcx);
-    return bcx;
-}
-
 fn build_return(bcx: block) {
     let _icx = bcx.insn_ctxt("build_return");
     Br(bcx, bcx.fcx.llreturn);
@@ -4455,14 +972,21 @@ fn ignore_lhs(_bcx: block, local: @ast::local) -> bool {
 }
 
 fn init_local(bcx: block, local: @ast::local) -> block {
+
+    debug!("init_local(bcx=%s, local.id=%?)",
+           bcx.to_str(), local.node.id);
+    let _indenter = indenter();
+
     let _icx = bcx.insn_ctxt("init_local");
     let ty = node_id_type(bcx, local.node.id);
 
+    debug!("ty=%s", bcx.ty_to_str(ty));
+
     if ignore_lhs(bcx, local) {
         // Handle let _ = e; just like e;
         match local.node.init {
             Some(init) => {
-              return trans_expr(bcx, init.expr, ignore);
+              return expr::trans_into(bcx, init.expr, expr::Ignore);
             }
             None => { return bcx; }
         }
@@ -4478,18 +1002,25 @@ fn init_local(bcx: block, local: @ast::local) -> block {
 
     let mut bcx = bcx;
     match local.node.init {
-      Some(init) => {
-        if init.op == ast::init_assign || !expr_is_lval(bcx, init.expr) {
-            bcx = trans_expr_save_in(bcx, init.expr, llptr);
-        } else { // This is a move from an lval, must perform an actual move
-            let sub = trans_lval(bcx, init.expr);
-            bcx = move_val(sub.bcx, INIT, llptr, sub, ty);
+        Some(init) => {
+            if init.op == ast::init_assign || !bcx.expr_is_lval(init.expr) {
+                bcx = expr::trans_into(bcx, init.expr, expr::SaveIn(llptr));
+            } else { // This is a move from an lval, perform an actual move
+                let init_datumblock = expr::trans_to_datum(bcx, init.expr);
+                bcx = init_datumblock.move_to(datum::INIT, llptr);
+            }
+        }
+        _ => {
+            zero_mem(bcx, llptr, ty);
         }
-      }
-      _ => bcx = zero_mem(bcx, llptr, ty),
     }
+
     // Make a note to drop this slot on the way out.
+    debug!("adding clean for %?/%s to bcx=%s",
+           local.node.id, bcx.ty_to_str(ty),
+           bcx.to_str());
     add_clean(bcx, llptr, ty);
+
     return alt::bind_irrefutable_pat(bcx, local.node.pat, llptr, false);
 }
 
@@ -4505,22 +1036,22 @@ fn trans_stmt(cx: block, s: ast::stmt) -> block {
     debuginfo::update_source_pos(cx, s.span);
 
     match s.node {
-      ast::stmt_expr(e, _) | ast::stmt_semi(e, _) => {
-        bcx = trans_expr(cx, e, ignore);
-      }
-      ast::stmt_decl(d, _) => {
-        match d.node {
-          ast::decl_local(locals) => {
-            for vec::each(locals) |local| {
-                bcx = init_local(bcx, local);
-                if cx.sess().opts.extra_debuginfo {
-                    debuginfo::create_local_var(bcx, local);
+        ast::stmt_expr(e, _) | ast::stmt_semi(e, _) => {
+            bcx = expr::trans_into(cx, e, expr::Ignore);
+        }
+        ast::stmt_decl(d, _) => {
+            match d.node {
+                ast::decl_local(locals) => {
+                    for vec::each(locals) |local| {
+                        bcx = init_local(bcx, local);
+                        if cx.sess().opts.extra_debuginfo {
+                            debuginfo::create_local_var(bcx, local);
+                        }
+                    }
                 }
+                ast::decl_item(i) => trans_item(cx.fcx.ccx, *i)
             }
-          }
-          ast::decl_item(i) => trans_item(cx.fcx.ccx, *i)
         }
-      }
     }
 
     return bcx;
@@ -4687,28 +1218,38 @@ fn leave_block(bcx: block, out_of: block) -> block {
 fn with_scope(bcx: block, opt_node_info: Option<node_info>,
               name: ~str, f: fn(block) -> block) -> block {
     let _icx = bcx.insn_ctxt("with_scope");
+
+    debug!("with_scope(bcx=%s, opt_node_info=%?, name=%s)",
+           bcx.to_str(), opt_node_info, name);
+    let _indenter = indenter();
+
     let scope_cx = scope_block(bcx, opt_node_info, name);
     Br(bcx, scope_cx.llbb);
     leave_block(f(scope_cx), scope_cx)
 }
 
 fn with_scope_result(bcx: block, opt_node_info: Option<node_info>,
-                     name: ~str, f: fn(block) -> result)
-    -> result {
+                     name: ~str, f: fn(block) -> Result)
+    -> Result
+{
     let _icx = bcx.insn_ctxt("with_scope_result");
     let scope_cx = scope_block(bcx, opt_node_info, name);
     Br(bcx, scope_cx.llbb);
-    let {bcx, val} = f(scope_cx);
-    {bcx: leave_block(bcx, scope_cx), val: val}
+    let Result {bcx, val} = f(scope_cx);
+    rslt(leave_block(bcx, scope_cx), val)
 }
 
-fn with_cond(bcx: block, val: ValueRef, f: fn(block) -> block) -> block {
-    let _icx = bcx.insn_ctxt("with_cond");
-    let next_cx = sub_block(bcx, ~"next"), cond_cx = sub_block(bcx, ~"cond");
-    CondBr(bcx, val, cond_cx.llbb, next_cx.llbb);
-    let after_cx = f(cond_cx);
-    if !after_cx.terminated { Br(after_cx, next_cx.llbb); }
-    next_cx
+fn with_scope_datumblock(bcx: block, opt_node_info: Option<node_info>,
+                         name: ~str, f: fn(block) -> datum::DatumBlock)
+    -> datum::DatumBlock
+{
+    import datum::DatumBlock;
+
+    let _icx = bcx.insn_ctxt("with_scope_result");
+    let scope_cx = scope_block(bcx, opt_node_info, name);
+    Br(bcx, scope_cx.llbb);
+    let DatumBlock {bcx, datum} = f(scope_cx);
+    DatumBlock {bcx: leave_block(bcx, scope_cx), datum: datum}
 }
 
 fn block_locals(b: ast::blk, it: fn(@ast::local)) {
@@ -4727,16 +1268,6 @@ fn block_locals(b: ast::blk, it: fn(@ast::local)) {
     }
 }
 
-fn alloc_ty(bcx: block, t: ty::t) -> ValueRef {
-    let _icx = bcx.insn_ctxt("alloc_ty");
-    let ccx = bcx.ccx();
-    let llty = type_of(ccx, t);
-    if ty::type_has_params(t) { log(error, ppaux::ty_to_str(ccx.tcx, t)); }
-    assert !ty::type_has_params(t);
-    let val = alloca(bcx, llty);
-    return val;
-}
-
 fn alloc_local(cx: block, local: @ast::local) -> block {
     let _icx = cx.insn_ctxt("alloc_local");
     let t = node_id_type(cx, local.node.id);
@@ -4756,24 +1287,117 @@ fn alloc_local(cx: block, local: @ast::local) -> block {
     return cx;
 }
 
-fn trans_block(bcx: block, b: ast::blk, dest: dest)
-    -> block {
-    let _icx = bcx.insn_ctxt("trans_block");
-    let mut bcx = bcx;
-    do block_locals(b) |local| { bcx = alloc_local(bcx, local); };
-    for vec::each(b.node.stmts) |s| {
-        debuginfo::update_source_pos(bcx, b.span);
-        bcx = trans_stmt(bcx, *s);
+
+fn with_cond(bcx: block, val: ValueRef, f: fn(block) -> block) -> block {
+    let _icx = bcx.insn_ctxt("with_cond");
+    let next_cx = base::sub_block(bcx, ~"next");
+    let cond_cx = base::sub_block(bcx, ~"cond");
+    CondBr(bcx, val, cond_cx.llbb, next_cx.llbb);
+    let after_cx = f(cond_cx);
+    if !after_cx.terminated { Br(after_cx, next_cx.llbb); }
+    next_cx
+}
+
+fn call_memmove(cx: block, dst: ValueRef, src: ValueRef,
+                n_bytes: ValueRef) {
+    // FIXME (Related to #1645, I think?): Provide LLVM with better
+    // alignment information when the alignment is statically known (it must
+    // be nothing more than a constant int, or LLVM complains -- not even a
+    // constant element of a tydesc works).
+    let _icx = cx.insn_ctxt("call_memmove");
+    let ccx = cx.ccx();
+    let key = match ccx.sess.targ_cfg.arch {
+      session::arch_x86 | session::arch_arm => ~"llvm.memmove.p0i8.p0i8.i32",
+      session::arch_x86_64 => ~"llvm.memmove.p0i8.p0i8.i64"
+    };
+    let memmove = ccx.intrinsics.get(key);
+    let src_ptr = PointerCast(cx, src, T_ptr(T_i8()));
+    let dst_ptr = PointerCast(cx, dst, T_ptr(T_i8()));
+    let size = IntCast(cx, n_bytes, ccx.int_type);
+    let align = C_i32(1i32);
+    let volatile = C_bool(false);
+    Call(cx, memmove, ~[dst_ptr, src_ptr, size, align, volatile]);
+}
+
+fn memmove_ty(bcx: block, dst: ValueRef, src: ValueRef, t: ty::t) {
+    let _icx = bcx.insn_ctxt("memmove_ty");
+    let ccx = bcx.ccx();
+    if ty::type_is_structural(t) {
+        let llsz = llsize_of(ccx, type_of::type_of(ccx, t));
+        call_memmove(bcx, dst, src, llsz);
+    } else {
+        Store(bcx, Load(bcx, src), dst);
     }
-    match b.node.expr {
-      Some(e) => {
-        let bt = ty::type_is_bot(expr_ty(bcx, e));
-        debuginfo::update_source_pos(bcx, e.span);
-        bcx = trans_expr(bcx, e, if bt { ignore } else { dest });
-      }
-      _ => assert dest == ignore || bcx.unreachable
+}
+
+fn zero_mem(cx: block, llptr: ValueRef, t: ty::t) {
+    let _icx = cx.insn_ctxt("zero_mem");
+    let bcx = cx;
+    let ccx = cx.ccx();
+    let llty = type_of::type_of(ccx, t);
+    memzero(bcx, llptr, llty);
+}
+
+// Always use this function instead of storing a zero constant to the memory
+// in question. If you store a zero constant, LLVM will drown in vreg
+// allocation for large data structures, and the generated code will be
+// awful. (A telltale sign of this is large quantities of
+// `mov [byte ptr foo],0` in the generated code.)
+fn memzero(cx: block, llptr: ValueRef, llty: TypeRef) {
+    let _icx = cx.insn_ctxt("memzero");
+    let ccx = cx.ccx();
+
+    let intrinsic_key;
+    match ccx.sess.targ_cfg.arch {
+        session::arch_x86 | session::arch_arm => {
+            intrinsic_key = ~"llvm.memset.p0i8.i32";
+        }
+        session::arch_x86_64 => {
+            intrinsic_key = ~"llvm.memset.p0i8.i64";
+        }
     }
-    return bcx;
+
+    let llintrinsicfn = ccx.intrinsics.get(intrinsic_key);
+    let llptr = PointerCast(cx, llptr, T_ptr(T_i8()));
+    let llzeroval = C_u8(0);
+    let size = IntCast(cx, shape::llsize_of(ccx, llty), ccx.int_type);
+    let align = C_i32(1i32);
+    let volatile = C_bool(false);
+    Call(cx, llintrinsicfn, ~[llptr, llzeroval, size, align, volatile]);
+}
+
+fn alloc_ty(bcx: block, t: ty::t) -> ValueRef {
+    let _icx = bcx.insn_ctxt("alloc_ty");
+    let ccx = bcx.ccx();
+    let llty = type_of::type_of(ccx, t);
+    if ty::type_has_params(t) { log(error, ty_to_str(ccx.tcx, t)); }
+    assert !ty::type_has_params(t);
+    let val = alloca(bcx, llty);
+    return val;
+}
+
+fn alloca(cx: block, t: TypeRef) -> ValueRef {
+    alloca_maybe_zeroed(cx, t, false)
+}
+
+fn alloca_zeroed(cx: block, t: TypeRef) -> ValueRef {
+    alloca_maybe_zeroed(cx, t, true)
+}
+
+fn alloca_maybe_zeroed(cx: block, t: TypeRef, zero: bool) -> ValueRef {
+    let _icx = cx.insn_ctxt("alloca");
+    if cx.unreachable { return llvm::LLVMGetUndef(t); }
+    let initcx = base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas);
+    let p = Alloca(initcx, t);
+    if zero { memzero(initcx, p, t); }
+    return p;
+}
+
+fn arrayalloca(cx: block, t: TypeRef, v: ValueRef) -> ValueRef {
+    let _icx = cx.insn_ctxt("arrayalloca");
+    if cx.unreachable { return llvm::LLVMGetUndef(t); }
+    return ArrayAlloca(
+        base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas), t, v);
 }
 
 // Creates the standard set of basic blocks for a function
@@ -4845,10 +1469,18 @@ fn create_llargs_for_fn_args(cx: fn_ctxt,
     let mut arg_n = first_real_arg;
     match ty_self {
       impl_self(tt) => {
-        cx.llself = Some({v: cx.llenv, t: tt, is_owned: false});
+        cx.llself = Some(ValSelfData {
+            v: cx.llenv,
+            t: tt,
+            is_owned: false
+        });
       }
       impl_owned_self(tt) => {
-        cx.llself = Some({v: cx.llenv, t: tt, is_owned: true});
+        cx.llself = Some(ValSelfData {
+            v: cx.llenv,
+            t: tt,
+            is_owned: true
+        });
       }
       no_self => ()
     }
@@ -4878,12 +1510,12 @@ fn copy_args_to_allocas(fcx: fn_ctxt, bcx: block, args: ~[ast::arg],
 
     match fcx.llself {
       Some(copy slf) => {
-        // We really should do this regardless of whether self is owned,
-        // but it doesn't work right with default method impls yet.
+        // We really should do this regardless of whether self is owned, but
+        // it doesn't work right with default method impls yet. (FIXME: #2794)
         if slf.is_owned {
             let self_val = PointerCast(bcx, slf.v,
                                        T_ptr(type_of(bcx.ccx(), slf.t)));
-            fcx.llself = Some({v: self_val,.. slf});
+            fcx.llself = Some(ValSelfData {v: self_val, ..slf});
             add_clean(bcx, self_val, slf.t);
         }
       }
@@ -4984,9 +1616,9 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
       (option::is_none(body.node.expr) ||
        ty::type_is_bot(block_ty) ||
        ty::type_is_nil(block_ty))  {
-        bcx = trans_block(bcx, body, ignore);
+        bcx = controlflow::trans_block(bcx, body, expr::Ignore);
     } else {
-        bcx = trans_block(bcx, body, save_in(fcx.llretptr));
+        bcx = controlflow::trans_block(bcx, body, expr::SaveIn(fcx.llretptr));
     }
     finish(bcx);
     cleanup_and_Br(bcx, bcx_top, fcx.llreturn);
@@ -5081,69 +1713,53 @@ fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
                     body: ast::blk, llctor_decl: ValueRef,
                     psubsts: param_substs, ctor_id: ast::node_id,
                     parent_id: ast::def_id, sp: span) {
-  // Add ctor to the ctor map
-  ccx.class_ctors.insert(ctor_id, parent_id);
-
-  // Translate the ctor
-
-  // Set up the type for the result of the ctor
-  // kludgy -- this wouldn't be necessary if the typechecker
-  // special-cased constructors, then we could just look up
-  // the ctor's return type.
-  let rslt_ty =  ty::mk_class(ccx.tcx, parent_id,
-                              dummy_substs(psubsts.tys));
-
-  // Make the fn context
-  let fcx = new_fn_ctxt_w_id(ccx, path, llctor_decl, ctor_id,
-                                   Some(psubsts), Some(sp));
-  create_llargs_for_fn_args(fcx, no_self, decl.inputs);
-  let mut bcx_top = top_scope_block(fcx, body.info());
-  let lltop = bcx_top.llbb;
-  bcx_top = copy_args_to_allocas(fcx, bcx_top, decl.inputs,
-              ty::ty_fn_args(node_id_type(bcx_top, ctor_id)));
-
-  // We *don't* want self to be passed to the ctor -- that
-  // wouldn't make sense
-  // So we initialize it here
-
-  let selfptr = alloc_ty(bcx_top, rslt_ty);
-  // If we have a dtor, we have a two-word representation with a drop
-  // flag, then a pointer to the class itself
-  let valptr = if option::is_some(ty::ty_dtor(bcx_top.tcx(),
-                                  parent_id)) {
-    // Initialize the drop flag
-    let one = C_u8(1u);
-    let flag = GEPi(bcx_top, selfptr, [0u, 0u]);
-    Store(bcx_top, one, flag);
-    // Select the pointer to the class itself
-    GEPi(bcx_top, selfptr, [0u, 1u])
-  }
-  else { selfptr };
-
-  // initialize fields to zero
-  let dsubsts = dummy_substs(psubsts.tys);
-  let fields = ty::class_items_as_mutable_fields(bcx_top.tcx(), parent_id,
-                                                 &dsubsts);
-  let mut bcx = bcx_top;
-  // Initialize fields to zero so init assignments can validly
-  // drop their LHS
-    for fields.each |field| {
-     let ix = field_idx_strict(bcx.tcx(), sp, field.ident, fields);
-     bcx = zero_mem(bcx, GEPi(bcx, valptr, [0u, ix]), field.mt.ty);
-  }
+    // Add ctor to the ctor map
+    ccx.class_ctors.insert(ctor_id, parent_id);
+
+    // Translate the ctor
+
+    // Set up the type for the result of the ctor
+    // kludgy -- this wouldn't be necessary if the typechecker
+    // special-cased constructors, then we could just look up
+    // the ctor's return type.
+    let rslt_ty =  ty::mk_class(ccx.tcx, parent_id,
+                                dummy_substs(psubsts.tys));
+
+    // Make the fn context
+    let fcx = new_fn_ctxt_w_id(ccx, path, llctor_decl, ctor_id,
+                               Some(psubsts), Some(sp));
+    create_llargs_for_fn_args(fcx, no_self, decl.inputs);
+    let mut bcx_top = top_scope_block(fcx, body.info());
+    let lltop = bcx_top.llbb;
+    let arg_tys = ty::ty_fn_args(node_id_type(bcx_top, ctor_id));
+    bcx_top = copy_args_to_allocas(fcx, bcx_top, decl.inputs, arg_tys);
+
+    // Create a temporary for `self` that we will return at the end
+    let selfdatum = datum::scratch_datum(bcx_top, rslt_ty, true);
+
+    // Initialize dtor flag (if any) to 1
+    if option::is_some(ty::ty_dtor(bcx_top.tcx(), parent_id)) {
+        let flag = GEPi(bcx_top, selfdatum.val, [0, 1]);
+        Store(bcx_top, C_u8(1), flag);
+    }
+
+    // initialize fields to zero
+    let mut bcx = bcx_top;
+
+    // note we don't want to take *or* drop self.
+    fcx.llself = Some(ValSelfData {v: selfdatum.val,
+                                   t: rslt_ty,
+                                   is_owned: false});
+
+    // Translate the body of the ctor
+    bcx = controlflow::trans_block(bcx, body, expr::Ignore);
 
-  // note we don't want to take *or* drop self.
-  fcx.llself = Some({v: selfptr, t: rslt_ty, is_owned: false});
+    // Generate the return expression
+    bcx = selfdatum.move_to(bcx, datum::INIT, fcx.llretptr);
 
-  // Translate the body of the ctor
-  bcx = trans_block(bcx_top, body, ignore);
-  let lval_res = {bcx: bcx, val: selfptr, kind: lv_owned};
-  // Generate the return expression
-  bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,
-                        rslt_ty, true);
-  cleanup_and_leave(bcx, None, Some(fcx.llreturn));
-  Unreachable(bcx);
-  finish_fn(fcx, lltop);
+    cleanup_and_leave(bcx, None, Some(fcx.llreturn));
+    Unreachable(bcx);
+    finish_fn(fcx, lltop);
 }
 
 fn trans_class_dtor(ccx: @crate_ctxt, path: path,
@@ -5429,16 +2045,6 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef,
     }
 }
 
-// Create a /real/ closure: this is like create_fn_pair, but creates a
-// a fn value on the stack with a specified environment (which need not be
-// on the stack).
-fn create_real_fn_pair(cx: block, llfnty: TypeRef, llfn: ValueRef,
-                       llenvptr: ValueRef) -> ValueRef {
-    let pair = alloca(cx, T_fn_pair(cx.ccx(), llfnty));
-    fill_fn_pair(cx, pair, llfn, llenvptr);
-    return pair;
-}
-
 fn fill_fn_pair(bcx: block, pair: ValueRef, llfn: ValueRef,
                 llenvptr: ValueRef) {
     let ccx = bcx.ccx();
@@ -5668,7 +2274,7 @@ fn trans_constant(ccx: @crate_ctxt, it: @ast::item) {
 fn trans_constants(ccx: @crate_ctxt, crate: @ast::crate) {
     visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{
         visit_item: |a| trans_constant(ccx, a),
-        .. *visit::default_simple_visitor()
+        ..*visit::default_simple_visitor()
     }));
 }
 
@@ -5776,7 +2382,7 @@ fn gather_local_rtcalls(ccx: @crate_ctxt, crate: @ast::crate) {
           }
           _ => ()
         },
-        .. *visit::default_simple_visitor()
+        ..*visit::default_simple_visitor()
     }));
 }
 
@@ -6089,7 +2695,7 @@ fn trans_crate(sess: session::session,
     // NB: Must call force_declare_tydescs before emit_tydescs to break
     // cyclical dependency with shape code! See shape.rs for details.
     force_declare_tydescs(ccx);
-    emit_tydescs(ccx);
+    glue::emit_tydescs(ccx);
     gen_shape_tables(ccx);
     write_abi_version(ccx);
 
diff --git a/src/rustc/middle/trans/block.rs b/src/rustc/middle/trans/block.rs
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/rustc/middle/trans/block.rs
diff --git a/src/rustc/middle/trans/build.rs b/src/rustc/middle/trans/build.rs
index 12b8f58117d..21ced9f5813 100644
--- a/src/rustc/middle/trans/build.rs
+++ b/src/rustc/middle/trans/build.rs
@@ -6,8 +6,8 @@ use codemap::span;
 use lib::llvm::{ValueRef, TypeRef, BasicBlockRef, BuilderRef, ModuleRef};
 use lib::llvm::{Opcode, IntPredicate, RealPredicate, True, False,
         CallConv, TypeKind, AtomicBinOp, AtomicOrdering};
-use common::*;
 use driver::session::session;
+use common::*;
 
 fn B(cx: block) -> BuilderRef {
     let b = cx.fcx.ccx.builder.B;
@@ -670,7 +670,7 @@ fn add_comment(bcx: block, text: ~str) {
     }
 }
 
-fn Call(cx: block, Fn: ValueRef, Args: ~[ValueRef]) -> ValueRef {
+fn Call(cx: block, Fn: ValueRef, Args: &[ValueRef]) -> ValueRef {
     if cx.unreachable { return _UndefReturn(cx, Fn); }
     unsafe {
         count_insn(cx, "call");
@@ -679,8 +679,9 @@ fn Call(cx: block, Fn: ValueRef, Args: ~[ValueRef]) -> ValueRef {
                val_str(cx.ccx().tn, Fn),
                Args.map(|arg| val_str(cx.ccx().tn, arg)));
 
-        return llvm::LLVMBuildCall(B(cx), Fn, vec::unsafe::to_ptr(Args),
-                                Args.len() as c_uint, noname());
+        do vec::as_buf(Args) |ptr, len| {
+            llvm::LLVMBuildCall(B(cx), Fn, ptr, len as c_uint, noname())
+        }
     }
 }
 
diff --git a/src/rustc/middle/trans/callee.rs b/src/rustc/middle/trans/callee.rs
new file mode 100644
index 00000000000..db6e9832ffd
--- /dev/null
+++ b/src/rustc/middle/trans/callee.rs
@@ -0,0 +1,665 @@
+//!
+//
+// Handles translation of callees as well as other call-related
+// things.  Callees are a superset of normal rust values and sometimes
+// have different representations.  In particular, top-level fn items
+// and methods are represented as just a fn ptr and not a full
+// closure.
+
+use lib::llvm::ValueRef;
+use syntax::ast;
+use datum::Datum;
+use common::{block, node_id_type_params};
+use build::*;
+use base::{get_item_val, trans_external_path};
+use syntax::visit;
+use syntax::print::pprust::{expr_to_str, stmt_to_str, path_to_str};
+use datum::*;
+use util::common::indenter;
+
+// Represents a (possibly monomorphized) top-level fn item or method
+// item.  Note that this is just the fn-ptr and is not a Rust closure
+// value (which is a pair).
+struct FnData {
+    llfn: ValueRef;
+}
+
+struct MethodData {
+    llfn: ValueRef;
+    llself: ValueRef;
+    self_ty: ty::t;
+    self_mode: ast::rmode;
+}
+
+enum CalleeData {
+    Closure(Datum),
+    Fn(FnData),
+    Method(MethodData)
+}
+
+struct Callee {
+    bcx: block;
+    data: CalleeData;
+}
+
+fn trans(bcx: block, expr: @ast::expr) -> Callee {
+    let _icx = bcx.insn_ctxt("trans_callee");
+
+    // pick out special kinds of expressions that can be called:
+    match expr.node {
+        ast::expr_path(_) => {
+            return trans_def(bcx, bcx.def(expr.id), expr);
+        }
+        ast::expr_field(base, _, _) => {
+            match bcx.ccx().maps.method_map.find(expr.id) {
+                Some(origin) => { // An impl method
+                    return impl::trans_method_callee(bcx, expr.id,
+                                                     base, origin);
+                }
+                None => {} // not a method, just a field
+            }
+        }
+        _ => {}
+    }
+
+    // any other expressions are closures:
+    return closure_callee(&expr::trans_to_datum(bcx, expr));
+
+    fn closure_callee(db: &DatumBlock) -> Callee {
+        return Callee {bcx: db.bcx, data: Closure(db.datum)};
+    }
+
+    fn fn_callee(bcx: block, fd: FnData) -> Callee {
+        return Callee {bcx: bcx, data: Fn(fd)};
+    }
+
+    fn trans_def(bcx: block, def: ast::def, ref_expr: @ast::expr) -> Callee {
+        match def {
+            ast::def_fn(did, _) => {
+                fn_callee(bcx, trans_fn_ref(bcx, did, ref_expr.id))
+            }
+            ast::def_static_method(did, _) => {
+                fn_callee(bcx, impl::trans_static_method_callee(bcx, did,
+                                                                ref_expr.id))
+            }
+            ast::def_variant(tid, vid) => {
+                // nullary variants are not callable
+                assert ty::enum_variant_with_id(bcx.tcx(),
+                                                tid,
+                                                vid).args.len() > 0u;
+                fn_callee(bcx, trans_fn_ref(bcx, vid, ref_expr.id))
+            }
+            ast::def_arg(*) |
+            ast::def_local(*) |
+            ast::def_binding(*) |
+            ast::def_upvar(*) |
+            ast::def_self(*) => {
+                closure_callee(&expr::trans_to_datum(bcx, ref_expr))
+            }
+            ast::def_mod(*) | ast::def_foreign_mod(*) |
+            ast::def_const(*) | ast::def_ty(*) | ast::def_prim_ty(*) |
+            ast::def_use(*) | ast::def_class(*) | ast::def_typaram_binder(*) |
+            ast::def_region(*) | ast::def_label(*) | ast::def_ty_param(*) => {
+                bcx.tcx().sess.span_bug(
+                    ref_expr.span,
+                    fmt!("Cannot translate def %? \
+                          to a callable thing!", def));
+            }
+        }
+    }
+}
+
+fn trans_fn_ref_to_callee(bcx: block,
+                          def_id: ast::def_id,
+                          ref_id: ast::node_id) -> Callee
+{
+    Callee {bcx: bcx,
+            data: Fn(trans_fn_ref(bcx, def_id, ref_id))}
+}
+
+fn trans_fn_ref(bcx: block,
+                def_id: ast::def_id,
+                ref_id: ast::node_id) -> FnData {
+    //!
+    //
+    // Translates a reference (with id `ref_id`) to the fn/method
+    // with id `def_id` into a function pointer.  This may require
+    // monomorphization or inlining.
+
+    let _icx = bcx.insn_ctxt("trans_fn");
+
+    let type_params = node_id_type_params(bcx, ref_id);
+
+    let raw_vtables = bcx.ccx().maps.vtable_map.find(ref_id);
+    let resolved_vtables = raw_vtables.map(
+        |vts| impl::resolve_vtables_in_fn_ctxt(bcx.fcx, vts));
+    trans_fn_ref_with_vtables(bcx, def_id, ref_id, type_params,
+                              resolved_vtables)
+}
+
+fn trans_fn_ref_with_vtables_to_callee(bcx: block,
+                                       def_id: ast::def_id,
+                                       ref_id: ast::node_id,
+                                       type_params: ~[ty::t],
+                                       vtables: Option<typeck::vtable_res>)
+    -> Callee
+{
+    Callee {bcx: bcx,
+            data: Fn(trans_fn_ref_with_vtables(bcx, def_id, ref_id,
+                                               type_params, vtables))}
+}
+
+fn trans_fn_ref_with_vtables(
+    bcx: block,            //
+    def_id: ast::def_id,   // def id of fn
+    ref_id: ast::node_id,  // node id of use of fn
+    type_params: ~[ty::t], // values for fn's ty params
+    vtables: Option<typeck::vtable_res>)
+    -> FnData
+{
+    //!
+    //
+    // Translates a reference to a fn/method item, monomorphizing and
+    // inlining as it goes.
+    //
+    // # Parameters
+    //
+    // - `bcx`: the current block where the reference to the fn occurs
+    // - `def_id`: def id of the fn or method item being referenced
+    // - `ref_id`: node id of the reference to the fn/method
+    // - `type_params`: values for each of the fn/method's type parameters
+    // - `vtables`: values for each bound on each of the type parameters
+
+    let _icx = bcx.insn_ctxt("trans_fn_with_vtables");
+    let ccx = bcx.ccx();
+    let tcx = ccx.tcx;
+
+    // Polytype of the function item (may have type params)
+    let fn_tpt = ty::lookup_item_type(tcx, def_id);
+
+    // Check whether this fn has an inlined copy and, if so, redirect
+    // def_id to the local id of the inlined copy.
+    let def_id = {
+        if def_id.crate != ast::local_crate {
+            inline::maybe_instantiate_inline(ccx, def_id)
+        } else {
+            def_id
+        }
+    };
+
+    // We must monomorphise if the fn has type parameters or is a rust
+    // intrinsic.  In particular, if we see an intrinsic that is
+    // inlined from a different crate, we want to reemit the intrinsic
+    // instead of trying to call it in the other crate.
+    let must_monomorphise = type_params.len() > 0 || {
+        if def_id.crate == ast::local_crate {
+            let map_node = session::expect(
+                ccx.sess,
+                ccx.tcx.items.find(def_id.node),
+                || fmt!("local item should be in ast map"));
+
+            match map_node {
+              ast_map::node_foreign_item(
+                  _, ast::foreign_abi_rust_intrinsic, _) => true,
+              _ => false
+            }
+        } else {
+            false
+        }
+    };
+
+    // Create a monomorphic verison of generic functions
+    if must_monomorphise {
+        // Should be either intra-crate or inlined.
+        assert def_id.crate == ast::local_crate;
+
+        let mut {val, must_cast} =
+            monomorphize::monomorphic_fn(ccx, def_id, type_params,
+                                         vtables, Some(ref_id));
+        if must_cast {
+            // Monotype of the REFERENCE to the function (type params
+            // are subst'd)
+            let ref_ty = common::node_id_type(bcx, ref_id);
+
+            val = PointerCast(
+                bcx, val, T_ptr(type_of::type_of_fn_from_ty(ccx, ref_ty)));
+        }
+        return FnData {llfn: val};
+    }
+
+    // Find the actual function pointer.
+    let mut val = {
+        if def_id.crate == ast::local_crate {
+            // Internal reference.
+            get_item_val(ccx, def_id.node)
+        } else {
+            // External reference.
+            trans_external_path(ccx, def_id, fn_tpt.ty)
+        }
+    };
+
+    //NDM I think this is dead. Commenting out to be sure!
+    //NDM
+    //NDM if tys.len() > 0u {
+    //NDM     val = PointerCast(bcx, val, T_ptr(type_of_fn_from_ty(
+    //NDM         ccx, node_id_type(bcx, id))));
+    //NDM }
+
+    return FnData {llfn: val};
+}
+
+// ______________________________________________________________________
+// Translating calls
+
+fn trans_call(in_cx: block,
+              call_ex: @ast::expr,
+              f: @ast::expr,
+              args: CallArgs,
+              id: ast::node_id,
+              dest: expr::Dest)
+    -> block
+{
+    let _icx = in_cx.insn_ctxt("trans_call");
+    trans_call_inner(
+        in_cx, call_ex.info(), expr_ty(in_cx, f), node_id_type(in_cx, id),
+        |cx| trans(cx, f), args, dest)
+}
+
+fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: expr::Dest)
+    -> block
+{
+    let did = bcx.ccx().rtcalls[name];
+    let fty = if did.crate == ast::local_crate {
+        ty::node_id_to_type(bcx.ccx().tcx, did.node)
+    } else {
+        csearch::get_type(bcx.ccx().tcx, did).ty
+    };
+    let rty = ty::ty_fn_ret(fty);
+    return callee::trans_call_inner(
+        bcx, None, fty, rty,
+        |bcx| trans_fn_ref_with_vtables_to_callee(bcx, did, 0, ~[], None),
+        ArgVals(args), dest);
+}
+
+fn body_contains_ret(body: ast::blk) -> bool {
+    let cx = {mut found: false};
+    visit::visit_block(body, cx, visit::mk_vt(@{
+        visit_item: |_i, _cx, _v| { },
+        visit_expr: |e: @ast::expr, cx: {mut found: bool}, v| {
+            if !cx.found {
+                match e.node {
+                  ast::expr_ret(_) => cx.found = true,
+                  _ => visit::visit_expr(e, cx, v),
+                }
+            }
+        },
+        ..*visit::default_visitor()
+    }));
+    cx.found
+}
+
+// See [Note-arg-mode]
+fn trans_call_inner(
+    ++in_cx: block,
+    call_info: Option<node_info>,
+    fn_expr_ty: ty::t,
+    ret_ty: ty::t,
+    get_callee: fn(block) -> Callee,
+    args: CallArgs,
+    dest: expr::Dest) -> block
+{
+    do base::with_scope(in_cx, call_info, ~"call") |cx| {
+        let ret_in_loop = match args {
+          ArgExprs(args) => {
+            args.len() > 0u && match vec::last(args).node {
+              ast::expr_loop_body(@{
+                node: ast::expr_fn_block(_, body, _),
+                _
+              }) =>  body_contains_ret(body),
+              _ => false
+            }
+          }
+          _ => false
+        };
+
+        let callee = get_callee(cx);
+        let mut bcx = callee.bcx;
+        let ccx = cx.ccx();
+        let ret_flag = if ret_in_loop {
+            let flag = alloca(bcx, T_bool());
+            Store(bcx, C_bool(false), flag);
+            Some(flag)
+        } else { None };
+
+        let (llfn, llenv) = match callee.data {
+            Fn(d) => {
+                (d.llfn, llvm::LLVMGetUndef(T_opaque_box_ptr(ccx)))
+            }
+            Method(d) => {
+                // Weird but true: we pass self in the *environment* slot!
+                let llself = PointerCast(bcx, d.llself,
+                                         T_opaque_box_ptr(ccx));
+                (d.llfn, llself)
+            }
+            Closure(d) => {
+                // Closures are represented as (llfn, llclosure) pair:
+                // load the requisite values out.
+                let pair = d.to_ref_llval(bcx);
+                let llfn = GEPi(bcx, pair, [0u, abi::fn_field_code]);
+                let llfn = Load(bcx, llfn);
+                let llenv = GEPi(bcx, pair, [0u, abi::fn_field_box]);
+                let llenv = Load(bcx, llenv);
+                (llfn, llenv)
+            }
+        };
+
+        let args_res = trans_args(bcx, llenv, args, fn_expr_ty,
+                                  dest, ret_flag);
+        bcx = args_res.bcx;
+        let mut llargs = args_res.args;
+
+        let llretslot = args_res.retslot;
+
+        // Now that the arguments have finished evaluating, we need to revoke
+        // the cleanup for the self argument, if it exists
+        match callee.data {
+            Method(d) if d.self_mode == ast::by_copy => {
+                revoke_clean(bcx, d.llself);
+            }
+            _ => {}
+        }
+
+        // If the block is terminated, then one or more of the args
+        // has type _|_. Since that means it diverges, the code for
+        // the call itself is unreachable.
+        bcx = base::invoke(bcx, llfn, llargs);
+        match dest { // drop the value if it is not being saved.
+            expr::Ignore => {
+                if llvm::LLVMIsUndef(llretslot) != lib::llvm::True {
+                    bcx = glue::drop_ty(bcx, llretslot, ret_ty);
+                }
+            }
+            expr::SaveIn(_) => { }
+        }
+        if ty::type_is_bot(ret_ty) {
+            Unreachable(bcx);
+        } else if ret_in_loop {
+            bcx = do with_cond(bcx, Load(bcx, option::get(ret_flag))) |bcx| {
+                do option::iter(copy bcx.fcx.loop_ret) |lret| {
+                    Store(bcx, C_bool(true), lret.flagptr);
+                    Store(bcx, C_bool(false), bcx.fcx.llretptr);
+                }
+                base::cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
+                Unreachable(bcx);
+                bcx
+            }
+        }
+        bcx
+    }
+}
+
+
+enum CallArgs {
+    ArgExprs(~[@ast::expr]),
+    ArgVals(~[ValueRef])
+}
+
+fn trans_args(cx: block, llenv: ValueRef, args: CallArgs, fn_ty: ty::t,
+              dest: expr::Dest, ret_flag: Option<ValueRef>)
+    -> {bcx: block, args: ~[ValueRef], retslot: ValueRef}
+{
+    let _icx = cx.insn_ctxt("trans_args");
+    let mut temp_cleanups = ~[];
+    let arg_tys = ty::ty_fn_args(fn_ty);
+    let mut llargs: ~[ValueRef] = ~[];
+
+    let mut bcx = cx;
+
+    let retty = ty::ty_fn_ret(fn_ty);
+
+    // Arg 0: Output pointer.
+    let llretslot = match dest {
+        expr::SaveIn(dst) => dst,
+        expr::Ignore => {
+            if ty::type_is_nil(retty) {
+                llvm::LLVMGetUndef(T_ptr(T_nil()))
+            } else {
+                alloc_ty(bcx, retty)
+            }
+        }
+    };
+    vec::push(llargs, llretslot);
+
+    // Arg 1: Env (closure-bindings / self value)
+    vec::push(llargs, llenv);
+
+    // ... then explicit args.
+
+    // First we figure out the caller's view of the types of the arguments.
+    // This will be needed if this is a generic call, because the callee has
+    // to cast her view of the arguments to the caller's view.
+    match args {
+      ArgExprs(arg_exprs) => {
+        let last = arg_exprs.len() - 1u;
+        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)
+            });
+            vec::push(llargs, arg_val);
+        }
+      }
+      ArgVals(vs) => {
+        vec::push_all(llargs, vs);
+      }
+    }
+
+    // now that all arguments have been successfully built, we can revoke any
+    // temporary cleanups, as they are only needed if argument construction
+    // should fail (for example, cleanup of copy mode args).
+    do vec::iter(temp_cleanups) |c| {
+        revoke_clean(bcx, c)
+    }
+
+    return {bcx: bcx, args: llargs, retslot: llretslot};
+}
+
+// temp_cleanups: cleanups that should run only if failure occurs before the
+// call takes place:
+fn trans_arg_expr(bcx: block,
+                  formal_ty: ty::arg,
+                  arg_expr: @ast::expr,
+                  temp_cleanups: &mut ~[ValueRef],
+                  ret_flag: Option<ValueRef>,
+                  derefs: uint)
+    -> 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=%?)",
+           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);
+    let _indenter = indenter();
+
+    // translate the arg expr to a datum
+    let arg_datumblock = match ret_flag {
+        None => expr::trans_to_datum(bcx, arg_expr),
+
+        // If there is a ret_flag, this *must* be a loop body
+        Some(_) => {
+            match arg_expr.node {
+                ast::expr_loop_body(
+                    blk @ @{node:ast::expr_fn_block(decl, body, cap), _}) =>
+                {
+                    let scratch_ty = expr_ty(bcx, blk);
+                    let scratch = alloc_ty(bcx, scratch_ty);
+                    let arg_ty = expr_ty(bcx, arg_expr);
+                    let proto = ty::ty_fn_proto(arg_ty);
+                    let bcx = closure::trans_expr_fn(
+                        bcx, proto, decl, body, blk.id,
+                        cap, Some(ret_flag), expr::SaveIn(scratch));
+                    DatumBlock {bcx: bcx,
+                                datum: Datum {val: scratch,
+                                              ty: scratch_ty,
+                                              mode: ByRef,
+                                              source: FromRvalue}}
+                }
+                _ => {
+                    bcx.sess().impossible_case(
+                        arg_expr.span, ~"ret_flag with non-loop-\
+                                         body expr");
+                }
+            }
+        }
+    };
+    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()));
+
+    // finally, deal with the various modes
+    let arg_mode = ty::resolved_mode(ccx.tcx, formal_ty.mode);
+    let mut val;
+    if ty::type_is_bot(arg_datum.ty) {
+        // For values of type _|_, we generate an
+        // "undef" value, as such a value should never
+        // be inspected. It's important for the value
+        // to have type lldestty (the callee's expected type).
+        let llformal_ty = type_of::type_of(ccx, formal_ty.ty);
+        val = llvm::LLVMGetUndef(llformal_ty);
+    } else {
+        match arg_mode {
+            ast::by_ref | ast::by_mutbl_ref => {
+                val = arg_datum.to_ref_llval(bcx);
+            }
+
+            ast::by_val => {
+                // NB: avoid running the take glue.
+                val = arg_datum.to_value_llval(bcx);
+            }
+
+            ast::by_copy | ast::by_move => {
+                let scratch = scratch_datum(bcx, arg_datum.ty, false);
+
+                if arg_mode == ast::by_move {
+                    // NDM---Doesn't seem like this should be necessary
+                    if !arg_datum.store_will_move() {
+                        bcx.sess().span_err(
+                            arg_expr.span,
+                            fmt!("move mode but datum will not store: %s",
+                                 arg_datum.to_str(bcx.ccx())));
+                    }
+                }
+
+                arg_datum.store_to_datum(bcx, INIT, scratch);
+
+                // Technically, ownership of val passes to the callee.
+                // However, we must cleanup should we fail before the
+                // callee is actually invoked.
+                scratch.add_clean(bcx);
+                vec::push(*temp_cleanups, scratch.val);
+                val = scratch.val;
+          }
+        }
+
+        if formal_ty.ty != arg_datum.ty {
+            // this could happen due to e.g. subtyping
+            let llformal_ty = type_of::type_of_explicit_arg(ccx, formal_ty);
+            debug!("casting actual type (%s) to match formal (%s)",
+                   bcx.val_str(val), bcx.llty_str(llformal_ty));
+            val = PointerCast(bcx, val, llformal_ty);
+        }
+    }
+
+    debug!("--- trans_arg_expr passing %s", val_str(bcx.ccx().tn, val));
+    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).struct {
+        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/closure.rs b/src/rustc/middle/trans/closure.rs
index 4e46286894d..9cf4393346b 100644
--- a/src/rustc/middle/trans/closure.rs
+++ b/src/rustc/middle/trans/closure.rs
@@ -17,6 +17,7 @@ use util::ppaux::ty_to_str;
 use syntax::ast_map::{path, path_mod, path_name};
 use driver::session::session;
 use std::map::hashmap;
+use datum::{Datum, INIT, ByRef, ByValue, FromLvalue};
 
 // ___Good to know (tm)__________________________________________________
 //
@@ -87,25 +88,35 @@ use std::map::hashmap;
 //
 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-enum environment_value {
-    // Copy the value from this llvm ValueRef into the environment.
-    env_copy(ValueRef, ty::t, lval_kind),
+enum EnvAction {
+    /// Copy the value from this llvm ValueRef into the environment.
+    EnvStore,
 
-    // Move the value from this llvm ValueRef into the environment.
-    env_move(ValueRef, ty::t, lval_kind),
+    /// Move the value from this llvm ValueRef into the environment.
+    EnvMove,
 
-    // Access by reference (used for blocks).
-    env_ref(ValueRef, ty::t, lval_kind),
+    /// Access by reference (used for stack closures).
+    EnvRef
 }
 
-fn ev_to_str(ccx: @crate_ctxt, ev: environment_value) -> ~str {
-    match ev {
-      env_copy(v, t, _) => fmt!("copy(%s,%s)", val_str(ccx.tn, v),
-                                ty_to_str(ccx.tcx, t)),
-      env_move(v, t, _) => fmt!("move(%s,%s)", val_str(ccx.tn, v),
-                                ty_to_str(ccx.tcx, t)),
-      env_ref(v, t, _) => fmt!("ref(%s,%s)", val_str(ccx.tn, v),
-                               ty_to_str(ccx.tcx, t))
+struct EnvValue {
+    action: EnvAction;
+    datum: Datum;
+}
+
+impl EnvAction {
+    fn to_str() -> ~str {
+        match self {
+            EnvStore => ~"EnvStore",
+            EnvMove => ~"EnvMove",
+            EnvRef => ~"EnvRef"
+        }
+    }
+}
+
+impl EnvValue {
+    fn to_str(ccx: @crate_ctxt) -> ~str {
+        fmt!("%s(%s)", self.action.to_str(), self.datum.to_str(ccx))
     }
 }
 
@@ -116,18 +127,18 @@ fn mk_tuplified_uniq_cbox_ty(tcx: ty::ctxt, cdata_ty: ty::t) -> ty::t {
 
 // Given a closure ty, emits a corresponding tuple ty
 fn mk_closure_tys(tcx: ty::ctxt,
-                  bound_values: ~[environment_value])
+                  bound_values: ~[EnvValue])
     -> ty::t {
-    let mut bound_tys = ~[];
-
-    // Compute the closed over data
-    for vec::each(bound_values) |bv| {
-        vec::push(bound_tys, match bv {
-            env_copy(_, t, _) => t,
-            env_move(_, t, _) => t,
-            env_ref(_, t, _) => t
-        });
-    }
+    // determine the types of the values in the env.  Note that this
+    // is the actual types that will be stored in the map, not the
+    // logical types as the user sees them, so by-ref upvars must be
+    // converted to ptrs.
+    let bound_tys = bound_values.map(|bv| {
+        match bv.action {
+            EnvStore | EnvMove => bv.datum.ty,
+            EnvRef => ty::mk_mut_ptr(tcx, bv.datum.ty)
+        }
+    });
     let cdata_ty = ty::mk_tup(tcx, bound_tys);
     debug!("cdata_ty=%s", ty_to_str(tcx, cdata_ty));
     return cdata_ty;
@@ -136,7 +147,8 @@ fn mk_closure_tys(tcx: ty::ctxt,
 fn allocate_cbox(bcx: block,
                  ck: ty::closure_kind,
                  cdata_ty: ty::t)
-    -> result {
+    -> Result
+{
     let _icx = bcx.insn_ctxt("closure::allocate_cbox");
     let ccx = bcx.ccx(), tcx = ccx.tcx;
 
@@ -151,18 +163,16 @@ fn allocate_cbox(bcx: block,
     }
 
     // Allocate and initialize the box:
-    let {bcx, val} = match ck {
+    match ck {
       ty::ck_box => malloc_raw(bcx, cdata_ty, heap_shared),
       ty::ck_uniq => malloc_raw(bcx, cdata_ty, heap_exchange),
       ty::ck_block => {
-        let cbox_ty = tuplify_box_ty(tcx, cdata_ty);
-        let llbox = base::alloc_ty(bcx, cbox_ty);
-        nuke_ref_count(bcx, llbox);
-        {bcx: bcx, val: llbox}
+          let cbox_ty = tuplify_box_ty(tcx, cdata_ty);
+          let llbox = base::alloc_ty(bcx, cbox_ty);
+          nuke_ref_count(bcx, llbox);
+          rslt(bcx, llbox)
       }
-    };
-
-    return {bcx: bcx, val: val};
+    }
 }
 
 type closure_result = {
@@ -176,7 +186,7 @@ type closure_result = {
 // heap allocated closure that copies the upvars into environment.
 // Otherwise, it is stack allocated and copies pointers to the upvars.
 fn store_environment(bcx: block,
-                     bound_values: ~[environment_value],
+                     bound_values: ~[EnvValue],
                      ck: ty::closure_kind) -> closure_result {
     let _icx = bcx.insn_ctxt("closure::store_environment");
     let ccx = bcx.ccx(), tcx = ccx.tcx;
@@ -185,7 +195,7 @@ fn store_environment(bcx: block,
     let cdata_ty = mk_closure_tys(tcx, bound_values);
 
     // allocate closure in the heap
-    let {bcx: bcx, val: llbox} = allocate_cbox(bcx, ck, cdata_ty);
+    let Result {bcx: bcx, val: llbox} = allocate_cbox(bcx, ck, cdata_ty);
     let mut temp_cleanups = ~[];
 
     // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a
@@ -200,43 +210,27 @@ fn store_environment(bcx: block,
     // Copy expr values into boxed bindings.
     let mut bcx = bcx;
     do vec::iteri(bound_values) |i, bv| {
-        debug!("Copy %s into closure", ev_to_str(ccx, bv));
+        debug!("Copy %s into closure", bv.to_str(ccx));
 
         if !ccx.sess.no_asm_comments() {
             add_comment(bcx, fmt!("Copy %s into closure",
-                                  ev_to_str(ccx, bv)));
+                                  bv.to_str(ccx)));
         }
 
         let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
-        match bv {
-          env_copy(val, ty, lv_owned) => {
-            let val1 = load_if_immediate(bcx, val, ty);
-            bcx = base::copy_val(bcx, INIT, bound_data, val1, ty);
-          }
-          env_copy(val, ty, lv_owned_imm) => {
-            bcx = base::copy_val(bcx, INIT, bound_data, val, ty);
-          }
-          env_copy(_, _, lv_temporary) => {
-            fail ~"cannot capture temporary upvar";
-          }
-          env_move(val, ty, kind) => {
-            let src = {bcx:bcx, val:val, kind:kind};
-            bcx = move_val(bcx, INIT, bound_data, src, ty);
-          }
-          env_ref(val, _, lv_owned) => {
-            debug!("> storing %s into %s",
-                   val_str(bcx.ccx().tn, val),
-                   val_str(bcx.ccx().tn, bound_data));
-            Store(bcx, val, bound_data);
-          }
-          env_ref(val, _, lv_owned_imm) => {
-            let addr = do_spill_noroot(bcx, val);
-            Store(bcx, addr, bound_data);
-          }
-          env_ref(_, _, lv_temporary) => {
-            fail ~"cannot capture temporary upvar";
-          }
+
+        match bv.action {
+            EnvStore => {
+                bcx = bv.datum.store_to(bcx, INIT, bound_data);
+            }
+            EnvMove => {
+                bcx = bv.datum.move_to(bcx, INIT, bound_data);
+            }
+            EnvRef => {
+                Store(bcx, bv.datum.to_ref_llval(bcx), bound_data);
+            }
         }
+
     }
     for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, cleanup); }
 
@@ -252,56 +246,57 @@ fn build_closure(bcx0: block,
                  include_ret_handle: Option<ValueRef>) -> closure_result {
     let _icx = bcx0.insn_ctxt("closure::build_closure");
     // If we need to, package up the iterator body to call
-    let mut env_vals = ~[];
-    let mut bcx = bcx0;
+    let mut bcx = bcx0;;
     let ccx = bcx.ccx(), tcx = ccx.tcx;
 
     // Package up the captured upvars
+    let mut env_vals = ~[];
     do vec::iter(cap_vars) |cap_var| {
         debug!("Building closure: captured variable %?", cap_var);
-        let lv = trans_local_var(bcx, cap_var.def);
-        let nid = ast_util::def_id_of_def(cap_var.def).node;
-        debug!("Node id is %s",
-               syntax::ast_map::node_id_to_str
-                   (bcx.ccx().tcx.items, nid,
-                    bcx.ccx().sess.parse_sess.interner));
-        let mut ty = node_id_type(bcx, nid);
+        let datum = expr::trans_local_var(bcx, id, cap_var.def);
         match cap_var.mode {
-          capture::cap_ref => {
-            assert ck == ty::ck_block;
-            ty = ty::mk_mut_ptr(tcx, ty);
-            vec::push(env_vals, env_ref(lv.val, ty, lv.kind));
-          }
-          capture::cap_copy => {
-            let mv = match ccx.maps.last_use_map.find(id) {
-              None => false,
-              Some(vars) => (*vars).contains(nid)
-            };
-            if mv { vec::push(env_vals, env_move(lv.val, ty, lv.kind)); }
-            else { vec::push(env_vals, env_copy(lv.val, ty, lv.kind)); }
-          }
-          capture::cap_move => {
-            vec::push(env_vals, env_move(lv.val, ty, lv.kind));
-          }
-          capture::cap_drop => {
-            assert lv.kind == lv_owned;
-            bcx = drop_ty(bcx, lv.val, ty);
-            bcx = zero_mem(bcx, lv.val, ty);
-          }
+            capture::cap_ref => {
+                assert ck == ty::ck_block;
+                vec::push(env_vals, EnvValue {action: EnvRef,
+                                              datum: datum});
+            }
+            capture::cap_copy => {
+                vec::push(env_vals, EnvValue {action: EnvStore,
+                                              datum: datum});
+            }
+            capture::cap_move => {
+                vec::push(env_vals, EnvValue {action: EnvMove,
+                                              datum: datum});
+            }
+            capture::cap_drop => {
+                bcx = datum.drop_val(bcx);
+                datum.cancel_clean(bcx);
+            }
         }
     }
+
+    // If this is a `for` loop body, add two special environment
+    // variables:
     do option::iter(include_ret_handle) |flagptr| {
-        let our_ret = match bcx.fcx.loop_ret {
-          Some({retptr, _}) => retptr,
-          None => bcx.fcx.llretptr
+        // Flag indicating we have returned (a by-ref bool):
+        let flag_datum = Datum {val: flagptr, ty: ty::mk_bool(tcx),
+                                mode: ByRef, source: FromLvalue};
+        vec::push(env_vals, EnvValue {action: EnvRef,
+                                      datum: flag_datum});
+
+        // Return value (we just pass a by-ref () and cast it later to
+        // the right thing):
+        let ret_true = match bcx.fcx.loop_ret {
+            Some({retptr, _}) => retptr,
+            None => bcx.fcx.llretptr
         };
-        let nil_ret = PointerCast(bcx, our_ret, T_ptr(T_nil()));
-        vec::push(env_vals,
-                  env_ref(flagptr,
-                          ty::mk_mut_ptr(tcx, ty::mk_bool(tcx)), lv_owned));
-        vec::push(env_vals,
-                  env_ref(nil_ret, ty::mk_nil_ptr(tcx), lv_owned));
+        let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil()));
+        let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx),
+                               mode: ByRef, source: FromLvalue};
+        vec::push(env_vals, EnvValue {action: EnvRef,
+                                      datum: ret_datum});
     }
+
     return store_environment(bcx, env_vals, ck);
 }
 
@@ -351,9 +346,16 @@ fn trans_expr_fn(bcx: block,
                  id: ast::node_id,
                  cap_clause: ast::capture_clause,
                  is_loop_body: Option<Option<ValueRef>>,
-                 dest: dest) -> block {
+                 dest: expr::Dest) -> block {
     let _icx = bcx.insn_ctxt("closure::trans_expr_fn");
-    if dest == ignore { return bcx; }
+
+    let dest_addr = match dest {
+        expr::SaveIn(p) => p,
+        expr::Ignore => {
+            return bcx; // closure construction is non-side-effecting
+        }
+    };
+
     let ccx = bcx.ccx();
     let fty = node_id_type(bcx, id);
     let llfnty = type_of_fn_from_ty(ccx, fty);
@@ -362,7 +364,7 @@ fn trans_expr_fn(bcx: block,
     let s = mangle_internal_name_by_path(ccx, sub_path);
     let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
 
-    let trans_closure_env = fn@(ck: ty::closure_kind) -> result {
+    let trans_closure_env = fn@(ck: ty::closure_kind) -> Result {
         let cap_vars = capture::compute_capture_vars(ccx.tcx, id, proto,
                                                      cap_clause);
         let ret_handle = match is_loop_body { Some(x) => x, None => None };
@@ -377,25 +379,29 @@ fn trans_expr_fn(bcx: block,
                 Store(bcx, C_bool(true), bcx.fcx.llretptr);
             }
         });
-        {bcx: bcx, val: llbox}
+        rslt(bcx, llbox)
     };
 
-    let {bcx: bcx, val: closure} = match proto {
-      ty::proto_vstore(ty::vstore_slice(_)) =>
-        trans_closure_env(ty::ck_block),
-      ty::proto_vstore(ty::vstore_box) =>
-        trans_closure_env(ty::ck_box),
-      ty::proto_vstore(ty::vstore_uniq) =>
-        trans_closure_env(ty::ck_uniq),
-      ty::proto_bare => {
-        trans_closure(ccx, sub_path, decl, body, llfn, no_self, None,
-                      id, |_fcx| { }, |_bcx| { });
-        {bcx: bcx, val: C_null(T_opaque_box_ptr(ccx))}
-      }
-      ty::proto_vstore(ty::vstore_fixed(_)) =>
-        fail ~"vstore_fixed unexpected"
+    let Result {bcx: bcx, val: closure} = match proto {
+        ty::proto_vstore(ty::vstore_slice(_)) => {
+            trans_closure_env(ty::ck_block)
+        }
+        ty::proto_vstore(ty::vstore_box) => {
+            trans_closure_env(ty::ck_box)
+        }
+        ty::proto_vstore(ty::vstore_uniq) => {
+            trans_closure_env(ty::ck_uniq)
+        }
+        ty::proto_bare => {
+            trans_closure(ccx, sub_path, decl, body, llfn, no_self, None,
+                          id, |_fcx| { }, |_bcx| { });
+            rslt(bcx, C_null(T_opaque_box_ptr(ccx)))
+        }
+        ty::proto_vstore(ty::vstore_fixed(_)) => {
+            fail ~"vstore_fixed unexpected"
+        }
     };
-    fill_fn_pair(bcx, get_dest_addr(dest), llfn, closure);
+    fill_fn_pair(bcx, dest_addr, llfn, closure);
 
     return bcx;
 }
@@ -440,12 +446,12 @@ fn make_opaque_cbox_take_glue(
     // Easy cases:
     let _icx = bcx.insn_ctxt("closure::make_opaque_cbox_take_glue");
     match ck {
-      ty::ck_block => return bcx,
-      ty::ck_box => {
-        incr_refcnt_of_boxed(bcx, Load(bcx, cboxptr));
-        return bcx;
-      }
-      ty::ck_uniq => { /* hard case: */ }
+        ty::ck_block => return bcx,
+        ty::ck_box => {
+            glue::incr_refcnt_of_boxed(bcx, Load(bcx, cboxptr));
+            return bcx;
+        }
+        ty::ck_uniq => { /* hard case: */ }
     }
 
     // Hard case, a deep copy:
@@ -467,20 +473,20 @@ fn make_opaque_cbox_take_glue(
         let malloc = ~"exchange_malloc";
         let opaque_tydesc = PointerCast(bcx, tydesc, T_ptr(T_i8()));
         let rval = alloca_zeroed(bcx, T_ptr(T_i8()));
-        let bcx = trans_rtcall(bcx, malloc, ~[opaque_tydesc, sz],
-                               save_in(rval));
+        let bcx = callee::trans_rtcall(bcx, malloc, ~[opaque_tydesc, sz],
+                                       expr::SaveIn(rval));
         let cbox_out = PointerCast(bcx, Load(bcx, rval), llopaquecboxty);
         call_memmove(bcx, cbox_out, cbox_in, sz);
         Store(bcx, cbox_out, cboxptr);
 
         // Take the (deeply cloned) type descriptor
         let tydesc_out = GEPi(bcx, cbox_out, [0u, abi::box_field_tydesc]);
-        let bcx = take_ty(bcx, tydesc_out, ty::mk_type(tcx));
+        let bcx = glue::take_ty(bcx, tydesc_out, ty::mk_type(tcx));
 
         // Take the data in the tuple
         let cdata_out = GEPi(bcx, cbox_out, [0u, abi::box_field_body]);
-        call_tydesc_glue_full(bcx, cdata_out, tydesc,
-                              abi::tydesc_field_take_glue, None);
+        glue::call_tydesc_glue_full(bcx, cdata_out, tydesc,
+                                    abi::tydesc_field_take_glue, None);
         bcx
     }
 }
@@ -492,15 +498,17 @@ fn make_opaque_cbox_drop_glue(
     -> block {
     let _icx = bcx.insn_ctxt("closure::make_opaque_cbox_drop_glue");
     match ck {
-      ty::ck_block => bcx,
-      ty::ck_box => {
-        decr_refcnt_maybe_free(bcx, Load(bcx, cboxptr),
-                               ty::mk_opaque_closure_ptr(bcx.tcx(), ck))
-      }
-      ty::ck_uniq => {
-        free_ty(bcx, cboxptr,
+        ty::ck_block => bcx,
+        ty::ck_box => {
+            glue::decr_refcnt_maybe_free(
+                bcx, Load(bcx, cboxptr),
                 ty::mk_opaque_closure_ptr(bcx.tcx(), ck))
-      }
+        }
+        ty::ck_uniq => {
+            glue::free_ty(
+                bcx, cboxptr,
+                ty::mk_opaque_closure_ptr(bcx.tcx(), ck))
+        }
     }
 }
 
@@ -526,14 +534,14 @@ fn make_opaque_cbox_free_glue(
 
         // Drop the tuple data then free the descriptor
         let cdata = GEPi(bcx, cbox, [0u, abi::box_field_body]);
-        call_tydesc_glue_full(bcx, cdata, tydesc,
-                              abi::tydesc_field_drop_glue, None);
+        glue::call_tydesc_glue_full(bcx, cdata, tydesc,
+                                    abi::tydesc_field_drop_glue, None);
 
         // Free the ty descr (if necc) and the box itself
         match ck {
-          ty::ck_block => fail ~"Impossible",
-          ty::ck_box => trans_free(bcx, cbox),
-          ty::ck_uniq => trans_unique_free(bcx, cbox)
+            ty::ck_block => fail ~"Impossible",
+            ty::ck_box => glue::trans_free(bcx, cbox),
+            ty::ck_uniq => glue::trans_unique_free(bcx, cbox)
         }
     }
 }
diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs
index 6edccf0edec..6fb547d757d 100644
--- a/src/rustc/middle/trans/common.rs
+++ b/src/rustc/middle/trans/common.rs
@@ -20,6 +20,7 @@ use metadata::{csearch};
 use metadata::common::link_meta;
 use syntax::ast_map::path;
 use util::ppaux::ty_to_str;
+use syntax::print::pprust::expr_to_str;
 use syntax::parse::token::ident_interner;
 use syntax::ast::ident;
 
@@ -165,7 +166,11 @@ type crate_ctxt = {
      mut do_not_commit_warning_issued: bool};
 
 // Types used for llself.
-type val_self_data = {v: ValueRef, t: ty::t, is_owned: bool};
+struct ValSelfData {
+    v: ValueRef;
+    t: ty::t;
+    is_owned: bool;
+}
 
 enum local_val { local_mem(ValueRef), local_imm(ValueRef), }
 
@@ -201,7 +206,7 @@ type fn_ctxt = @{
     mut llreturn: BasicBlockRef,
     // The 'self' value currently in use in this function, if there
     // is one.
-    mut llself: Option<val_self_data>,
+    mut llself: Option<ValSelfData>,
     // The a value alloca'd for calls to upcalls.rust_personality. Used when
     // outputting the resume instruction.
     mut personality: Option<ValueRef>,
@@ -257,6 +262,25 @@ enum cleanup {
     clean_temp(ValueRef, fn@(block) -> block, cleantype),
 }
 
+impl cleantype : cmp::Eq {
+    pure fn eq(&&other: cleantype) -> bool {
+        match self {
+            normal_exit_only => {
+                match other {
+                    normal_exit_only => true,
+                    _ => false
+                }
+            }
+            normal_exit_and_unwind => {
+                match other {
+                    normal_exit_and_unwind => true,
+                    _ => false
+                }
+            }
+        }
+    }
+}
+
 // Used to remember and reuse existing cleanup paths
 // target: none means the path ends in an resume instruction
 type cleanup_path = {target: Option<BasicBlockRef>,
@@ -275,12 +299,12 @@ fn cleanup_type(cx: ty::ctxt, ty: ty::t) -> cleantype {
     }
 }
 
-// This is not the same as base::root_value, which appears to be the vestigial
-// remains of the previous GC regime. In the new GC, we can identify
-// immediates on the stack without difficulty, but have trouble knowing where
-// non-immediates are on the stack. For non-immediates, we must add an
-// additional level of indirection, which allows us to alloca a pointer with
-// the right addrspace.
+// This is not the same as datum::Datum::root(), which is used to keep copies
+// of @ values live for as long as a borrowed pointer to the interior exists.
+// In the new GC, we can identify immediates on the stack without difficulty,
+// but have trouble knowing where non-immediates are on the stack. For
+// non-immediates, we must add an additional level of indirection, which
+// allows us to alloca a pointer with the right addrspace.
 fn root_for_cleanup(bcx: block, v: ValueRef, t: ty::t)
     -> {root: ValueRef, rooted: bool} {
     let ccx = bcx.ccx();
@@ -305,11 +329,12 @@ fn add_clean(bcx: block, val: ValueRef, t: ty::t) {
     let cleanup_type = cleanup_type(bcx.tcx(), t);
     do in_scope_cx(bcx) |info| {
         vec::push(info.cleanups,
-                  clean(|a| base::drop_ty_root(a, root, rooted, t),
+                  clean(|a| glue::drop_ty_root(a, root, rooted, t),
                         cleanup_type));
         scope_clean_changed(info);
     }
 }
+
 fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) {
     if !ty::type_needs_drop(cx.tcx(), ty) { return; }
     debug!("add_clean_temp_immediate(%s, %s, %s)",
@@ -318,7 +343,7 @@ fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) {
     let cleanup_type = cleanup_type(cx.tcx(), ty);
     do in_scope_cx(cx) |info| {
         vec::push(info.cleanups,
-                  clean_temp(val, |a| base::drop_ty_immediate(a, val, ty),
+                  clean_temp(val, |a| glue::drop_ty_immediate(a, val, ty),
                              cleanup_type));
         scope_clean_changed(info);
     }
@@ -332,15 +357,15 @@ fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
     let cleanup_type = cleanup_type(bcx.tcx(), t);
     do in_scope_cx(bcx) |info| {
         vec::push(info.cleanups,
-                  clean_temp(val, |a| base::drop_ty_root(a, root, rooted, t),
+                  clean_temp(val, |a| glue::drop_ty_root(a, root, rooted, t),
                              cleanup_type));
         scope_clean_changed(info);
     }
 }
 fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
     let free_fn = match heap {
-      heap_shared => |a| base::trans_free(a, ptr),
-      heap_exchange => |a| base::trans_unique_free(a, ptr)
+      heap_shared => |a| glue::trans_free(a, ptr),
+      heap_exchange => |a| glue::trans_unique_free(a, ptr)
     };
     do in_scope_cx(cx) |info| {
         vec::push(info.cleanups, clean_temp(ptr, free_fn,
@@ -355,12 +380,13 @@ fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
 // drop glue checks whether it is zero.
 fn revoke_clean(cx: block, val: ValueRef) {
     do in_scope_cx(cx) |info| {
-        do option::iter(vec::position(info.cleanups, |cu| {
-            match cu {
-              clean_temp(v, _, _) if v == val => true,
-              _ => false
-            }
-        })) |i| {
+        let cleanup_pos = vec::position(
+            info.cleanups,
+            |cu| match cu {
+                clean_temp(v, _, _) if v == val => true,
+                _ => false
+            });
+        for cleanup_pos.each |i| {
             info.cleanups =
                 vec::append(vec::slice(info.cleanups, 0u, i),
                             vec::view(info.cleanups,
@@ -384,6 +410,7 @@ enum block_kind {
     // to an implicit scope, for example, calls introduce an implicit scope in
     // which the arguments are evaluated and cleaned up.
     block_scope(scope_info),
+
     // A non-scope block is a basic block created as a translation artifact
     // from translating code that expresses conditional logic rather than by
     // explicit { ... } block structure in the source language.  It's called a
@@ -480,11 +507,20 @@ fn mk_block(llbb: BasicBlockRef, parent: Option<block>, -kind: block_kind,
 // First two args are retptr, env
 const first_real_arg: uint = 2u;
 
-type result = {bcx: block, val: ValueRef};
-type result_t = {bcx: block, val: ValueRef, ty: ty::t};
+struct Result {
+    bcx: block;
+    val: ValueRef;
+}
+
+fn rslt(bcx: block, val: ValueRef) -> Result {
+    Result {bcx: bcx, val: val}
+}
 
-fn rslt(bcx: block, val: ValueRef) -> result {
-    {bcx: bcx, val: val}
+impl Result {
+    fn unpack(bcx: &mut block) -> ValueRef {
+        *bcx = self.bcx;
+        return self.val;
+    }
 }
 
 fn ty_str(tn: type_names, t: TypeRef) -> ~str {
@@ -510,7 +546,12 @@ fn in_scope_cx(cx: block, f: fn(scope_info)) {
     let mut cur = cx;
     loop {
         match cur.kind {
-          block_scope(inf) => { f(inf); return; }
+          block_scope(inf) => {
+              debug!("in_scope_cx: selected cur=%s (cx=%s)",
+                     cur.to_str(), cx.to_str());
+              f(inf);
+              return;
+          }
           _ => ()
         }
         cur = block_parent(cur);
@@ -532,9 +573,40 @@ impl block {
     pure fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
     pure fn sess() -> session { self.fcx.ccx.sess }
 
+    fn node_id_to_str(id: ast::node_id) -> ~str {
+        ast_map::node_id_to_str(self.tcx().items, id, self.sess().intr())
+    }
+
+    fn expr_to_str(e: @ast::expr) -> ~str {
+        fmt!("expr(%d: %s)", e.id, expr_to_str(e, self.sess().intr()))
+    }
+
+    fn expr_is_lval(e: @ast::expr) -> bool {
+        ty::expr_is_lval(self.tcx(), self.ccx().maps.method_map, e)
+    }
+
+    fn expr_kind(e: @ast::expr) -> ty::ExprKind {
+        ty::expr_kind(self.tcx(), self.ccx().maps.method_map, e)
+    }
+
+    fn def(nid: ast::node_id) -> ast::def {
+        match self.tcx().def_map.find(nid) {
+            Some(v) => v,
+            None => {
+                self.tcx().sess.bug(fmt!(
+                    "No def associated with node id %?", nid));
+            }
+        }
+    }
+
     fn val_str(val: ValueRef) -> ~str {
         val_str(self.ccx().tn, val)
     }
+
+    fn llty_str(llty: TypeRef) -> ~str {
+        ty_str(self.ccx().tn, llty)
+    }
+
     fn ty_to_str(t: ty::t) -> ~str {
         ty_to_str(self.tcx(), t)
     }
@@ -954,14 +1026,16 @@ fn C_zero_byte_arr(size: uint) -> ValueRef unsafe {
                              elts.len() as c_uint);
 }
 
-fn C_struct(elts: ~[ValueRef]) -> ValueRef unsafe {
-    return llvm::LLVMConstStruct(vec::unsafe::to_ptr(elts),
-                              elts.len() as c_uint, False);
+fn C_struct(elts: &[ValueRef]) -> ValueRef {
+    do vec::as_buf(elts) |ptr, len| {
+        llvm::LLVMConstStruct(ptr, len as c_uint, False)
+    }
 }
 
-fn C_named_struct(T: TypeRef, elts: ~[ValueRef]) -> ValueRef unsafe {
-    return llvm::LLVMConstNamedStruct(T, vec::unsafe::to_ptr(elts),
-                                   elts.len() as c_uint);
+fn C_named_struct(T: TypeRef, elts: &[ValueRef]) -> ValueRef {
+    do vec::as_buf(elts) |ptr, len| {
+        llvm::LLVMConstNamedStruct(T, ptr, len as c_uint)
+    }
 }
 
 fn C_array(ty: TypeRef, elts: ~[ValueRef]) -> ValueRef unsafe {
@@ -1100,40 +1174,22 @@ fn node_id_type_params(bcx: block, id: ast::node_id) -> ~[ty::t] {
     }
 }
 
-fn field_idx_strict(cx: ty::ctxt, sp: span, ident: ast::ident,
-                    fields: ~[ty::field])
-    -> uint {
-    match ty::field_idx(ident, fields) {
-       None => cx.sess.span_bug(
-           sp, fmt!("base expr doesn't appear to \
-                         have a field named %s", cx.sess.str_of(ident))),
-       Some(i) => i
-    }
-}
-
 fn dummy_substs(tps: ~[ty::t]) -> ty::substs {
     {self_r: Some(ty::re_bound(ty::br_self)),
      self_ty: None,
      tps: tps}
 }
 
-impl cleantype : cmp::Eq {
-    pure fn eq(&&other: cleantype) -> bool {
-        match self {
-            normal_exit_only => {
-                match other {
-                    normal_exit_only => true,
-                    _ => false
-                }
-            }
-            normal_exit_and_unwind => {
-                match other {
-                    normal_exit_and_unwind => true,
-                    _ => false
-                }
-            }
-        }
-    }
+fn struct_field(index: uint) -> [uint]/3 {
+    //! The GEPi sequence to access a field of a record/struct.
+
+    [0, 0, index]
+}
+
+fn struct_dtor() -> [uint]/2 {
+    //! The GEPi sequence to access the dtor of a struct.
+
+    [0, 1]
 }
 
 //
diff --git a/src/rustc/middle/trans/consts.rs b/src/rustc/middle/trans/consts.rs
index ec3e55fb2ae..f877a5a24b2 100644
--- a/src/rustc/middle/trans/consts.rs
+++ b/src/rustc/middle/trans/consts.rs
@@ -59,9 +59,15 @@ fn const_deref(cx: @crate_ctxt, v: ValueRef) -> ValueRef {
     v
 }
 
-fn const_get_elt(v: ValueRef, u: uint) -> ValueRef {
-    let u = u;
-    llvm::LLVMConstExtractValue(v, ptr::addr_of(u), 1 as c_uint)
+fn const_get_elt(cx: @crate_ctxt, v: ValueRef, us: &[c_uint]) -> ValueRef {
+    let r = do vec::as_buf(us) |p, len| {
+        llvm::LLVMConstExtractValue(v, p, len as c_uint)
+    };
+
+    debug!("const_get_elt(v=%s, us=%?, r=%s)",
+           val_str(cx.tn, v), us, val_str(cx.tn, r));
+
+    return r;
 }
 
 fn const_autoderef(cx: @crate_ctxt, ty: ty::t, v: ValueRef)
@@ -83,7 +89,7 @@ fn const_autoderef(cx: @crate_ctxt, ty: ty::t, v: ValueRef)
 
 fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
     let _icx = cx.insn_ctxt("const_expr");
-    match e.node {
+    return match e.node {
       ast::expr_lit(lit) => consts::const_lit(cx, e, *lit),
       ast::expr_binary(b, e1, e2) => {
         let te1 = const_expr(cx, e1);
@@ -156,15 +162,15 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
           let bt = ty::expr_ty(cx.tcx, base);
           let bv = const_expr(cx, base);
           let (bt, bv) = const_autoderef(cx, bt, bv);
-          let fields = match ty::get(bt).struct {
-              ty::ty_rec(fs) => fs,
-              ty::ty_class(did, ref substs) =>
-                  ty::class_items_as_mutable_fields(cx.tcx, did, substs),
-              _ => cx.sess.span_bug(e.span,
-                                    ~"field access on unknown type in const"),
-          };
-          let ix = field_idx_strict(cx.tcx, e.span, field, fields);
-          const_get_elt(bv, ix)
+          do expr::with_field_tys(cx.tcx, bt) |_has_dtor, field_tys| {
+              let ix = ty::field_idx_strict(cx.tcx, field, field_tys);
+
+              // Note: ideally, we'd use `struct_field()` here instead
+              // of hardcoding [0, ix], but we can't because it yields
+              // the wrong type and also inserts an extra 0 that is
+              // not needed in the constant variety:
+              const_get_elt(cx, bv, [0, ix as c_uint])
+          }
       }
 
       ast::expr_index(base, index) => {
@@ -189,8 +195,8 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
                       let llunitty = type_of::type_of(cx, unit_ty);
                       let unit_sz = shape::llsize_of(cx, llunitty);
 
-                      (const_deref(cx, const_get_elt(bv, 0)),
-                       llvm::LLVMConstUDiv(const_get_elt(bv, 1),
+                      (const_deref(cx, const_get_elt(cx, bv, [0])),
+                       llvm::LLVMConstUDiv(const_get_elt(cx, bv, [1]),
                                            unit_sz))
                   },
                   _ => cx.sess.span_bug(base.span,
@@ -240,27 +246,27 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
               cx.sess.span_err(e.span,
                                ~"const index-expr is out of bounds");
           }
-          const_get_elt(arr, iv as uint)
+          const_get_elt(cx, arr, [iv as c_uint])
       }
       ast::expr_cast(base, _) => {
         let ety = ty::expr_ty(cx.tcx, e), llty = type_of::type_of(cx, ety);
         let basety = ty::expr_ty(cx.tcx, base);
         let v = const_expr(cx, base);
-        match (base::cast_type_kind(basety),
-                     base::cast_type_kind(ety)) {
+        match (expr::cast_type_kind(basety),
+               expr::cast_type_kind(ety)) {
 
-          (base::cast_integral, base::cast_integral) => {
+          (expr::cast_integral, expr::cast_integral) => {
             let s = if ty::type_is_signed(basety) { True } else { False };
             llvm::LLVMConstIntCast(v, llty, s)
           }
-          (base::cast_integral, base::cast_float) => {
+          (expr::cast_integral, expr::cast_float) => {
             if ty::type_is_signed(basety) { llvm::LLVMConstSIToFP(v, llty) }
             else { llvm::LLVMConstUIToFP(v, llty) }
           }
-          (base::cast_float, base::cast_float) => {
+          (expr::cast_float, expr::cast_float) => {
             llvm::LLVMConstFPCast(v, llty)
           }
-          (base::cast_float, base::cast_integral) => {
+          (expr::cast_float, expr::cast_integral) => {
             if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) }
             else { llvm::LLVMConstFPToUI(v, llty) }
           }
@@ -282,34 +288,25 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
       ast::expr_tup(es) => {
         C_struct(es.map(|e| const_expr(cx, e)))
       }
-      ast::expr_struct(_, fs, _) => {
+      ast::expr_rec(fs, None) => {
+          C_struct([C_struct(
+              fs.map(|f| const_expr(cx, f.node.expr)))])
+      }
+      ast::expr_struct(_, ref fs, _) => {
           let ety = ty::expr_ty(cx.tcx, e);
-          let llty = type_of::type_of(cx, ety);
-          let class_fields =
-              match ty::get(ety).struct {
-              ty::ty_class(clsid, _) =>
-                  ty::lookup_class_fields(cx.tcx, clsid),
-              _ =>
-                  cx.tcx.sess.span_bug(e.span,
-                                       ~"didn't resolve to a struct")
-          };
-          let mut cs = ~[];
-          for class_fields.each |class_field| {
-              let mut found = false;
-              for fs.each |field| {
-                  if class_field.ident == field.node.ident  {
-                      found = true;
-                      vec::push(cs, const_expr(cx, field.node.expr));
+          let cs = do expr::with_field_tys(cx.tcx, ety) |_hd, field_tys| {
+              field_tys.map(|field_ty| {
+                  match fs.find(|f| field_ty.ident == f.node.ident) {
+                      Some(f) => const_expr(cx, f.node.expr),
+                      None => {
+                          cx.tcx.sess.span_bug(
+                              e.span, ~"missing struct field");
+                      }
                   }
-              }
-              if !found {
-                  cx.tcx.sess.span_bug(e.span, ~"missing struct field");
-              }
-          }
-          C_named_struct(llty, cs)
-      }
-      ast::expr_rec(fs, None) => {
-        C_struct(fs.map(|f| const_expr(cx, f.node.expr)))
+              })
+          };
+          let llty = type_of::type_of(cx, ety);
+          C_named_struct(llty, [C_struct(cs)])
       }
       ast::expr_vec(es, ast::m_imm) => {
         let (v, _, _) = const_vec(cx, e, es);
@@ -364,7 +361,7 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
       }
       _ => cx.sess.span_bug(e.span,
             ~"bad constant expression type in consts::const_expr")
-    }
+    };
 }
 
 fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) {
diff --git a/src/rustc/middle/trans/controlflow.rs b/src/rustc/middle/trans/controlflow.rs
new file mode 100644
index 00000000000..83c5eb5f5f3
--- /dev/null
+++ b/src/rustc/middle/trans/controlflow.rs
@@ -0,0 +1,346 @@
+use lib::llvm::ValueRef;
+use common::*;
+use datum::*;
+use base::*;
+
+fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
+
+fn trans_block(bcx: block, b: ast::blk, dest: expr::Dest) -> block {
+    let _icx = bcx.insn_ctxt("trans_block");
+    let mut bcx = bcx;
+    do block_locals(b) |local| {
+        bcx = alloc_local(bcx, local);
+    };
+    for vec::each(b.node.stmts) |s| {
+        debuginfo::update_source_pos(bcx, b.span);
+        bcx = trans_stmt(bcx, *s);
+    }
+    match b.node.expr {
+        Some(e) => {
+            debuginfo::update_source_pos(bcx, e.span);
+            bcx = expr::trans_into(bcx, e, dest);
+        }
+        None => {
+            assert dest == expr::Ignore || bcx.unreachable;
+        }
+    }
+    return bcx;
+}
+
+fn trans_if(bcx: block,
+            cond: @ast::expr,
+            thn: ast::blk,
+            els: Option<@ast::expr>,
+            dest: expr::Dest)
+    -> block
+{
+    debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)",
+           bcx.to_str(), bcx.expr_to_str(cond), thn.node.id,
+           dest.to_str(bcx.ccx()));
+    let _indenter = indenter();
+
+    let _icx = bcx.insn_ctxt("trans_if");
+    let Result {bcx, val: cond_val} =
+        expr::trans_to_appropriate_llval(bcx, cond);
+
+    let then_bcx_in = scope_block(bcx, thn.info(), ~"then");
+    let else_bcx_in = scope_block(bcx, els.info(), ~"else");
+    CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
+
+    debug!("then_bcx_in=%s, else_bcx_in=%s",
+           then_bcx_in.to_str(), else_bcx_in.to_str());
+
+    let then_bcx_out = trans_block(then_bcx_in, thn, dest);
+    let then_bcx_out = trans_block_cleanups(then_bcx_out,
+                                            block_cleanups(then_bcx_in));
+
+    // Calling trans_block directly instead of trans_expr
+    // because trans_expr will create another scope block
+    // context for the block, but we've already got the
+    // 'else' context
+    let else_bcx_out = match els {
+      Some(elexpr) => {
+        match elexpr.node {
+          ast::expr_if(_, _, _) => {
+            let elseif_blk = ast_util::block_from_expr(elexpr);
+            trans_block(else_bcx_in, elseif_blk, dest)
+          }
+          ast::expr_block(blk) => {
+            trans_block(else_bcx_in, blk, dest)
+          }
+          // would be nice to have a constraint on ifs
+          _ => bcx.tcx().sess.bug(~"strange alternative in if")
+        }
+      }
+      _ => else_bcx_in
+    };
+    let else_bcx_out = trans_block_cleanups(else_bcx_out,
+                                            block_cleanups(else_bcx_in));
+    return join_blocks(bcx, ~[then_bcx_out, else_bcx_out]);
+
+}
+
+fn join_blocks(parent_bcx: block, in_cxs: ~[block]) -> block {
+    let out = sub_block(parent_bcx, ~"join");
+    let mut reachable = false;
+    for vec::each(in_cxs) |bcx| {
+        if !bcx.unreachable {
+            Br(bcx, out.llbb);
+            reachable = true;
+        }
+    }
+    if !reachable {
+        Unreachable(out);
+    }
+    return out;
+}
+
+fn trans_while(bcx: block, cond: @ast::expr, body: ast::blk)
+    -> block {
+    let _icx = bcx.insn_ctxt("trans_while");
+    let next_bcx = sub_block(bcx, ~"while next");
+
+    //            bcx
+    //             |
+    //          loop_bcx
+    //             |
+    //         cond_bcx_in  <--------+
+    //             |                 |
+    //         cond_bcx_out          |
+    //           |      |            |
+    //           |    body_bcx_in    |
+    //    +------+      |            |
+    //    |           body_bcx_out --+
+    // next_bcx
+
+    let loop_bcx = loop_scope_block(bcx, next_bcx, ~"`while`", body.info());
+    let cond_bcx_in = scope_block(loop_bcx, cond.info(), ~"while loop cond");
+    let body_bcx_in = scope_block(loop_bcx, body.info(), ~"while loop body");
+    Br(bcx, loop_bcx.llbb);
+    Br(loop_bcx, cond_bcx_in.llbb);
+
+    // compile the condition
+    let Result {bcx: cond_bcx_out, val: cond_val} =
+        expr::trans_to_appropriate_llval(cond_bcx_in, cond);
+    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);
+
+    // loop body:
+    let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
+    cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb);
+
+    return next_bcx;
+}
+
+fn trans_loop(bcx:block, body: ast::blk) -> block {
+    let _icx = bcx.insn_ctxt("trans_loop");
+    let next_bcx = sub_block(bcx, ~"next");
+    let body_bcx_in = loop_scope_block(bcx, next_bcx, ~"`loop`", body.info());
+    Br(bcx, body_bcx_in.llbb);
+    let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
+    cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb);
+    return next_bcx;
+}
+
+fn trans_log(log_ex: @ast::expr,
+             lvl: @ast::expr,
+             bcx: block,
+             e: @ast::expr) -> block
+{
+    let _icx = bcx.insn_ctxt("trans_log");
+    let ccx = bcx.ccx();
+    let mut bcx = bcx;
+    if ty::type_is_bot(expr_ty(bcx, lvl)) {
+       return expr::trans_into(bcx, lvl, expr::Ignore);
+    }
+
+    let modpath = vec::append(
+        ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))],
+        vec::filter(bcx.fcx.path, |e|
+            match e { path_mod(_) => true, _ => false }
+        ));
+    let modname = path_str(ccx.sess, modpath);
+
+    let global = if ccx.module_data.contains_key(modname) {
+        ccx.module_data.get(modname)
+    } else {
+        let s = link::mangle_internal_name_by_path_and_seq(
+            ccx, modpath, ~"loglevel");
+        let global = str::as_c_str(s, |buf| {
+            llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf)
+        });
+        llvm::LLVMSetGlobalConstant(global, False);
+        llvm::LLVMSetInitializer(global, C_null(T_i32()));
+        lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage);
+        ccx.module_data.insert(modname, global);
+        global
+    };
+    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)
+        }
+    });
+
+    let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level);
+    do with_cond(bcx, llenabled) |bcx| {
+        do with_scope(bcx, log_ex.info(), ~"log") |bcx| {
+            let mut bcx = bcx;
+
+            // Translate the value to be logged
+            let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e));
+            let tydesc = get_tydesc_simple(ccx, val_datum.ty);
+
+            // Call the polymorphic log function
+            let val = val_datum.to_ref_llval(bcx);
+            let val = PointerCast(bcx, val, T_ptr(T_i8()));
+            Call(bcx, ccx.upcalls.log_type, [tydesc, val, level]);
+            bcx
+        }
+    }
+}
+
+fn trans_break_cont(bcx: block, to_end: bool)
+    -> block {
+    let _icx = bcx.insn_ctxt("trans_break_cont");
+    // Locate closest loop block, outputting cleanup as we go.
+    let mut unwind = bcx;
+    let mut target;
+    loop {
+        match unwind.kind {
+          block_scope({loop_break: Some(brk), _}) => {
+            target = if to_end {
+                brk
+            } else {
+                unwind
+            };
+            break;
+          }
+          _ => ()
+        }
+        unwind = match unwind.parent {
+          Some(bcx) => bcx,
+          // This is a return from a loop body block
+          None => {
+            Store(bcx, C_bool(!to_end), bcx.fcx.llretptr);
+            cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
+            Unreachable(bcx);
+            return bcx;
+          }
+        };
+    }
+    cleanup_and_Br(bcx, unwind, target.llbb);
+    Unreachable(bcx);
+    return bcx;
+}
+
+fn trans_break(bcx: block) -> block {
+    return trans_break_cont(bcx, true);
+}
+
+fn trans_cont(bcx: block) -> block {
+    return trans_break_cont(bcx, false);
+}
+
+fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block {
+    let _icx = bcx.insn_ctxt("trans_ret");
+    let mut bcx = bcx;
+    let retptr = match copy bcx.fcx.loop_ret {
+      Some({flagptr, retptr}) => {
+        // This is a loop body return. Must set continue flag (our retptr)
+        // to false, return flag to true, and then store the value in the
+        // parent's retptr.
+        Store(bcx, C_bool(true), flagptr);
+        Store(bcx, C_bool(false), bcx.fcx.llretptr);
+        match e {
+          Some(x) => PointerCast(bcx, retptr,
+                                 T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))),
+          None => retptr
+        }
+      }
+      None => bcx.fcx.llretptr
+    };
+    match e {
+      Some(x) => {
+        bcx = expr::trans_into(bcx, x, expr::SaveIn(retptr));
+      }
+      _ => ()
+    }
+    cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
+    Unreachable(bcx);
+    return bcx;
+}
+fn trans_check_expr(bcx: block, chk_expr: @ast::expr,
+                    pred_expr: @ast::expr, s: ~str) -> block {
+    let _icx = bcx.insn_ctxt("trans_check_expr");
+    let expr_str = s + ~" " + expr_to_str(pred_expr, bcx.ccx().sess.intr())
+        + ~" failed";
+    let Result {bcx, val} = {
+        do with_scope_result(bcx, chk_expr.info(), ~"check") |bcx| {
+            expr::trans_to_appropriate_llval(bcx, pred_expr)
+        }
+    };
+    do with_cond(bcx, Not(bcx, val)) |bcx| {
+        trans_fail(bcx, Some(pred_expr.span), expr_str)
+    }
+}
+
+fn trans_fail_expr(bcx: block,
+                   sp_opt: Option<span>,
+                   fail_expr: Option<@ast::expr>) -> block {
+    let _icx = bcx.insn_ctxt("trans_fail_expr");
+    let mut bcx = bcx;
+    match fail_expr {
+        Some(arg_expr) => {
+            let ccx = bcx.ccx(), tcx = ccx.tcx;
+            let arg_datum = unpack_datum!(
+                bcx, expr::trans_to_datum(bcx, arg_expr));
+
+            if ty::type_is_str(arg_datum.ty) {
+                let (lldata, _lllen) = arg_datum.get_base_and_len(bcx);
+                return trans_fail_value(bcx, sp_opt, lldata);
+            } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) {
+                return bcx;
+            } else {
+                bcx.sess().span_bug(
+                    arg_expr.span, ~"fail called with unsupported type " +
+                    ppaux::ty_to_str(tcx, arg_datum.ty));
+            }
+        }
+        _ => return trans_fail(bcx, sp_opt, ~"explicit failure")
+    }
+}
+
+fn trans_fail(bcx: block, sp_opt: Option<span>, fail_str: ~str)
+    -> block
+{
+    let _icx = bcx.insn_ctxt("trans_fail");
+    let V_fail_str = C_cstr(bcx.ccx(), fail_str);
+    return trans_fail_value(bcx, sp_opt, V_fail_str);
+}
+
+fn trans_fail_value(bcx: block, sp_opt: Option<span>, V_fail_str: ValueRef)
+    -> block
+{
+    let _icx = bcx.insn_ctxt("trans_fail_value");
+    let ccx = bcx.ccx();
+    let {V_filename, V_line} = match sp_opt {
+      Some(sp) => {
+        let sess = bcx.sess();
+        let loc = codemap::lookup_char_pos(sess.parse_sess.cm, sp.lo);
+        {V_filename: C_cstr(bcx.ccx(), loc.file.name),
+         V_line: loc.line as int}
+      }
+      None => {
+        {V_filename: C_cstr(bcx.ccx(), ~"<runtime>"),
+         V_line: 0}
+      }
+    };
+    let V_str = PointerCast(bcx, V_fail_str, T_ptr(T_i8()));
+    let V_filename = PointerCast(bcx, V_filename, T_ptr(T_i8()));
+    let args = ~[V_str, V_filename, C_int(ccx, V_line)];
+    let bcx = callee::trans_rtcall(bcx, ~"fail", args, expr::Ignore);
+    Unreachable(bcx);
+    return bcx;
+}
diff --git a/src/rustc/middle/trans/datum.rs b/src/rustc/middle/trans/datum.rs
new file mode 100644
index 00000000000..4f3591d99ca
--- /dev/null
+++ b/src/rustc/middle/trans/datum.rs
@@ -0,0 +1,703 @@
+/*!
+ *
+ * A `Datum` contains all the information you need to describe the LLVM
+ * translation of a Rust value.  It describes where the value is stored,
+ * what Rust type the value has, whether it is addressed by reference,
+ * and so forth.
+ *
+ * The idea of a datum is that, to the extent possible, you should not
+ * care about these details, but rather use the methods on the Datum
+ * type to "do what you want to do".  For example, you can simply call
+ * `copy_to()` or `move_to()` to copy or move the value into a new
+ * home.
+ *
+ * # Datum location
+ *
+ * The primary two fields of a datum are the `val` and the `mode`.
+ * The `val` is an LLVM value ref.  It may either *be the value* that
+ * is being tracked, or it may be a *pointer to the value being
+ * tracked*.  This is specified in the `mode` field, which can either
+ * be `ByValue` or `ByRef`, respectively.  The (Rust) type of the
+ * value stored in the datum is indicated in the field `ty`.
+ *
+ * Generally speaking, you probably do not want to access the `val` field
+ * unless you know what mode the value is in.  Intead you should use one
+ * of the following accessors:
+ *
+ * - `to_value_llval()` converts to by-value
+ * - `to_ref_llval()` converts to by-ref, allocating a stack slot if necessary
+ * - `to_appropriate_llval()` converts to by-value if this is an
+ *   immediate type, by-ref otherwise.  This is particularly
+ *   convenient for interfacing with the various code floating around
+ *   that predates datums.
+ *
+ * # Datum sources
+ *
+ * Each datum carries with it an idea of its "source".  This indicates
+ * the kind of expression from which the datum originated.  The source
+ * affects what happens when the datum is stored or moved.
+ *
+ * There are three options:
+ *
+ * 1. `FromRvalue`: This value originates from some temporary rvalue.
+ *    This is therefore the owning reference to the datum.  If the
+ *    datum is stored, then, it will be *moved* into its new home.
+ *    Furthermore, we will not zero out the datum but rather use
+ *    `revoke_clean()` to cancel any cleanup.
+ *
+ * 2. `FromLvalue`: This value originates from an lvalue.  If the datum
+ *    is stored, it will be *copied* into its new home.  If the datum
+ *    is moved, it will be zeroed out.
+ *
+ * 3. `FromLastUseLvalue`: The same as FromLvalue, except that it
+ *    originates from the *last use* of an lvalue.  If the datum is
+ *    stored, then, it will be moved (and zeroed out).
+ *
+ * # Storing, copying, and moving
+ *
+ * There are three kinds of methods for moving the value into a new
+ * location.  *Storing* a datum is probably the one you want to reach
+ * for first: it is used when you will no longer use the datum and
+ * would like to place it somewhere.  It may translate to a copy or a
+ * move, depending on the source of the datum.  After a store, the
+ * datum may or may not be usable anymore, so you must assume it is
+ * not.
+ *
+ * Sometimes, though, you want to use an explicit copy or move.  A
+ * copy copies the data from the datum into a new location and
+ * executes the take glue on that location, thus leaving the datum
+ * valid for further use.  Moving, in contrast, copies the data into
+ * the new location and then cancels any cleanups on the current datum
+ * (as appropriate for the source).  No glue code is executed.  After
+ * a move, the datum is no longer usable.
+ *
+ * # Scratch datum
+ *
+ * Sometimes you just need some temporary scratch space.  The
+ * `scratch_datum()` function will yield you up a by-ref datum that
+ * points into the stack.  It's your responsibility to ensure that
+ * whatever you put in there gets cleaned up etc.
+ *
+ * # Other actions
+ *
+ * There are various other helper methods on Datum, such as `deref()`,
+ * `get_base_and_len()` and so forth.  These are documented on the
+ * methods themselves.  Most are only suitable for some types of
+ * values. */
+
+use lib::llvm::ValueRef;
+use base::*;
+use common::*;
+use build::*;
+use util::ppaux::ty_to_str;
+use util::common::indenter;
+
+enum CopyAction {
+    INIT,
+    DROP_EXISTING
+}
+
+struct Datum {
+    /// The llvm value.  This is either a pointer to the Rust value or
+    /// the value itself, depending on `mode` below.
+    val: ValueRef;
+
+    /// The rust type of the value.
+    ty: ty::t;
+
+    /// Indicates whether this is by-ref or by-value.
+    mode: DatumMode;
+
+    /// How did this value originate?  This is particularly important
+    /// if the value is MOVED or prematurely DROPPED, because it
+    /// describes how to cancel the cleanup that was scheduled before.
+    /// See the def'n of the `DatumSource` type.
+    source: DatumSource;
+}
+
+struct DatumBlock {
+    bcx: block;
+    datum: Datum;
+}
+
+enum DatumMode {
+    /// `val` is a pointer to the actual value (and thus has type *T)
+    ByRef,
+
+    /// `val` is the actual value (*only used for immediates* like ints, ptrs)
+    ByValue,
+}
+
+impl DatumMode {
+    fn is_by_ref() -> bool {
+        match self { ByRef => true, ByValue => false }
+    }
+
+    fn is_by_value() -> bool {
+        match self { ByRef => false, ByValue => true }
+    }
+}
+
+/// See `Datum Sources` section at the head of this module.
+enum DatumSource {
+    FromRvalue,
+    FromLvalue,
+    FromLastUseLvalue,
+}
+
+impl DatumSource {
+    fn is_rvalue() -> bool {
+        match self {
+            FromRvalue => true,
+            FromLvalue | FromLastUseLvalue => false
+        }
+    }
+
+    fn is_any_lvalue() -> bool {
+        match self {
+            FromRvalue => false,
+            FromLvalue | FromLastUseLvalue => true
+        }
+    }
+}
+
+fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum {
+    return Datum {val: val, ty: ty,
+                  mode: ByValue, source: FromRvalue};
+}
+
+fn immediate_rvalue_bcx(bcx: block, val: ValueRef, ty: ty::t) -> DatumBlock {
+    return DatumBlock {bcx: bcx, datum: immediate_rvalue(val, ty)};
+}
+
+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! */
+
+    let scratch = alloc_ty(bcx, ty);
+    if zero { zero_mem(bcx, scratch, ty); }
+    Datum { val: scratch, ty: ty, mode: ByRef, source: FromRvalue }
+}
+
+impl Datum {
+    fn store_will_move() -> bool {
+        match self.source {
+            FromRvalue | FromLastUseLvalue => true,
+            FromLvalue => false
+        }
+    }
+
+    fn store_to(bcx: block, action: CopyAction, dst: ValueRef) -> block {
+        /*!
+         *
+         * Stores this value into its final home.  This moves if
+         * possible, but copies otherwise. */
+
+        if self.store_will_move() {
+            self.move_to(bcx, action, dst)
+        } else {
+            self.copy_to(bcx, action, dst)
+        }
+    }
+
+    fn store_to_dest(bcx: block, dest: expr::Dest) -> block {
+        match dest {
+            expr::Ignore => {
+                return bcx;
+            }
+            expr::SaveIn(addr) => {
+                return self.store_to(bcx, INIT, addr);
+            }
+        }
+    }
+
+    fn store_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
+        debug!("store_to_datum(self=%s, action=%?, datum=%s)",
+               self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
+        assert datum.mode.is_by_ref();
+        self.store_to(bcx, action, datum.val)
+    }
+
+    fn move_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
+        assert datum.mode.is_by_ref();
+        self.move_to(bcx, action, datum.val)
+    }
+
+    fn copy_to_datum(bcx: block, action: CopyAction, datum: Datum) -> block {
+        assert datum.mode.is_by_ref();
+        self.copy_to(bcx, action, datum.val)
+    }
+
+    fn copy_to(bcx: block, action: CopyAction, dst: ValueRef) -> block {
+        /*!
+         *
+         * Copies the value into `dst`, which should be a pointer to a
+         * memory location suitable for `self.ty`.  You PROBABLY want
+         * `store_to()` instead, which will move if possible but copy if
+         * neccessary. */
+
+        let _icx = bcx.insn_ctxt("copy_to");
+
+        debug!("copy_to(self=%s, action=%?, dst=%s)",
+               self.to_str(bcx.ccx()), action, bcx.val_str(dst));
+
+        // Watch out for the case where we are writing the copying the
+        // value into the same location we read it out from.  We want
+        // to avoid the case where we drop the existing value, which
+        // frees it, and then overwrite it with itself (which has been
+        // freed).
+        if action == DROP_EXISTING &&
+            ty::type_needs_drop(bcx.tcx(), self.ty)
+        {
+            match self.mode {
+                ByRef => {
+                    let cast = PointerCast(bcx, dst, val_ty(self.val));
+                    let cmp = ICmp(bcx, lib::llvm::IntNE, cast, self.val);
+                    do with_cond(bcx, cmp) |bcx| {
+                        self.copy_to_no_check(bcx, action, dst)
+                    }
+                }
+                ByValue => {
+                    self.copy_to_no_check(bcx, action, dst)
+                }
+            }
+        } else {
+            self.copy_to_no_check(bcx, action, dst)
+        }
+    }
+
+    fn copy_to_no_check(bcx: block, action: CopyAction,
+                        dst: ValueRef) -> block
+    {
+        /*!
+         *
+         * A helper for `copy_to()` which does not check to see if we
+         * are copying to/from the same value. */
+
+        let _icx = bcx.insn_ctxt("copy_to_no_check");
+        let mut bcx = bcx;
+
+        if action == DROP_EXISTING {
+            bcx = glue::drop_ty(bcx, dst, self.ty);
+        }
+
+        match self.mode {
+            ByValue => {
+                Store(bcx, self.val, dst);
+            }
+            ByRef => {
+                memmove_ty(bcx, dst, self.val, self.ty);
+            }
+        }
+
+        return glue::take_ty(bcx, dst, self.ty);
+    }
+
+    // This works like copy_val, except that it deinitializes the source.
+    // Since it needs to zero out the source, src also needs to be an lval.
+    //
+    // FIXME (#839): We always zero out the source. Ideally we would
+    // detect the case where a variable is always deinitialized by
+    // block exit and thus doesn't need to be dropped.
+    fn move_to(bcx: block, action: CopyAction, dst: ValueRef) -> block {
+        let _icx = bcx.insn_ctxt("move_to");
+        let mut bcx = bcx;
+
+        debug!("move_to(self=%s, action=%?, dst=%s)",
+               self.to_str(bcx.ccx()), action, bcx.val_str(dst));
+
+        if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
+            return bcx;
+        }
+
+        if action == DROP_EXISTING {
+            bcx = glue::drop_ty(bcx, dst, self.ty);
+        }
+
+        match self.mode {
+            ByRef => {
+                glue::memmove_ty(bcx, dst, self.val, self.ty);
+            }
+            ByValue => {
+                Store(bcx, self.val, dst);
+            }
+        }
+
+        self.cancel_clean(bcx);
+
+        return bcx;
+    }
+
+    fn add_clean(bcx: block) {
+        /*!
+         *
+         * Schedules this datum for cleanup in `bcx`.  The datum
+         * must be an rvalue. */
+
+        assert self.source.is_rvalue();
+        match self.mode {
+            ByValue => {
+                add_clean_temp_immediate(bcx, self.val, self.ty);
+            }
+            ByRef => {
+                add_clean_temp_mem(bcx, self.val, self.ty);
+            }
+        }
+    }
+
+    fn cancel_clean(bcx: block) {
+        if ty::type_needs_drop(bcx.tcx(), self.ty) {
+            match self.source {
+                FromRvalue => {
+                    revoke_clean(bcx, self.val);
+                }
+                FromLvalue | FromLastUseLvalue => {
+                    // Lvalues which potentially need to be dropped
+                    // must be passed by ref, so that we can zero them
+                    // out.
+                    assert self.mode.is_by_ref();
+                    zero_mem(bcx, self.val, self.ty);
+                }
+            }
+        }
+    }
+
+    fn to_str(ccx: &crate_ctxt) -> ~str {
+        fmt!("Datum { val=%s, ty=%s, mode=%?, source=%? }",
+             val_str(ccx.tn, self.val),
+             ty_to_str(ccx.tcx, self.ty),
+             self.mode,
+             self.source)
+    }
+
+    fn to_value_llval(bcx: block) -> ValueRef {
+        /*!
+         *
+         * Yields the value itself. */
+
+        match self.mode {
+            ByValue => self.val,
+            ByRef => Load(bcx, self.val)
+        }
+    }
+
+    fn to_ref(bcx: block) -> Datum {
+        /*!
+         *
+         * Yields a by-ref form of this datum.  This may involve
+         * creation of a temporary stack slot.  The value returned by
+         * this function is not separately rooted from this datum, so
+         * it will not live longer than the current datum. */
+
+        match self.mode {
+            ByRef => self,
+            ByValue => {
+                Datum {val: self.to_ref_llval(bcx), mode: ByRef,
+                       ty: self.ty, source: FromRvalue}
+            }
+        }
+    }
+
+    fn to_ref_llval(bcx: block) -> ValueRef {
+        match self.mode {
+            ByRef => self.val,
+            ByValue => {
+                let slot = alloc_ty(bcx, self.ty);
+                Store(bcx, self.val, slot);
+                slot
+            }
+        }
+    }
+
+    fn to_appropriate_llval(bcx: block) -> ValueRef {
+        /*!
+         *
+         * Yields something that is by value if the type is immediate
+         * and by ref otherwise. */
+
+        if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
+            self.to_value_llval(bcx)
+        } else if ty::type_is_immediate(self.ty) {
+            self.to_value_llval(bcx)
+        } else {
+            self.to_ref_llval(bcx)
+        }
+    }
+
+    fn GEPi(bcx: block, ixs: &[uint], ty: ty::t) -> Datum {
+        let base_val = self.to_ref_llval(bcx);
+        Datum {
+            val: GEPi(bcx, base_val, ixs),
+            mode: ByRef,
+            ty: ty,
+            source: FromLvalue
+        }
+    }
+
+    fn root(bcx: block, scope_id: ast::node_id) {
+        /*!
+         *
+         * In some cases, borrowck will decide that an @T/@[]/@str
+         * value must be rooted for the program to be safe.  In that
+         * case, we will call this function, which will stash a copy
+         * away until we exit the scope `scope_id`. */
+
+        debug!("root(scope_id=%?, self=%?)",
+               scope_id, self.to_str(bcx.ccx()));
+
+        if bcx.sess().trace() {
+            trans_trace(
+                bcx, None,
+                fmt!("preserving until end of scope %d", scope_id));
+        }
+
+        let scratch = scratch_datum(bcx, self.ty, true);
+        self.copy_to_datum(bcx, INIT, scratch);
+        base::add_root_cleanup(bcx, scope_id, scratch.val, scratch.ty);
+    }
+
+    fn drop_val(bcx: block) -> block {
+        if !ty::type_needs_drop(bcx.tcx(), self.ty) {
+            return bcx;
+        }
+
+        return match self.mode {
+            ByRef => glue::drop_ty(bcx, self.val, self.ty),
+            ByValue => glue::drop_ty_immediate(bcx, self.val, self.ty)
+        };
+    }
+
+    fn box_body(bcx: block) -> Datum {
+        /*!
+         *
+         * This datum must represent an @T or ~T box.  Returns a new
+         * by-ref datum of type T, pointing at the contents. */
+
+        let content_ty = match ty::get(self.ty).struct {
+            ty::ty_box(mt) | ty::ty_uniq(mt) => mt.ty,
+            _ => {
+                bcx.tcx().sess.bug(fmt!(
+                    "box_body() invoked on non-box type %s",
+                    ty_to_str(bcx.tcx(), self.ty)));
+            }
+        };
+
+        let ptr = self.to_value_llval(bcx);
+        let body = opaque_box_body(bcx, content_ty, ptr);
+        Datum {val: body, ty: content_ty, mode: ByRef, source: FromLvalue}
+    }
+
+    fn to_rptr(bcx: block) -> Datum {
+        //!
+        //
+        // Returns a new datum of region-pointer type containing the
+        // the same ptr as this datum (after converting to by-ref
+        // using `to_ref_llval()`).
+
+        // Convert to ref, yielding lltype *T.  Then create a Rust
+        // type &static/T (which translates to *T).  Construct new
+        // result (which will be by-value).  Note that it is not
+        // significant *which* region we pick here.
+        let llval = self.to_ref_llval(bcx);
+        let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static,
+                                      self.ty);
+        Datum {val: llval, ty: rptr_ty,
+               mode: ByValue, source: FromRvalue}
+    }
+
+    fn try_deref(
+        bcx: block,            // block wherein to generate insn's
+        expr_id: ast::node_id, // id of expr being deref'd
+        derefs: uint,          // number of times deref'd already
+        is_auto: bool)         // if true, only deref if auto-derefable
+        -> Option<Datum>
+    {
+        let ccx = bcx.ccx();
+
+        debug!("try_deref(expr_id=%d, derefs=%?, is_auto=%b, self=%?)",
+               expr_id, derefs, is_auto, self.to_str(bcx.ccx()));
+        let _indenter = indenter();
+
+        // root the autoderef'd value, if necessary:
+        //
+        // (Note: root'd values are always boxes)
+        match ccx.maps.root_map.find({id:expr_id, derefs:derefs}) {
+            None => (),
+            Some(scope_id) => {
+                self.root(bcx, scope_id);
+            }
+        }
+
+        match ty::get(self.ty).struct {
+            ty::ty_box(_) | ty::ty_uniq(_) => {
+                return Some(self.box_body(bcx));
+            }
+            ty::ty_ptr(mt) => {
+                if is_auto { // unsafe ptrs are not AUTO-derefable
+                    return None;
+                } else {
+                    return Some(deref_ptr(bcx, &self, mt.ty));
+                }
+            }
+            ty::ty_rptr(_, mt) => {
+                return Some(deref_ptr(bcx, &self, mt.ty));
+            }
+            ty::ty_enum(did, ref substs) => {
+                // Check whether this enum is a newtype enum:
+                let variants = ty::enum_variants(ccx.tcx, did);
+                if (*variants).len() != 1u || variants[0].args.len() != 1u {
+                    return None;
+                }
+
+                let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]);
+                return match self.mode {
+                    ByRef => {
+                        // Recast lv.val as a pointer to the newtype
+                        // rather than a ptr to the enum type.
+                        let llty = T_ptr(type_of::type_of(ccx, ty));
+                        Some(Datum {
+                            val: PointerCast(bcx, self.val, llty),
+                            ty: ty,
+                            mode: ByRef,
+                            source: FromLvalue
+                        })
+                    }
+                    ByValue => {
+                        // Actually, this case cannot happen right
+                        // now, because enums are never immediate.
+                        // But in principle newtype'd immediate
+                        // values should be immediate, and in that
+                        // case the * would be a no-op except for
+                        // changing the type, so I am putting this
+                        // code in place here to do the right
+                        // thing if this change ever goes through.
+                        assert ty::type_is_immediate(ty);
+                        Some(Datum {ty: ty, ..self})
+                    }
+                };
+            }
+            _ => { // not derefable.
+                return None;
+            }
+        }
+
+        fn deref_ptr(bcx: block, lv: &Datum, ty: ty::t) -> Datum {
+            Datum {
+                val: lv.to_value_llval(bcx),
+                ty: ty,
+                mode: ByRef,
+                source: FromLvalue // *p is an lvalue
+            }
+        }
+    }
+
+    fn deref(bcx: block,
+             expr: @ast::expr,  // the expression whose value is being deref'd
+             derefs: uint) -> Datum {
+        match self.try_deref(bcx, expr.id, derefs, false) {
+            Some(lvres) => lvres,
+            None => {
+                bcx.ccx().sess.span_bug(
+                    expr.span, ~"Cannot deref this expression");
+            }
+        }
+    }
+
+    fn autoderef(bcx: block,
+                 expr_id: ast::node_id,
+                 max: uint) -> Datum {
+        let _icx = bcx.insn_ctxt("autoderef");
+
+        debug!("autoderef(expr_id=%d, max=%?, self=%?)",
+               expr_id, max, self.to_str(bcx.ccx()));
+        let _indenter = indenter();
+
+        let mut datum = self;
+        let mut derefs = 0u;
+        while derefs < max {
+            derefs += 1u;
+            match datum.try_deref(bcx, expr_id, derefs, true) {
+                None => break,
+                Some(datum_deref) => {
+                    datum = datum_deref;
+                }
+            }
+        }
+
+        // either we were asked to deref a specific number of times,
+        // in which case we should have, or we asked to deref as many
+        // times as we can
+        assert derefs == max || max == uint::max_value;
+        datum
+    }
+
+    fn get_base_and_len(bcx: block) -> (ValueRef, ValueRef) {
+        tvec::get_base_and_len(bcx, self.to_appropriate_llval(bcx), self.ty)
+    }
+
+    fn to_result(bcx: block) -> common::Result {
+        rslt(bcx, self.to_appropriate_llval(bcx))
+    }
+}
+
+impl DatumBlock {
+    fn unpack(bcx: &mut block) -> Datum {
+        *bcx = self.bcx;
+        return self.datum;
+    }
+
+    fn assert_by_ref() -> DatumBlock {
+        assert self.datum.mode.is_by_ref();
+        self
+    }
+
+    fn drop_val() -> block {
+        self.datum.drop_val(self.bcx)
+    }
+
+    fn store_to(action: CopyAction, dst: ValueRef) -> block {
+        self.datum.store_to(self.bcx, action, dst)
+    }
+
+    fn copy_to(action: CopyAction, dst: ValueRef) -> block {
+        self.datum.copy_to(self.bcx, action, dst)
+    }
+
+    fn move_to(action: CopyAction, dst: ValueRef) -> block {
+        self.datum.move_to(self.bcx, action, dst)
+    }
+
+    fn to_value_llval() -> ValueRef {
+        self.datum.to_value_llval(self.bcx)
+    }
+
+    fn to_result() -> common::Result {
+        rslt(self.bcx, self.datum.to_appropriate_llval(self.bcx))
+    }
+
+    fn ccx() -> @crate_ctxt {
+        self.bcx.ccx()
+    }
+
+    fn tcx() -> ty::ctxt {
+        self.bcx.tcx()
+    }
+
+    fn to_str() -> ~str {
+        self.datum.to_str(self.ccx())
+    }
+}
+
+impl CopyAction : cmp::Eq {
+    pure fn eq(&&other: CopyAction) -> bool {
+        match (self, other) {
+            (INIT, INIT) => true,
+            (DROP_EXISTING, DROP_EXISTING) => true,
+            (INIT, _) => false,
+            (DROP_EXISTING, _) => false,
+        }
+    }
+}
diff --git a/src/rustc/middle/trans/expr.rs b/src/rustc/middle/trans/expr.rs
new file mode 100644
index 00000000000..8b2acf1c0b5
--- /dev/null
+++ b/src/rustc/middle/trans/expr.rs
@@ -0,0 +1,1371 @@
+/*!
+
+# Translation of expressions.
+
+## User's guide
+
+If you wish to translate an expression, there are two basic modes:
+
+1. `trans_into(block, expr, Dest) -> block`
+2. `trans_to_datum(block, expr) -> DatumBlock`
+
+`trans_into()` is the preferred form to use whenever possible.  It
+evaluates the expression and stores its result into `Dest`, which
+must either be the special flag ignore (throw the result away) or
+be a pointer to memory of the same type/size as the expression.
+
+Sometimes, though, you just want to evaluate the expression into
+some memory location so you can go and inspect it (e.g., a `match`
+expression).  In that case, `trans_to_datum()` is your friend.  It
+will evaluate the expression and return a `Datum` describing where
+the result is to be found.  This function tries to return its
+result in the most efficient way possible, without introducing
+extra copies or sacrificing information.  Therefore, for lvalue
+expressions, you always get a by-ref `Datum` in return that points
+at the memory for this lvalue (almost, see [1]).  For rvalue
+expressions, we will return a by-value `Datum` whenever possible,
+but it is often necessary to allocate a stack slot, store the
+result of the rvalue in there, and then return a pointer to the
+slot (see the discussion later on about the different kinds of
+rvalues).
+
+## More specific functions
+
+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
+  you will get a by-ref Datum back (almost, see [1]).
+- `trans_local_var()` can be used to trans a ref to a local variable
+  that is not an expression.
+
+## Ownership and cleanups
+
+The current system for cleanups associates required cleanups with
+block contexts.  Block contexts are structured into a tree that
+resembles the code itself.  Not every block context has cleanups
+associated with it, only those blocks that have a kind of
+`block_scope`.  See `common::block_kind` for more details.
+
+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.
+
+During the evaluation of an expression, temporary cleanups are created
+and later canceled.  These represent intermediate or partial results
+which must be cleaned up in the event of task failure.
+
+## Implementation details
+
+We divide expressions into three categories, based on how they are most
+naturally implemented:
+
+1. Lvalues
+2. Datum rvalues
+3. DPS rvalues
+4. Statement rvalues
+
+Lvalues always refer to user-assignable memory locations.
+Translating those always results in a by-ref datum; this introduces
+no inefficiencies into the generated code, because all lvalues are
+naturally addressable.
+
+Datum rvalues are rvalues that always generate datums as a result.
+These are generally scalar results, such as `a+b` where `a` and `b`
+are integers.
+
+DPS rvalues are rvalues that, when translated, must be given a
+memory location to write into (or the Ignore flag).  These are
+generally expressions that produce structural results that are
+larger than one word (e.g., a struct literal), but also expressions
+(like `if`) that involve control flow (otherwise we'd have to
+generate phi nodes).
+
+Finally, statement rvalues are rvalues that always produce a nil
+return type, such as `while` loops or assignments (`a = b`).
+
+## Caveats
+
+[1] Actually, some lvalues are only stored by value and not by
+reference.  An example (as of this writing) would be immutable
+arguments or pattern bindings of immediate type.  However, mutable
+lvalues are *never* stored by value.
+
+*/
+
+use ty::class_items_as_mutable_fields;
+use lib::llvm::ValueRef;
+use common::*;
+use datum::*;
+use base::*;
+use syntax::print::pprust::{expr_to_str};
+use util::ppaux::ty_to_str;
+use util::common::indenter;
+
+// The primary two functions for translating expressions:
+export trans_to_datum, trans_into;
+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
+// destination of a computation's value.
+
+fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
+
+enum Dest {
+    SaveIn(ValueRef),
+    Ignore,
+}
+
+impl Dest {
+    fn to_str(ccx: @crate_ctxt) -> ~str {
+        match self {
+            SaveIn(v) => fmt!("SaveIn(%s)", val_str(ccx.tn, v)),
+            Ignore => ~"Ignore"
+        }
+    }
+}
+
+impl Dest : cmp::Eq {
+    pure fn eq(&&other: Dest) -> bool {
+        match (self, other) {
+            (SaveIn(e0a), SaveIn(e0b)) => e0a == e0b,
+            (Ignore, Ignore) => true,
+            (SaveIn(*), _) => false,
+            (Ignore, _) => false,
+        }
+    }
+}
+
+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 {
+    /*!
+     *
+     * Translates an expression into a datum.  If this expression
+     * is an rvalue, this will result in a temporary value being
+     * created.  If you already know where the result should be stored,
+     * you should use `trans_into()` instead. */
+
+    let mut bcx = bcx;
+
+    debug!("trans_to_datum(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);
+        }
+
+        ty::RvalueDatumExpr => {
+            let datum = unpack_datum!(bcx, trans_rvalue_datum(bcx, expr));
+            datum.add_clean(bcx);
+            return DatumBlock {bcx: bcx, datum: datum};
+        }
+
+        ty::RvalueStmtExpr => {
+            bcx = trans_rvalue_stmt(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);
+                return nil(bcx, ty);
+            } else {
+                let scratch = scratch_datum(bcx, ty, false);
+                bcx = trans_rvalue_dps(bcx, expr, SaveIn(scratch.val));
+                scratch.add_clean(bcx);
+                return DatumBlock {bcx: bcx, datum: scratch};
+            }
+        }
+    }
+
+    fn nil(bcx: block, ty: ty::t) -> DatumBlock {
+        let datum = immediate_rvalue(C_nil(), ty);
+        DatumBlock {bcx: bcx, datum: datum}
+    }
+}
+
+fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
+    let ty = expr_ty(bcx, expr);
+
+    debug!("trans_into(expr=%s, dest=%s)",
+           bcx.expr_to_str(expr),
+           dest.to_str(bcx.ccx()));
+    let _indenter = indenter();
+
+    debuginfo::update_source_pos(bcx, expr.span);
+
+    let dest = {
+        if ty::type_is_nil(ty) || ty::type_is_bot(ty) {
+            Ignore
+        } else {
+            dest
+        }
+    };
+
+    let kind = bcx.expr_kind(expr);
+    debug!("expr kind = %?", kind);
+    match kind {
+        ty::LvalueExpr => {
+            let datumblock = trans_lvalue(bcx, expr);
+            match dest {
+                Ignore => datumblock.bcx,
+                SaveIn(lldest) => datumblock.store_to(INIT, lldest)
+            }
+        }
+        ty::RvalueDatumExpr => {
+            let datumblock = trans_rvalue_datum(bcx, expr);
+            match dest {
+                Ignore => datumblock.drop_val(),
+                SaveIn(lldest) => datumblock.store_to(INIT, lldest)
+            }
+        }
+        ty::RvalueDpsExpr => {
+            return trans_rvalue_dps(bcx, expr, dest);
+        }
+        ty::RvalueStmtExpr => {
+            return trans_rvalue_stmt(bcx, expr);
+        }
+    }
+}
+
+fn trans_rvalue_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
+    let _icx = bcx.insn_ctxt("trans_rvalue_datum");
+
+    trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
+
+    match expr.node {
+        ast::expr_vstore(contents, ast::vstore_box) => {
+            return tvec::trans_uniq_or_managed_vstore(bcx, heap_shared,
+                                                      expr, contents);
+        }
+        ast::expr_vstore(contents, ast::vstore_uniq) => {
+            return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
+                                                      expr, contents);
+        }
+        ast::expr_lit(lit) => {
+            return trans_immediate_lit(bcx, expr, *lit);
+        }
+        ast::expr_binary(op, lhs, rhs) => {
+            // if overloaded, would be RvalueDpsExpr
+            assert !bcx.ccx().maps.method_map.contains_key(expr.id);
+
+            return trans_binary(bcx, expr, op, lhs, rhs);
+        }
+        ast::expr_unary(op, x) => {
+            return trans_unary_datum(bcx, expr, op, x);
+        }
+        ast::expr_addr_of(_, x) => {
+            return trans_addr_of(bcx, expr, x);
+        }
+        ast::expr_cast(val, _) => {
+            return trans_imm_cast(bcx, val, expr.id);
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(
+                expr.span,
+                fmt!("trans_rvalue_datum reached fall-through case: %?",
+                     expr.node));
+        }
+    }
+}
+
+fn trans_rvalue_stmt(bcx: block, expr: @ast::expr) -> block {
+    let mut bcx = bcx;
+    let _icx = bcx.insn_ctxt("trans_rvalue_stmt");
+
+    trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
+
+    match expr.node {
+        ast::expr_break(label_opt) => {
+            if label_opt.is_some() {
+                bcx.tcx().sess.span_unimpl(expr.span, ~"labeled break");
+            }
+            return controlflow::trans_break(bcx);
+        }
+        ast::expr_again(label_opt) => {
+            if label_opt.is_some() {
+                bcx.tcx().sess.span_unimpl(expr.span, ~"labeled again");
+            }
+            return controlflow::trans_cont(bcx);
+        }
+        ast::expr_ret(ex) => {
+            return controlflow::trans_ret(bcx, ex);
+        }
+        ast::expr_fail(why) => {
+            return controlflow::trans_fail_expr(bcx, Some(expr.span), why);
+        }
+        ast::expr_log(_, lvl, a) => {
+            return controlflow::trans_log(expr, lvl, bcx, a);
+        }
+        ast::expr_assert(a) => {
+            return controlflow::trans_check_expr(bcx, expr, a, ~"Assertion");
+        }
+        ast::expr_while(cond, body) => {
+            return controlflow::trans_while(bcx, cond, body);
+        }
+        ast::expr_loop(body, _) => {
+            return controlflow::trans_loop(bcx, body);
+        }
+        ast::expr_assign(dst, src) => {
+            let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src));
+            let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
+            return src_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum);
+        }
+        ast::expr_move(dst, src) => {
+            let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src));
+            let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
+            return src_datum.move_to_datum(bcx, DROP_EXISTING, dst_datum);
+        }
+        ast::expr_swap(dst, src) => {
+            let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
+            let src_datum = unpack_datum!(bcx, trans_lvalue(bcx, src));
+            let scratch = scratch_datum(bcx, dst_datum.ty, false);
+
+            let bcx = dst_datum.move_to_datum(bcx, INIT, scratch);
+            let bcx = src_datum.move_to_datum(bcx, INIT, dst_datum);
+            return scratch.move_to_datum(bcx, INIT, src_datum);
+        }
+        ast::expr_assign_op(op, dst, src) => {
+            return trans_assign_op(bcx, expr, op, dst, src);
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(
+                expr.span,
+                fmt!("trans_rvalue_stmt reached fall-through case: %?",
+                     expr.node));
+        }
+    };
+}
+
+fn trans_rvalue_dps(bcx: block, expr: @ast::expr, dest: Dest) -> block {
+    let mut bcx = bcx;
+    let _icx = bcx.insn_ctxt("trans_rvalue_dps");
+    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);
+        }
+        ast::expr_if(cond, thn, els) => {
+            return controlflow::trans_if(bcx, cond, thn, els, dest);
+        }
+        ast::expr_match(discr, arms) => {
+            return alt::trans_alt(bcx, expr, discr, arms, dest);
+        }
+        ast::expr_block(blk) => {
+            return do base::with_scope(bcx, blk.info(),
+                                       ~"block-expr body") |bcx| {
+                controlflow::trans_block(bcx, blk, dest)
+            };
+        }
+        ast::expr_rec(fields, base) | ast::expr_struct(_, fields, base) => {
+            return trans_rec_or_struct(bcx, fields, base, expr.id, dest);
+        }
+        ast::expr_tup(args) => {
+            return trans_tup(bcx, args, dest);
+        }
+        ast::expr_lit(@{node: ast::lit_str(s), _}) => {
+            return tvec::trans_lit_str(bcx, expr, s, dest);
+        }
+        ast::expr_vstore(contents, ast::vstore_slice(_)) => {
+            return tvec::trans_slice_vstore(bcx, expr, contents, dest);
+        }
+        ast::expr_vstore(contents, ast::vstore_fixed(_)) => {
+            return tvec::trans_fixed_vstore(bcx, expr, contents, dest);
+        }
+        ast::expr_vec(*) | ast::expr_repeat(*) => {
+            return tvec::trans_fixed_vstore(bcx, expr, expr, dest);
+        }
+        ast::expr_fn(proto, decl, body, cap_clause) => {
+            // Don't use this function for anything real. Use the one in
+            // astconv instead.
+            fn ast_proto_to_proto_simple(ast_proto: ast::proto)
+                -> ty::fn_proto {
+                match ast_proto {
+                    ast::proto_bare => ty::proto_bare,
+                    ast::proto_uniq => ty::proto_vstore(ty::vstore_uniq),
+                    ast::proto_box => ty::proto_vstore(ty::vstore_box),
+                    ast::proto_block => {
+                        ty::proto_vstore(ty::vstore_slice(ty::re_static))
+                    }
+                }
+            }
+
+            return closure::trans_expr_fn(bcx,
+                                          ast_proto_to_proto_simple(proto),
+                                          decl, body, expr.id, cap_clause,
+                                          None, dest);
+        }
+        ast::expr_fn_block(decl, body, cap_clause) => {
+            let expr_ty = expr_ty(bcx, expr);
+            match ty::get(expr_ty).struct {
+                ty::ty_fn({proto, _}) => {
+                    debug!("translating fn_block %s with type %s",
+                           expr_to_str(expr, tcx.sess.intr()),
+                           ty_to_str(tcx, expr_ty));
+                    return closure::trans_expr_fn(bcx, proto, decl, body,
+                                                  expr.id, cap_clause, None,
+                                                  dest);
+                }
+                _ => {
+                    bcx.sess().impossible_case(
+                        expr.span, "fn_block has body with a non-fn type");
+                }
+            }
+        }
+        ast::expr_loop_body(blk) => {
+            match ty::get(expr_ty(bcx, expr)).struct {
+                ty::ty_fn({proto, _}) => {
+                    match blk.node {
+                        ast::expr_fn_block(decl, body, cap) => {
+                            return closure::trans_expr_fn(
+                                bcx, proto, decl, body, blk.id,
+                                cap, Some(None), dest);
+                        }
+                        _ => {
+                            bcx.sess().impossible_case(
+                                expr.span,
+                                "loop_body has the wrong kind of contents")
+                        }
+                    }
+                }
+                _ => {
+                    bcx.sess().impossible_case(
+                        expr.span, "loop_body has body with a non-fn type")
+                }
+            }
+        }
+        ast::expr_do_body(blk) => {
+            return trans_into(bcx, blk, dest);
+        }
+        ast::expr_copy(a) => {
+            return trans_into(bcx, a, dest);
+        }
+        ast::expr_unary_move(a) => {
+            if bcx.expr_is_lval(a) {
+                let datum = unpack_datum!(bcx, trans_to_datum(bcx, a));
+                return match dest {
+                    Ignore => datum.drop_val(bcx),
+                    SaveIn(addr) => datum.move_to(bcx, INIT, addr)
+                };
+            } else {
+                return trans_into(bcx, a, dest);
+            }
+        }
+        ast::expr_call(f, args, _) => {
+            return callee::trans_call(
+                bcx, expr, f, callee::ArgExprs(args), expr.id, dest);
+        }
+        ast::expr_binary(_, lhs, rhs) => {
+            // if not overloaded, would be RvalueDatumExpr
+            return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest);
+        }
+        ast::expr_unary(_, subexpr) => {
+            // if not overloaded, would be RvalueDatumExpr
+            return trans_overloaded_op(bcx, expr, subexpr, ~[], dest);
+        }
+        ast::expr_index(base, idx) => {
+            // if not overloaded, would be RvalueDatumExpr
+            return trans_overloaded_op(bcx, expr, base, ~[idx], dest);
+        }
+        ast::expr_cast(val, _) => {
+            return impl::trans_trait_cast(bcx, val, expr.id, dest);
+        }
+        ast::expr_assign_op(op, dst, src) => {
+            return trans_assign_op(bcx, expr, op, dst, src);
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(
+                expr.span,
+                fmt!("trans_rvalue_dps 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");
+    let ccx = bcx.ccx();
+
+    let lldest = match dest {
+        SaveIn(lldest) => lldest,
+        Ignore => { return bcx; }
+    };
+
+    match def {
+        ast::def_fn(did, _) => {
+            let fn_data = callee::trans_fn_ref(bcx, did, ref_expr.id);
+            return fn_data_to_datum(bcx, did, fn_data, lldest);
+        }
+        ast::def_static_method(did, _) => {
+            let fn_data = impl::trans_static_method_callee(bcx, did,
+                                                           ref_expr.id);
+            return fn_data_to_datum(bcx, did, fn_data, lldest);
+        }
+        ast::def_variant(tid, vid) => {
+            if ty::enum_variant_with_id(ccx.tcx, tid, vid).args.len() > 0u {
+                // N-ary variant.
+                let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id);
+                return fn_data_to_datum(bcx, vid, fn_data, lldest);
+            } else {
+                // Nullary variant.
+                let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]);
+                let lldiscrim_gv = base::lookup_discriminant(ccx, vid);
+                let lldiscrim = Load(bcx, lldiscrim_gv);
+                Store(bcx, lldiscrim, lldiscrimptr);
+                return bcx;
+            }
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(ref_expr.span, fmt!(
+                "Non-DPS def %? referened by %s",
+                def, bcx.node_id_to_str(ref_expr.id)));
+        }
+    }
+}
+
+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.
+
+    let _icx = bcx.insn_ctxt("trans_lval");
+    let mut bcx = bcx;
+
+    debug!("trans_lvalue(expr=%s)", bcx.expr_to_str(expr));
+    let _indenter = indenter();
+
+    trace_span!(bcx, expr.span, shorten(bcx.expr_to_str(expr)));
+
+    let unrooted_datum = unpack_datum!(bcx, unrooted(bcx, expr));
+
+    // If the lvalue must remain rooted, create a scratch datum, copy
+    // the lvalue in there, and then arrange for it to be cleaned up
+    // at the end of the scope with id `scope_id`:
+    let root_key = {id:expr.id, derefs:0u};
+    for bcx.ccx().maps.root_map.find(root_key).each |scope_id| {
+        unrooted_datum.root(bcx, scope_id);
+    }
+
+    return DatumBlock {bcx: bcx, datum: unrooted_datum};
+
+    fn unrooted(bcx: block, expr: @ast::expr) -> DatumBlock {
+        let mut bcx = bcx;
+
+        match expr.node {
+            ast::expr_path(_) => {
+                return trans_def_lvalue(bcx, expr, bcx.def(expr.id));
+            }
+            ast::expr_field(base, ident, _) => {
+                return trans_rec_field(bcx, base, ident);
+            }
+            ast::expr_index(base, idx) => {
+                return trans_index(bcx, expr, base, idx);
+            }
+            ast::expr_unary(ast::deref, base) => {
+                let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base));
+                let derefdatum = basedatum.deref(bcx, base, 0);
+                return DatumBlock {bcx: bcx, datum: derefdatum};
+            }
+            _ => {
+                bcx.tcx().sess.span_bug(
+                    expr.span,
+                    fmt!("trans_lvalue reached fall-through case: %?",
+                         expr.node));
+            }
+        }
+    }
+}
+
+fn trans_def_lvalue(bcx: block, ref_expr: @ast::expr,
+                    def: ast::def) -> DatumBlock {
+    let _icx = bcx.insn_ctxt("trans_def_lvalue");
+    let ccx = bcx.ccx();
+    match def {
+        ast::def_const(did) => {
+            let const_ty = expr_ty(bcx, ref_expr);
+            let val = if did.crate == ast::local_crate {
+                base::get_item_val(ccx, did.node)
+            } else {
+                base::trans_external_path(ccx, did, const_ty)
+            };
+            DatumBlock {
+                bcx: bcx,
+                datum: Datum {val: val,
+                              ty: const_ty,
+                              mode: ByRef,
+                              source: FromLvalue}
+            }
+        }
+        _ => {
+            DatumBlock {
+                bcx: bcx,
+                datum: trans_local_var(bcx, ref_expr.id, def)
+            }
+        }
+    }
+}
+
+fn trans_local_var(bcx: block, ref_id: ast::node_id, def: ast::def) -> Datum {
+    let _icx = bcx.insn_ctxt("trans_local_var");
+
+    return match def {
+        ast::def_upvar(nid, _, _, _) => {
+            let local_ty = node_id_type(bcx, nid);
+            match bcx.fcx.llupvars.find(nid) {
+                Some(val) => {
+                    Datum {
+                        val: val,
+                        ty: local_ty,
+                        mode: ByRef,
+                        source: FromLvalue
+                    }
+                }
+                None => {
+                    bcx.sess().bug(fmt!(
+                        "trans_local_var: no llval for upvar %? found", nid));
+                }
+            }
+        }
+        ast::def_arg(nid, _) => {
+            take_local(bcx, ref_id, bcx.fcx.llargs, nid)
+        }
+        ast::def_local(nid, _) | ast::def_binding(nid, _) => {
+            take_local(bcx, ref_id, bcx.fcx.lllocals, nid)
+        }
+        ast::def_self(nid) => {
+            let self_info: ValSelfData = match bcx.fcx.llself {
+                Some(ref self_info) => *self_info,
+                None => {
+                    bcx.sess().bug(fmt!(
+                        "trans_local_var: reference to self \
+                         out of context with id %?", nid));
+                }
+            };
+
+            // This cast should not be necessary. We should cast self *once*,
+            // but right now this conflicts with default methods.
+            let llselfty = T_ptr(type_of::type_of(bcx.ccx(), self_info.t));
+            let casted_val = PointerCast(bcx, self_info.v, llselfty);
+            Datum {
+                val: casted_val,
+                ty: self_info.t,
+                mode: ByRef,
+                source: FromLvalue
+            }
+        }
+        _ => {
+            bcx.sess().unimpl(fmt!(
+                "unsupported def type in trans_local_var: %?", def));
+        }
+    };
+
+    fn take_local(bcx: block,
+                  ref_id: ast::node_id,
+                  table: hashmap<ast::node_id, local_val>,
+                  nid: ast::node_id) -> Datum {
+        let is_last_use = match bcx.ccx().maps.last_use_map.find(ref_id) {
+            None => false,
+            Some(vars) => (*vars).contains(nid)
+        };
+
+        let source = if is_last_use {FromLastUseLvalue} else {FromLvalue};
+
+        let (v, mode) = match table.find(nid) {
+            Some(local_mem(v)) => (v, ByRef),
+            Some(local_imm(v)) => (v, ByValue),
+            None => {
+                bcx.sess().bug(fmt!(
+                    "trans_local_var: no llval for local/arg %? found", nid));
+            }
+        };
+        let ty = node_id_type(bcx, nid);
+
+        debug!("take_local(nid=%?, last_use=%b, v=%s, mode=%?, ty=%s)",
+               nid, is_last_use, bcx.val_str(v), mode, bcx.ty_to_str(ty));
+
+        Datum { val: v, ty: ty, mode: mode, source: source }
+    }
+}
+
+fn fn_data_to_datum(bcx: block,
+                    def_id: ast::def_id,
+                    fn_data: callee::FnData,
+                    lldest: ValueRef) -> block {
+    //!
+    //
+    // Translates a reference to a top-level fn item into a rust
+    // value.  This is generally a Rust closure pair: (fn ptr, env)
+    // where the environment is NULL.  However, extern functions for
+    // interfacing with C are represted as just the fn ptr with type
+    // *u8.
+    //
+    // Strictly speaking, references to extern fns ought to be
+    // RvalueDatumExprs, but it's not worth the complexity to avoid the
+    // extra stack slot that LLVM probably optimizes away anyhow.
+
+    let fn_tpt = ty::lookup_item_type(bcx.tcx(), def_id);
+    if ty::ty_fn_purity(fn_tpt.ty) == ast::extern_fn {
+        let val = PointerCast(bcx, fn_data.llfn, T_ptr(T_i8()));
+        Store(bcx, val, lldest);
+        return bcx;
+    }
+
+    let llfn = GEPi(bcx, lldest, [0u, abi::fn_field_code]);
+    Store(bcx, fn_data.llfn, llfn);
+    let llenv = GEPi(bcx, lldest, [0u, abi::fn_field_box]);
+    Store(bcx, base::null_env_ptr(bcx), llenv);
+    return bcx;
+}
+
+fn with_field_tys<R>(tcx: ty::ctxt, ty: ty::t,
+                     op: fn(bool, (&[ty::field])) -> R) -> R {
+    match ty::get(ty).struct {
+        ty::ty_rec(ref fields) => {
+            op(false, *fields)
+        }
+
+        ty::ty_class(did, ref substs) => {
+            let has_dtor = ty::ty_dtor(tcx, did).is_some();
+            op(has_dtor, class_items_as_mutable_fields(tcx, did, substs))
+        }
+
+        _ => {
+            tcx.sess.bug(fmt!(
+                "cannot get field types from the type %s",
+                ty_to_str(tcx, ty)));
+        }
+    }
+}
+
+fn trans_rec_field(bcx: block,
+                   base: @ast::expr,
+                   field: ast::ident) -> DatumBlock {
+    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 {
+            datum: base_datum.GEPi(bcx, [0u, 0u, ix], field_tys[ix].mt.ty),
+            bcx: bcx
+        }
+    }
+}
+
+fn trans_index(bcx: block,
+               index_expr: @ast::expr,
+               base: @ast::expr,
+               idx: @ast::expr) -> DatumBlock {
+    let _icx = bcx.insn_ctxt("trans_index");
+    let ccx = bcx.ccx();
+    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 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 = {
+        if ix_size < int_size {
+            if ty::type_is_signed(expr_ty(bcx, idx)) {
+                SExt(bcx, ix_val, ccx.int_type)
+            } else { ZExt(bcx, ix_val, ccx.int_type) }
+        } else if ix_size > int_size {
+            Trunc(bcx, ix_val, ccx.int_type)
+        } else {
+            ix_val
+        }
+    };
+
+    let vt = tvec::vec_types(bcx, base_datum.ty);
+    base::maybe_name_value(bcx.ccx(), vt.llunit_size, ~"unit_sz");
+    let scaled_ix = Mul(bcx, ix_val, vt.llunit_size);
+    base::maybe_name_value(bcx.ccx(), scaled_ix, ~"scaled_ix");
+
+    let mut (base, len) = base_datum.get_base_and_len(bcx);
+
+    if ty::type_is_str(base_ty) {
+        // acccount for null terminator in the case of string
+        len = Sub(bcx, len, C_uint(bcx.ccx(), 1u));
+    }
+
+    debug!("trans_index: base %s", val_str(bcx.ccx().tn, base));
+    debug!("trans_index: len %s", val_str(bcx.ccx().tn, len));
+
+    let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, len);
+    let bcx = do with_cond(bcx, bounds_check) |bcx| {
+        controlflow::trans_fail(bcx, Some(index_expr.span), ~"bounds check")
+    };
+    let elt = InBoundsGEP(bcx, base, ~[ix_val]);
+    let elt = PointerCast(bcx, elt, T_ptr(vt.llunit_ty));
+    return DatumBlock {
+        bcx: bcx,
+        datum: Datum {val: elt, ty: vt.unit_ty,
+                      mode: ByRef, source: FromLvalue}
+    };
+}
+
+fn trans_rec_or_struct(bcx: block,
+                       fields: &[ast::field],
+                       base: Option<@ast::expr>,
+                       id: ast::node_id,
+                       dest: Dest) -> block
+{
+    let _icx = bcx.insn_ctxt("trans_rec");
+    let mut bcx = bcx;
+
+    // Handle the case where the result is ignored.
+    let addr;
+    match dest {
+        SaveIn(p) => {
+            addr = p;
+        }
+        Ignore => {
+            // just evaluate the values for each field and drop them
+            // on the floor
+            for vec::each(fields) |fld| {
+                bcx = trans_into(bcx, fld.node.expr, Ignore);
+            }
+            return bcx;
+        }
+    }
+
+    let ty = node_id_type(bcx, id);
+    let tcx = bcx.tcx();
+    do with_field_tys(tcx, ty) |has_dtor, field_tys| {
+        // evaluate each of the fields and store them into their
+        // correct locations
+        let mut temp_cleanups = ~[];
+        for fields.each |field| {
+            let ix = ty::field_idx_strict(tcx, field.node.ident, field_tys);
+            let dest = GEPi(bcx, addr, struct_field(ix));
+            bcx = trans_into(bcx, field.node.expr, SaveIn(dest));
+            add_clean_temp_mem(bcx, dest, field_tys[ix].mt.ty);
+            vec::push(temp_cleanups, dest);
+        }
+
+        // copy over any remaining fields from the base (for
+        // functional record update)
+        for base.each |base_expr| {
+            let base_datum = unpack_datum!(
+                bcx, trans_to_datum(bcx, base_expr));
+
+            // Copy over inherited fields
+            for field_tys.eachi |i, field_ty| {
+                if !fields.any(|f| f.node.ident == field_ty.ident) {
+                    let dest = GEPi(bcx, addr, struct_field(i));
+                    let base_field =
+                        base_datum.GEPi(bcx, struct_field(i), field_ty.mt.ty);
+                    bcx = base_field.store_to(bcx, INIT, dest);
+                }
+            }
+        }
+
+        // Add the drop flag if necessary.
+        if has_dtor {
+            let dest = GEPi(bcx, addr, struct_dtor());
+            Store(bcx, C_u8(1), dest);
+        }
+
+        // Now revoke the cleanups as we pass responsibility for the data
+        // structure on to the caller
+        for temp_cleanups.each |cleanup| { revoke_clean(bcx, cleanup); }
+        bcx
+    }
+}
+
+fn trans_tup(bcx: block, elts: ~[@ast::expr], dest: Dest) -> block {
+    let _icx = bcx.insn_ctxt("trans_tup");
+    let mut bcx = bcx;
+    let addr = match dest {
+        Ignore => {
+            for vec::each(elts) |ex| { bcx = trans_into(bcx, ex, Ignore); }
+            return bcx;
+        }
+        SaveIn(pos) => pos,
+    };
+    let mut temp_cleanups = ~[];
+    for vec::eachi(elts) |i, e| {
+        let dest = GEPi(bcx, addr, [0u, i]);
+        let e_ty = expr_ty(bcx, e);
+        bcx = trans_into(bcx, e, SaveIn(dest));
+        add_clean_temp_mem(bcx, dest, e_ty);
+        vec::push(temp_cleanups, dest);
+    }
+    for vec::each(temp_cleanups) |cleanup| { revoke_clean(bcx, cleanup); }
+    return bcx;
+}
+
+fn trans_immediate_lit(bcx: block, expr: @ast::expr,
+                       lit: ast::lit) -> DatumBlock {
+    // must not be a string constant, that is a RvalueDpsExpr
+    let _icx = bcx.insn_ctxt("trans_immediate_lit");
+    let ty = expr_ty(bcx, expr);
+    immediate_rvalue_bcx(bcx, consts::const_lit(bcx.ccx(), expr, lit), ty)
+}
+
+fn trans_unary_datum(bcx: block,
+                     un_expr: @ast::expr,
+                     op: ast::unop,
+                     sub_expr: @ast::expr) -> DatumBlock {
+
+    let _icx = bcx.insn_ctxt("trans_unary_datum");
+
+    // if deref, would be LvalueExpr
+    assert op != ast::deref;
+
+    // if overloaded, would be RvalueDpsExpr
+    assert !bcx.ccx().maps.method_map.contains_key(un_expr.id);
+
+    let un_ty = expr_ty(bcx, un_expr);
+    let sub_ty = expr_ty(bcx, sub_expr);
+
+    return match op {
+        ast::not => {
+            let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
+            immediate_rvalue_bcx(bcx, Not(bcx, val), un_ty)
+        }
+        ast::neg => {
+            let Result {bcx, val} = trans_to_appropriate_llval(bcx, sub_expr);
+            let llneg = {
+                if ty::type_is_fp(un_ty) {
+                    FNeg(bcx, val)
+                } else {
+                    Neg(bcx, val)
+                }
+            };
+            immediate_rvalue_bcx(bcx, llneg, un_ty)
+        }
+        ast::box(_) => {
+            trans_boxed_expr(bcx, un_ty, sub_expr, sub_ty, heap_shared)
+        }
+        ast::uniq(_) => {
+            trans_boxed_expr(bcx, un_ty, sub_expr, sub_ty, heap_exchange)
+        }
+        ast::deref => {
+            bcx.sess().bug(~"deref expressions should have been \
+                             translated using trans_lvalue(), not \
+                             trans_unary_datum()")
+        }
+    };
+
+    fn trans_boxed_expr(bcx: block,
+                        box_ty: ty::t,
+                        contents: @ast::expr,
+                        contents_ty: ty::t,
+                        heap: heap) -> DatumBlock {
+        let _icx = bcx.insn_ctxt("trans_boxed_expr");
+        let {bcx, box, body} =
+            base::malloc_general(bcx, contents_ty, heap);
+        add_clean_free(bcx, box, heap);
+        let bcx = trans_into(bcx, contents, SaveIn(body));
+        revoke_clean(bcx, box);
+        return immediate_rvalue_bcx(bcx, box, box_ty);
+    }
+}
+
+fn trans_addr_of(bcx: block, expr: @ast::expr,
+                 subexpr: @ast::expr) -> DatumBlock {
+    let _icx = bcx.insn_ctxt("trans_addr_of");
+    let mut bcx = bcx;
+    let sub_datum = unpack_datum!(bcx, trans_to_datum(bcx, subexpr));
+    let llval = sub_datum.to_ref_llval(bcx);
+    return immediate_rvalue_bcx(bcx, llval, expr_ty(bcx, expr));
+}
+
+// Important to get types for both lhs and rhs, because one might be _|_
+// and the other not.
+fn trans_eager_binop(bcx: block,
+                     binop_expr: @ast::expr,
+                     binop_ty: ty::t,
+                     op: ast::binop,
+                     lhs_datum: &Datum,
+                     rhs_datum: &Datum) -> DatumBlock
+{
+    let mut bcx = bcx;
+    let _icx = bcx.insn_ctxt("trans_eager_binop");
+
+    let lhs = lhs_datum.to_appropriate_llval(bcx);
+    let lhs_t = lhs_datum.ty;
+
+    let rhs = rhs_datum.to_appropriate_llval(bcx);
+    let rhs_t = rhs_datum.ty;
+
+    let intype = {
+        if ty::type_is_bot(lhs_t) { rhs_t }
+        else { lhs_t }
+    };
+    let is_float = ty::type_is_fp(intype);
+
+    let rhs = base::cast_shift_expr_rhs(bcx, op, lhs, rhs);
+
+    let mut bcx = bcx;
+    let val = match op {
+      ast::add => {
+        if is_float { FAdd(bcx, lhs, rhs) }
+        else { Add(bcx, lhs, rhs) }
+      }
+      ast::subtract => {
+        if is_float { FSub(bcx, lhs, rhs) }
+        else { Sub(bcx, lhs, rhs) }
+      }
+      ast::mul => {
+        if is_float { FMul(bcx, lhs, rhs) }
+        else { Mul(bcx, lhs, rhs) }
+      }
+      ast::div => {
+        if is_float {
+            FDiv(bcx, lhs, rhs)
+        } else {
+            // Only zero-check integers; fp /0 is NaN
+            bcx = base::fail_if_zero(bcx, binop_expr.span,
+                                     op, rhs, rhs_t);
+            if ty::type_is_signed(intype) {
+                SDiv(bcx, lhs, rhs)
+            } else {
+                UDiv(bcx, lhs, rhs)
+            }
+        }
+      }
+      ast::rem => {
+        if is_float {
+            FRem(bcx, lhs, rhs)
+        } else {
+            // Only zero-check integers; fp %0 is NaN
+            bcx = base::fail_if_zero(bcx, binop_expr.span,
+                                     op, rhs, rhs_t);
+            if ty::type_is_signed(intype) {
+                SRem(bcx, lhs, rhs)
+            } else {
+                URem(bcx, lhs, rhs)
+            }
+        }
+      }
+      ast::bitor => Or(bcx, lhs, rhs),
+      ast::bitand => And(bcx, lhs, rhs),
+      ast::bitxor => Xor(bcx, lhs, rhs),
+      ast::shl => Shl(bcx, lhs, rhs),
+      ast::shr => {
+        if ty::type_is_signed(intype) {
+            AShr(bcx, lhs, rhs)
+        } else { LShr(bcx, lhs, rhs) }
+      }
+      _ => {
+        let cmpr = base::trans_compare(bcx, op,
+                                       lhs, lhs_t,
+                                       rhs, rhs_t);
+        bcx = cmpr.bcx;
+        cmpr.val
+      }
+    };
+
+    return immediate_rvalue_bcx(bcx, val, binop_ty);
+}
+
+// refinement types would obviate the need for this
+enum lazy_binop_ty { lazy_and, lazy_or }
+
+fn trans_lazy_binop(bcx: block,
+                    binop_expr: @ast::expr,
+                    op: lazy_binop_ty,
+                    a: @ast::expr,
+                    b: @ast::expr) -> DatumBlock
+{
+    let _icx = bcx.insn_ctxt("trans_lazy_binop");
+    let binop_ty = expr_ty(bcx, binop_expr);
+    let mut bcx = bcx;
+
+    let Result {bcx: past_lhs, val: lhs} = {
+        do base::with_scope_result(bcx, a.info(), ~"lhs") |bcx| {
+            trans_to_appropriate_llval(bcx, a)
+        }
+    };
+
+    if past_lhs.unreachable {
+        return immediate_rvalue_bcx(past_lhs, lhs, binop_ty);
+    }
+
+    let join = base::sub_block(bcx, ~"join");
+    let before_rhs = base::sub_block(bcx, ~"rhs");
+
+    match op {
+      lazy_and => CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb),
+      lazy_or => CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb)
+    }
+    let Result {bcx: past_rhs, val: rhs} = {
+        do base::with_scope_result(before_rhs, b.info(), ~"rhs") |bcx| {
+            trans_to_appropriate_llval(bcx, b)
+        }
+    };
+
+    if past_rhs.unreachable {
+        return immediate_rvalue_bcx(join, lhs, binop_ty);
+    }
+
+    Br(past_rhs, join.llbb);
+    let phi = Phi(join, T_bool(), ~[lhs, rhs], ~[past_lhs.llbb,
+                                                 past_rhs.llbb]);
+
+    return immediate_rvalue_bcx(join, phi, binop_ty);
+}
+
+fn trans_binary(bcx: block,
+                binop_expr: @ast::expr,
+                op: ast::binop,
+                lhs: @ast::expr,
+                rhs: @ast::expr) -> DatumBlock
+{
+    let _icx = bcx.insn_ctxt("trans_binary");
+
+    match op {
+        ast::and => {
+            trans_lazy_binop(bcx, binop_expr, lazy_and, lhs, rhs)
+        }
+        ast::or => {
+            trans_lazy_binop(bcx, binop_expr, lazy_or, lhs, rhs)
+        }
+        _ => {
+            let mut bcx = bcx;
+            let lhs_datum = unpack_datum!(bcx, trans_to_datum(bcx, lhs));
+            let rhs_datum = unpack_datum!(bcx, trans_to_datum(bcx, rhs));
+            let binop_ty = expr_ty(bcx, binop_expr);
+            trans_eager_binop(bcx, binop_expr, binop_ty, op,
+                              &lhs_datum, &rhs_datum)
+        }
+    }
+}
+
+fn trans_overloaded_op(bcx: block,
+                       expr: @ast::expr,
+                       rcvr: @ast::expr,
+                       +args: ~[@ast::expr],
+                       dest: Dest) -> block
+{
+    let origin = bcx.ccx().maps.method_map.get(expr.id);
+    let fty = node_id_type(bcx, expr.callee_id);
+    return callee::trans_call_inner(
+        bcx, expr.info(), fty,
+        expr_ty(bcx, expr),
+        |bcx| impl::trans_method_callee(bcx, expr.callee_id, rcvr, origin),
+        callee::ArgExprs(args), dest);
+}
+
+fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef,
+            llsrc: ValueRef, signed: bool) -> ValueRef {
+    let _icx = bcx.insn_ctxt("int_cast");
+    let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype);
+    let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype);
+    return if dstsz == srcsz {
+        BitCast(bcx, llsrc, lldsttype)
+    } else if srcsz > dstsz {
+        TruncOrBitCast(bcx, llsrc, lldsttype)
+    } else if signed {
+        SExtOrBitCast(bcx, llsrc, lldsttype)
+    } else { ZExtOrBitCast(bcx, llsrc, lldsttype) };
+}
+
+fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef,
+              llsrc: ValueRef) -> ValueRef {
+    let _icx = bcx.insn_ctxt("float_cast");
+    let srcsz = lib::llvm::float_width(llsrctype);
+    let dstsz = lib::llvm::float_width(lldsttype);
+    return if dstsz > srcsz {
+        FPExt(bcx, llsrc, lldsttype)
+    } else if srcsz > dstsz {
+        FPTrunc(bcx, llsrc, lldsttype)
+    } else { llsrc };
+}
+
+enum cast_kind {
+    cast_pointer,
+    cast_integral,
+    cast_float,
+    cast_enum,
+    cast_other,
+}
+
+impl cast_kind : cmp::Eq {
+    pure fn eq(&&other: cast_kind) -> bool {
+        match (self, other) {
+            (cast_pointer, cast_pointer) => true,
+            (cast_integral, cast_integral) => true,
+            (cast_float, cast_float) => true,
+            (cast_enum, cast_enum) => true,
+            (cast_other, cast_other) => true,
+            (cast_pointer, _) => false,
+            (cast_integral, _) => false,
+            (cast_float, _) => false,
+            (cast_enum, _) => false,
+            (cast_other, _) => false,
+        }
+    }
+}
+
+fn cast_type_kind(t: ty::t) -> cast_kind {
+    match ty::get(t).struct {
+        ty::ty_float(*)   => cast_float,
+        ty::ty_ptr(*)     => cast_pointer,
+        ty::ty_rptr(*)    => cast_pointer,
+        ty::ty_int(*)     => cast_integral,
+        ty::ty_uint(*)    => cast_integral,
+        ty::ty_bool       => cast_integral,
+        ty::ty_enum(*)    => cast_enum,
+        _                 => cast_other
+    }
+}
+
+fn trans_imm_cast(bcx: block, expr: @ast::expr,
+                  id: ast::node_id) -> DatumBlock {
+    let _icx = bcx.insn_ctxt("trans_cast");
+    let ccx = bcx.ccx();
+
+    let t_out = node_id_type(bcx, id);
+
+    let mut bcx = bcx;
+    let llexpr = unpack_result!(bcx, trans_to_appropriate_llval(bcx, expr));
+    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);
+
+    let k_in = cast_type_kind(t_in);
+    let k_out = cast_type_kind(t_out);
+    let s_in = k_in == cast_integral && ty::type_is_signed(t_in);
+
+    let newval =
+        match {in: k_in, out: k_out} {
+            {in: cast_integral, out: cast_integral} => {
+                int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in)
+            }
+            {in: cast_float, out: cast_float} => {
+                float_cast(bcx, ll_t_out, ll_t_in, llexpr)
+            }
+            {in: cast_integral, out: cast_float} => {
+                if s_in {
+                    SIToFP(bcx, llexpr, ll_t_out)
+                } else { UIToFP(bcx, llexpr, ll_t_out) }
+            }
+            {in: cast_float, out: cast_integral} => {
+                if ty::type_is_signed(t_out) {
+                    FPToSI(bcx, llexpr, ll_t_out)
+                } else { FPToUI(bcx, llexpr, ll_t_out) }
+            }
+            {in: cast_integral, out: cast_pointer} => {
+                IntToPtr(bcx, llexpr, ll_t_out)
+            }
+            {in: cast_pointer, out: cast_integral} => {
+                PtrToInt(bcx, llexpr, ll_t_out)
+            }
+            {in: cast_pointer, out: cast_pointer} => {
+                PointerCast(bcx, llexpr, ll_t_out)
+            }
+            {in: cast_enum, out: cast_integral} |
+            {in: cast_enum, out: cast_float} => {
+                let bcx = bcx;
+                let llenumty = T_opaque_enum_ptr(ccx);
+                let av_enum = PointerCast(bcx, llexpr, llenumty);
+                let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]);
+                let lldiscrim_a = Load(bcx, lldiscrim_a_ptr);
+                match k_out {
+                    cast_integral => int_cast(bcx, ll_t_out,
+                                              val_ty(lldiscrim_a),
+                                              lldiscrim_a, true),
+                    cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out),
+                    _ => ccx.sess.bug(~"translating unsupported cast.")
+                }
+            }
+            _ => ccx.sess.bug(~"translating unsupported cast.")
+        };
+    return immediate_rvalue_bcx(bcx, newval, t_out);
+}
+
+fn trans_assign_op(bcx: block,
+                   expr: @ast::expr,
+                   op: ast::binop,
+                   dst: @ast::expr,
+                   src: @ast::expr) -> block
+{
+    let _icx = bcx.insn_ctxt("trans_assign_op");
+    let mut bcx = bcx;
+
+    debug!("trans_assign_op(expr=%s)", bcx.expr_to_str(expr));
+
+    // Evaluate LHS (destination), which should be an lvalue
+    let dst_datum = unpack_datum!(bcx, trans_lvalue(bcx, dst));
+
+    // A user-defined operator method
+    if bcx.ccx().maps.method_map.find(expr.id).is_some() {
+        // FIXME(#2582) evaluates the receiver twice!!
+        let scratch = scratch_datum(bcx, dst_datum.ty, false);
+        let bcx = trans_overloaded_op(bcx, expr, dst, ~[src],
+                                      SaveIn(scratch.val));
+        return scratch.move_to_datum(bcx, DROP_EXISTING, dst_datum);
+    }
+
+    // Evaluate RHS (source)
+    let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src));
+
+    // Perform computation and store the result
+    let result_datum =
+        unpack_datum!(bcx,
+                      trans_eager_binop(
+                          bcx, expr, dst_datum.ty, op,
+                          &dst_datum, &src_datum));
+    return result_datum.store_to_datum(bcx, DROP_EXISTING, dst_datum);
+}
+
+fn shorten(+x: ~str) -> ~str {
+    if x.len() > 60 { x.substr(0, 60) } else { x }
+}
\ No newline at end of file
diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs
index 504aea89214..cd2be095178 100644
--- a/src/rustc/middle/trans/foreign.rs
+++ b/src/rustc/middle/trans/foreign.rs
@@ -17,6 +17,9 @@ use base::*;
 use type_of::*;
 use std::map::hashmap;
 use util::ppaux::ty_to_str;
+use datum::*;
+use callee::*;
+use expr::{Dest, Ignore};
 
 export link_name, trans_foreign_mod, register_foreign_fn, trans_foreign_fn,
        trans_intrinsic;
@@ -87,8 +90,8 @@ fn classify_ty(ty: TypeRef) -> ~[x86_64_reg_class] {
             Double => 8,
             Struct => {
               do vec::foldl(0, struct_tys(ty)) |a, t| {
-                    uint::max(a, ty_align(t))
-                }
+                  uint::max(a, ty_align(t))
+              }
             }
             Array => {
                 let elt = llvm::LLVMGetElementType(ty);
@@ -785,210 +788,209 @@ fn trans_foreign_mod(ccx: @crate_ctxt,
 
 fn trans_intrinsic(ccx: @crate_ctxt, decl: ValueRef, item: @ast::foreign_item,
                    path: ast_map::path, substs: param_substs,
-                   ref_id: Option<ast::node_id>) {
+                   ref_id: Option<ast::node_id>)
+{
+    debug!("trans_intrinsic(item.ident=%s)", ccx.sess.str_of(item.ident));
+
     let fcx = new_fn_ctxt_w_id(ccx, path, decl, item.id,
                                Some(substs), Some(item.span));
     let mut bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
     match ccx.sess.str_of(item.ident) {
-      ~"atomic_xchg" => {
-        let old = AtomicRMW(bcx, Xchg,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  SequentiallyConsistent);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xchg_acq" => {
-        let old = AtomicRMW(bcx, Xchg,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  Acquire);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xchg_rel" => {
-        let old = AtomicRMW(bcx, Xchg,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  Release);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xadd" => {
-        let old = AtomicRMW(bcx, lib::llvm::Add,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  SequentiallyConsistent);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xadd_acq" => {
-        let old = AtomicRMW(bcx, lib::llvm::Add,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  Acquire);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xadd_rel" => {
-        let old = AtomicRMW(bcx, lib::llvm::Add,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  Release);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xsub" => {
-        let old = AtomicRMW(bcx, lib::llvm::Sub,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  SequentiallyConsistent);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xsub_acq" => {
-        let old = AtomicRMW(bcx, lib::llvm::Sub,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  Acquire);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"atomic_xsub_rel" => {
-        let old = AtomicRMW(bcx, lib::llvm::Sub,
-                  get_param(decl, first_real_arg),
-                  get_param(decl, first_real_arg + 1u),
-                  Release);
-        Store(bcx, old, fcx.llretptr);
-      }
-      ~"size_of" => {
-        let tp_ty = substs.tys[0];
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        Store(bcx, C_uint(ccx, shape::llsize_of_real(ccx, lltp_ty)),
-              fcx.llretptr);
-      }
-      ~"move_val" => {
-        let tp_ty = substs.tys[0];
-        let src = {bcx: bcx,
-                   val: get_param(decl, first_real_arg + 1u),
-                   kind: lv_owned};
-        bcx = move_val(bcx, DROP_EXISTING,
-                       get_param(decl, first_real_arg),
-                       src,
-                       tp_ty);
-      }
-      ~"move_val_init" => {
-        let tp_ty = substs.tys[0];
-        let src = {bcx: bcx,
-                   val: get_param(decl, first_real_arg + 1u),
-                   kind: lv_owned};
-        bcx = move_val(bcx, INIT,
-                       get_param(decl, first_real_arg),
-                       src,
-                       tp_ty);
-      }
-      ~"min_align_of" => {
-        let tp_ty = substs.tys[0];
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        Store(bcx, C_uint(ccx, shape::llalign_of_min(ccx, lltp_ty)),
-              fcx.llretptr);
-      }
-      ~"pref_align_of"=> {
-        let tp_ty = substs.tys[0];
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        Store(bcx, C_uint(ccx, shape::llalign_of_pref(ccx, lltp_ty)),
-              fcx.llretptr);
-      }
-      ~"get_tydesc" => {
-        let tp_ty = substs.tys[0];
-        let static_ti = get_tydesc(ccx, tp_ty);
-        lazily_emit_all_tydesc_glue(ccx, static_ti);
-        // FIXME (#2712): change this to T_ptr(ccx.tydesc_ty) when the
-        // core::sys copy of the get_tydesc interface dies off.
-        let td = PointerCast(bcx, static_ti.tydesc, T_ptr(T_nil()));
-        Store(bcx, td, fcx.llretptr);
-      }
-      ~"init" => {
-        let tp_ty = substs.tys[0];
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        if !ty::type_is_nil(tp_ty) {
-            Store(bcx, C_null(lltp_ty), fcx.llretptr);
+        ~"atomic_xchg" => {
+            let old = AtomicRMW(bcx, Xchg,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                SequentiallyConsistent);
+            Store(bcx, old, fcx.llretptr);
         }
-      }
-      ~"forget" => {}
-      ~"reinterpret_cast" => {
-        let tp_ty = substs.tys[0];
-        let lltp_ty = type_of::type_of(ccx, tp_ty);
-        let llout_ty = type_of::type_of(ccx, substs.tys[1]);
-        let tp_sz = shape::llsize_of_real(ccx, lltp_ty),
-            out_sz = shape::llsize_of_real(ccx, llout_ty);
-        if tp_sz != out_sz {
-            let sp = match ccx.tcx.items.get(option::get(ref_id)) {
-              ast_map::node_expr(e) => e.span,
-              _ => fail ~"reinterpret_cast or forget has non-expr arg"
-            };
-            ccx.sess.span_fatal(
-                sp, fmt!("reinterpret_cast called on types \
-                          with different size: %s (%u) to %s (%u)",
-                         ty_to_str(ccx.tcx, tp_ty), tp_sz,
-                         ty_to_str(ccx.tcx, substs.tys[1]), out_sz));
+        ~"atomic_xchg_acq" => {
+            let old = AtomicRMW(bcx, Xchg,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                Acquire);
+            Store(bcx, old, fcx.llretptr);
         }
-        if !ty::type_is_nil(substs.tys[1]) {
-            // NB: Do not use a Load and Store here. This causes massive code
-            // bloat when reinterpret_cast is used on large structural types.
-            let llretptr = PointerCast(bcx, fcx.llretptr, T_ptr(T_i8()));
-            let llcast = get_param(decl, first_real_arg);
-            let llcast = PointerCast(bcx, llcast, T_ptr(T_i8()));
-            call_memmove(bcx, llretptr, llcast, llsize_of(ccx, lltp_ty));
+        ~"atomic_xchg_rel" => {
+            let old = AtomicRMW(bcx, Xchg,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                Release);
+            Store(bcx, old, fcx.llretptr);
         }
+        ~"atomic_xadd" => {
+            let old = AtomicRMW(bcx, lib::llvm::Add,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                SequentiallyConsistent);
+            Store(bcx, old, fcx.llretptr);
+        }
+        ~"atomic_xadd_acq" => {
+            let old = AtomicRMW(bcx, lib::llvm::Add,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                Acquire);
+            Store(bcx, old, fcx.llretptr);
+        }
+        ~"atomic_xadd_rel" => {
+            let old = AtomicRMW(bcx, lib::llvm::Add,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                Release);
+            Store(bcx, old, fcx.llretptr);
+        }
+        ~"atomic_xsub" => {
+            let old = AtomicRMW(bcx, lib::llvm::Sub,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                SequentiallyConsistent);
+            Store(bcx, old, fcx.llretptr);
+        }
+        ~"atomic_xsub_acq" => {
+            let old = AtomicRMW(bcx, lib::llvm::Sub,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                Acquire);
+            Store(bcx, old, fcx.llretptr);
+        }
+        ~"atomic_xsub_rel" => {
+            let old = AtomicRMW(bcx, lib::llvm::Sub,
+                                get_param(decl, first_real_arg),
+                                get_param(decl, first_real_arg + 1u),
+                                Release);
+            Store(bcx, old, fcx.llretptr);
+        }
+        ~"size_of" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            Store(bcx, C_uint(ccx, shape::llsize_of_real(ccx, lltp_ty)),
+                  fcx.llretptr);
+        }
+        ~"move_val" => {
+            let tp_ty = substs.tys[0];
+            let src = Datum {val: get_param(decl, first_real_arg + 1u),
+                             ty: tp_ty, mode: ByRef, source: FromLvalue};
+            bcx = src.move_to(bcx, DROP_EXISTING,
+                              get_param(decl, first_real_arg));
+        }
+        ~"move_val_init" => {
+            let tp_ty = substs.tys[0];
+            let src = Datum {val: get_param(decl, first_real_arg + 1u),
+                             ty: tp_ty, mode: ByRef, source: FromLvalue};
+            bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
+        }
+        ~"min_align_of" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            Store(bcx, C_uint(ccx, shape::llalign_of_min(ccx, lltp_ty)),
+                  fcx.llretptr);
+        }
+        ~"pref_align_of"=> {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            Store(bcx, C_uint(ccx, shape::llalign_of_pref(ccx, lltp_ty)),
+                  fcx.llretptr);
+        }
+        ~"get_tydesc" => {
+            let tp_ty = substs.tys[0];
+            let static_ti = get_tydesc(ccx, tp_ty);
+            glue::lazily_emit_all_tydesc_glue(ccx, static_ti);
+
+            // FIXME (#2712): change this to T_ptr(ccx.tydesc_ty) when the
+            // core::sys copy of the get_tydesc interface dies off.
+            let td = PointerCast(bcx, static_ti.tydesc, T_ptr(T_nil()));
+            Store(bcx, td, fcx.llretptr);
+        }
+        ~"init" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            if !ty::type_is_nil(tp_ty) {
+                Store(bcx, C_null(lltp_ty), fcx.llretptr);
+            }
+        }
+        ~"forget" => {}
+        ~"reinterpret_cast" => {
+            let tp_ty = substs.tys[0];
+            let lltp_ty = type_of::type_of(ccx, tp_ty);
+            let llout_ty = type_of::type_of(ccx, substs.tys[1]);
+            let tp_sz = shape::llsize_of_real(ccx, lltp_ty),
+            out_sz = shape::llsize_of_real(ccx, llout_ty);
+          if tp_sz != out_sz {
+              let sp = match ccx.tcx.items.get(option::get(ref_id)) {
+                  ast_map::node_expr(e) => e.span,
+                  _ => fail ~"reinterpret_cast or forget has non-expr arg"
+              };
+              ccx.sess.span_fatal(
+                  sp, fmt!("reinterpret_cast called on types \
+                            with different size: %s (%u) to %s (%u)",
+                           ty_to_str(ccx.tcx, tp_ty), tp_sz,
+                           ty_to_str(ccx.tcx, substs.tys[1]), out_sz));
+          }
+          if !ty::type_is_nil(substs.tys[1]) {
+              // NB: Do not use a Load and Store here. This causes
+              // massive code bloat when reinterpret_cast is used on
+              // large structural types.
+              let llretptr = PointerCast(bcx, fcx.llretptr, T_ptr(T_i8()));
+              let llcast = get_param(decl, first_real_arg);
+              let llcast = PointerCast(bcx, llcast, T_ptr(T_i8()));
+              call_memmove(bcx, llretptr, llcast, llsize_of(ccx, lltp_ty));
+          }
       }
-      ~"addr_of" => {
-        Store(bcx, get_param(decl, first_real_arg), fcx.llretptr);
-      }
-      ~"needs_drop" => {
-        let tp_ty = substs.tys[0];
-        Store(bcx, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)),
-              fcx.llretptr);
-      }
-      ~"visit_tydesc" => {
-        let td = get_param(decl, first_real_arg);
-        let visitor = get_param(decl, first_real_arg + 1u);
-        let td = PointerCast(bcx, td, T_ptr(ccx.tydesc_type));
-        call_tydesc_glue_full(bcx, visitor, td,
-                              abi::tydesc_field_visit_glue, None);
-      }
-      ~"frame_address" => {
-        let frameaddress = ccx.intrinsics.get(~"llvm.frameaddress");
-        let frameaddress_val = Call(bcx, frameaddress, ~[C_i32(0i32)]);
-        let fty = ty::mk_fn(bcx.tcx(), {
-            purity: ast::impure_fn,
-            proto:
-                ty::proto_vstore(ty::vstore_slice(
-                    ty::re_bound(ty::br_anon(0)))),
-            bounds: @~[],
-            inputs: ~[{
-                mode: ast::expl(ast::by_val),
-                ty: ty::mk_imm_ptr(
-                    bcx.tcx(),
-                    ty::mk_mach_uint(bcx.tcx(), ast::ty_u8))
-            }],
-            output: ty::mk_nil(bcx.tcx()),
-            ret_style: ast::return_val
-        });
-        bcx = trans_call_inner(bcx, None, fty, ty::mk_nil(bcx.tcx()),
-                               |bcx| lval_no_env(
-                                   bcx,
-                                   get_param(decl, first_real_arg),
-                                   lv_temporary),
-                               arg_vals(~[frameaddress_val]), ignore);
-      }
-      ~"morestack_addr" => {
-        // XXX This is a hack to grab the address of this particular
-        // native function. There should be a general in-language
-        // way to do this
-        let llfty = type_of_fn(bcx.ccx(), ~[], ty::mk_nil(bcx.tcx()));
-        let morestack_addr = decl_cdecl_fn(
-            bcx.ccx().llmod, ~"__morestack", llfty);
-        let morestack_addr = PointerCast(bcx, morestack_addr, T_ptr(T_nil()));
-        Store(bcx, morestack_addr, fcx.llretptr);
-      }
-      _ => {
-      // Could we make this an enum rather than a string? does it get
-      // checked earlier?
-          ccx.sess.span_bug(item.span, ~"unknown intrinsic");
-      }
+        ~"addr_of" => {
+            Store(bcx, get_param(decl, first_real_arg), fcx.llretptr);
+        }
+        ~"needs_drop" => {
+            let tp_ty = substs.tys[0];
+            Store(bcx, C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)),
+                  fcx.llretptr);
+        }
+        ~"visit_tydesc" => {
+            let td = get_param(decl, first_real_arg);
+            let visitor = get_param(decl, first_real_arg + 1u);
+            let td = PointerCast(bcx, td, T_ptr(ccx.tydesc_type));
+            glue::call_tydesc_glue_full(bcx, visitor, td,
+                                        abi::tydesc_field_visit_glue, None);
+        }
+        ~"frame_address" => {
+            let frameaddress = ccx.intrinsics.get(~"llvm.frameaddress");
+            let frameaddress_val = Call(bcx, frameaddress, ~[C_i32(0i32)]);
+            let fty = ty::mk_fn(bcx.tcx(), {
+                purity: ast::impure_fn,
+                proto:
+                    ty::proto_vstore(ty::vstore_slice(
+                        ty::re_bound(ty::br_anon(0)))),
+                bounds: @~[],
+                inputs: ~[{
+                    mode: ast::expl(ast::by_val),
+                    ty: ty::mk_imm_ptr(
+                        bcx.tcx(),
+                        ty::mk_mach_uint(bcx.tcx(), ast::ty_u8))
+                }],
+                output: ty::mk_nil(bcx.tcx()),
+                ret_style: ast::return_val
+            });
+            let datum = Datum {val: get_param(decl, first_real_arg),
+                               mode: ByRef, ty: fty, source: FromLvalue};
+            bcx = trans_call_inner(
+                bcx, None, fty, ty::mk_nil(bcx.tcx()),
+                |bcx| Callee {bcx: bcx, data: Closure(datum)},
+                ArgVals(~[frameaddress_val]), Ignore);
+        }
+        ~"morestack_addr" => {
+            // XXX This is a hack to grab the address of this particular
+            // native function. There should be a general in-language
+            // way to do this
+            let llfty = type_of_fn(bcx.ccx(), ~[], ty::mk_nil(bcx.tcx()));
+            let morestack_addr = decl_cdecl_fn(
+                bcx.ccx().llmod, ~"__morestack", llfty);
+            let morestack_addr = PointerCast(bcx, morestack_addr,
+                                             T_ptr(T_nil()));
+            Store(bcx, morestack_addr, fcx.llretptr);
+        }
+        _ => {
+            // Could we make this an enum rather than a string? does it get
+            // checked earlier?
+            ccx.sess.span_bug(item.span, ~"unknown intrinsic");
+        }
     }
     build_return(bcx);
     finish_fn(fcx, lltop);
diff --git a/src/rustc/middle/trans/glue.rs b/src/rustc/middle/trans/glue.rs
new file mode 100644
index 00000000000..3aee04755f0
--- /dev/null
+++ b/src/rustc/middle/trans/glue.rs
@@ -0,0 +1,681 @@
+//!
+//
+// Code relating to taking, dropping, etc as well as type descriptors.
+
+use lib::llvm::{ValueRef, TypeRef};
+use base::*;
+use common::*;
+use build::*;
+use type_of::type_of;
+
+fn trans_free(cx: block, v: ValueRef) -> block {
+    let _icx = cx.insn_ctxt("trans_free");
+    callee::trans_rtcall(cx, ~"free", ~[PointerCast(cx, v, T_ptr(T_i8()))],
+                         expr::Ignore)
+}
+
+fn trans_unique_free(cx: block, v: ValueRef) -> block {
+    let _icx = cx.insn_ctxt("trans_unique_free");
+    callee::trans_rtcall(
+        cx, ~"exchange_free", ~[PointerCast(cx, v, T_ptr(T_i8()))],
+        expr::Ignore)
+}
+
+fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block {
+    // NB: v is an *alias* of type t here, not a direct value.
+    let _icx = cx.insn_ctxt("take_ty");
+    if ty::type_needs_drop(cx.tcx(), t) {
+        return call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue);
+    }
+    return cx;
+}
+
+fn drop_ty(cx: block, v: ValueRef, t: ty::t) -> block {
+    // NB: v is an *alias* of type t here, not a direct value.
+    let _icx = cx.insn_ctxt("drop_ty");
+    if ty::type_needs_drop(cx.tcx(), t) {
+        return call_tydesc_glue(cx, v, t, abi::tydesc_field_drop_glue);
+    }
+    return cx;
+}
+
+fn drop_ty_root(bcx: block, v: ValueRef, rooted: bool, t: ty::t) -> block {
+    if rooted {
+        // NB: v is a raw ptr to an addrspace'd ptr to the value.
+        let v = PointerCast(bcx, Load(bcx, v), T_ptr(type_of(bcx.ccx(), t)));
+        drop_ty(bcx, v, t)
+    } else {
+        drop_ty(bcx, v, t)
+    }
+}
+
+fn drop_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block {
+    let _icx = bcx.insn_ctxt("drop_ty_immediate");
+    match ty::get(t).struct {
+      ty::ty_uniq(_) |
+      ty::ty_evec(_, ty::vstore_uniq) |
+      ty::ty_estr(ty::vstore_uniq) => {
+        free_ty_immediate(bcx, v, t)
+      }
+      ty::ty_box(_) | ty::ty_opaque_box |
+      ty::ty_evec(_, ty::vstore_box) |
+      ty::ty_estr(ty::vstore_box) => {
+        decr_refcnt_maybe_free(bcx, v, t)
+      }
+      _ => bcx.tcx().sess.bug(~"drop_ty_immediate: non-box ty")
+    }
+}
+
+fn take_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> Result {
+    let _icx = bcx.insn_ctxt("take_ty_immediate");
+    match ty::get(t).struct {
+      ty::ty_box(_) | ty::ty_opaque_box |
+      ty::ty_evec(_, ty::vstore_box) |
+      ty::ty_estr(ty::vstore_box) => {
+        incr_refcnt_of_boxed(bcx, v);
+        rslt(bcx, v)
+      }
+      ty::ty_uniq(_) => {
+        uniq::duplicate(bcx, v, t)
+      }
+      ty::ty_evec(_, ty::vstore_uniq) |
+      ty::ty_estr(ty::vstore_uniq) => {
+        tvec::duplicate_uniq(bcx, v, t)
+      }
+      _ => rslt(bcx, v)
+    }
+}
+
+fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block {
+    // NB: v is an *alias* of type t here, not a direct value.
+    let _icx = cx.insn_ctxt("free_ty");
+    if ty::type_needs_drop(cx.tcx(), t) {
+        return call_tydesc_glue(cx, v, t, abi::tydesc_field_free_glue);
+    }
+    return cx;
+}
+
+fn free_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block {
+    let _icx = bcx.insn_ctxt("free_ty_immediate");
+    match ty::get(t).struct {
+      ty::ty_uniq(_) |
+      ty::ty_evec(_, ty::vstore_uniq) |
+      ty::ty_estr(ty::vstore_uniq) |
+      ty::ty_box(_) | ty::ty_opaque_box |
+      ty::ty_evec(_, ty::vstore_box) |
+      ty::ty_estr(ty::vstore_box) |
+      ty::ty_opaque_closure_ptr(_) => {
+        let vp = alloca_zeroed(bcx, type_of(bcx.ccx(), t));
+        Store(bcx, v, vp);
+        free_ty(bcx, vp, t)
+      }
+      _ => bcx.tcx().sess.bug(~"free_ty_immediate: non-box ty")
+    }
+}
+
+fn lazily_emit_all_tydesc_glue(ccx: @crate_ctxt,
+                               static_ti: @tydesc_info) {
+    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, static_ti);
+    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_drop_glue, static_ti);
+    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_free_glue, static_ti);
+    lazily_emit_tydesc_glue(ccx, abi::tydesc_field_visit_glue, static_ti);
+}
+
+fn lazily_emit_tydesc_glue(ccx: @crate_ctxt, field: uint,
+                           ti: @tydesc_info) {
+    let _icx = ccx.insn_ctxt("lazily_emit_tydesc_glue");
+    let llfnty = type_of_glue_fn(ccx, ti.ty);
+    if field == abi::tydesc_field_take_glue {
+        match ti.take_glue {
+          Some(_) => (),
+          None => {
+            debug!("+++ lazily_emit_tydesc_glue TAKE %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"take");
+            ti.take_glue = Some(glue_fn);
+            make_generic_glue(ccx, ti.ty, glue_fn, make_take_glue, ~"take");
+            debug!("--- lazily_emit_tydesc_glue TAKE %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+          }
+        }
+    } else if field == abi::tydesc_field_drop_glue {
+        match ti.drop_glue {
+          Some(_) => (),
+          None => {
+            debug!("+++ lazily_emit_tydesc_glue DROP %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"drop");
+            ti.drop_glue = Some(glue_fn);
+            make_generic_glue(ccx, ti.ty, glue_fn, make_drop_glue, ~"drop");
+            debug!("--- lazily_emit_tydesc_glue DROP %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+          }
+        }
+    } else if field == abi::tydesc_field_free_glue {
+        match ti.free_glue {
+          Some(_) => (),
+          None => {
+            debug!("+++ lazily_emit_tydesc_glue FREE %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"free");
+            ti.free_glue = Some(glue_fn);
+            make_generic_glue(ccx, ti.ty, glue_fn, make_free_glue, ~"free");
+            debug!("--- lazily_emit_tydesc_glue FREE %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+          }
+        }
+    } else if field == abi::tydesc_field_visit_glue {
+        match ti.visit_glue {
+          Some(_) => (),
+          None => {
+            debug!("+++ lazily_emit_tydesc_glue VISIT %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+            let glue_fn = declare_generic_glue(ccx, ti.ty, llfnty, ~"visit");
+            ti.visit_glue = Some(glue_fn);
+            make_generic_glue(ccx, ti.ty, glue_fn, make_visit_glue, ~"visit");
+            debug!("--- lazily_emit_tydesc_glue VISIT %s",
+                   ppaux::ty_to_str(ccx.tcx, ti.ty));
+          }
+        }
+    }
+}
+
+// See [Note-arg-mode]
+fn call_tydesc_glue_full(++bcx: block, v: ValueRef, tydesc: ValueRef,
+                         field: uint, static_ti: Option<@tydesc_info>) {
+    let _icx = bcx.insn_ctxt("call_tydesc_glue_full");
+    let ccx = bcx.ccx();
+    // NB: Don't short-circuit even if this block is unreachable because
+    // GC-based cleanup needs to the see that the roots are live.
+    let no_lpads =
+        ccx.sess.opts.debugging_opts & session::no_landing_pads != 0;
+    if bcx.unreachable && !no_lpads { return; }
+
+    let static_glue_fn = match static_ti {
+      None => None,
+      Some(sti) => {
+        lazily_emit_tydesc_glue(ccx, field, sti);
+        if field == abi::tydesc_field_take_glue {
+            sti.take_glue
+        } else if field == abi::tydesc_field_drop_glue {
+            sti.drop_glue
+        } else if field == abi::tydesc_field_free_glue {
+            sti.free_glue
+        } else if field == abi::tydesc_field_visit_glue {
+            sti.visit_glue
+        } else {
+            None
+        }
+      }
+    };
+
+    // When available, use static type info to give glue the right type.
+    let static_glue_fn = match static_ti {
+      None => None,
+      Some(sti) => {
+        match static_glue_fn {
+          None => None,
+          Some(sgf) => Some(
+              PointerCast(bcx, sgf, T_ptr(type_of_glue_fn(ccx, sti.ty))))
+        }
+      }
+    };
+
+    // When static type info is available, avoid casting parameter because the
+    // function already has the right type. Otherwise cast to generic pointer.
+    let llrawptr = if is_none(static_ti) || is_none(static_glue_fn) {
+        PointerCast(bcx, v, T_ptr(T_i8()))
+    } else {
+        v
+    };
+
+    let llfn = {
+        match static_glue_fn {
+          None => {
+            // Select out the glue function to call from the tydesc
+            let llfnptr = GEPi(bcx, tydesc, [0u, field]);
+            Load(bcx, llfnptr)
+          }
+          Some(sgf) => sgf
+        }
+    };
+
+    Call(bcx, llfn, ~[C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())),
+                      C_null(T_ptr(T_ptr(bcx.ccx().tydesc_type))), llrawptr]);
+}
+
+// See [Note-arg-mode]
+fn call_tydesc_glue(++cx: block, v: ValueRef, t: ty::t, field: uint)
+    -> block {
+    let _icx = cx.insn_ctxt("call_tydesc_glue");
+    let ti = get_tydesc(cx.ccx(), t);
+    call_tydesc_glue_full(cx, v, ti.tydesc, field, Some(ti));
+    return cx;
+}
+
+fn call_cmp_glue(bcx: block, lhs: ValueRef, rhs: ValueRef, t: ty::t,
+                 llop: ValueRef) -> ValueRef {
+    // We can't use call_tydesc_glue_full() and friends here because compare
+    // glue has a special signature.
+    let _icx = bcx.insn_ctxt("call_cmp_glue");
+
+    let lllhs = spill_if_immediate(bcx, lhs, t);
+    let llrhs = spill_if_immediate(bcx, rhs, t);
+
+    let llrawlhsptr = BitCast(bcx, lllhs, T_ptr(T_i8()));
+    let llrawrhsptr = BitCast(bcx, llrhs, T_ptr(T_i8()));
+    let lltydesc = get_tydesc_simple(bcx.ccx(), t);
+
+    let llfn = bcx.ccx().upcalls.cmp_type;
+
+    let llcmpresultptr = alloca(bcx, T_i1());
+    Call(bcx, llfn, ~[llcmpresultptr, lltydesc,
+                      llrawlhsptr, llrawrhsptr, llop]);
+    return Load(bcx, llcmpresultptr);
+}
+
+fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) {
+    let _icx = bcx.insn_ctxt("make_visit_glue");
+    let mut bcx = bcx;
+    let ty_visitor_name = special_idents::ty_visitor;
+    assert bcx.ccx().tcx.intrinsic_defs.contains_key(ty_visitor_name);
+    let (trait_id, ty) = bcx.ccx().tcx.intrinsic_defs.get(ty_visitor_name);
+    let v = PointerCast(bcx, v, T_ptr(type_of::type_of(bcx.ccx(), ty)));
+    bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, trait_id);
+    build_return(bcx);
+}
+
+fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
+    // NB: v0 is an *alias* of type t here, not a direct value.
+    let _icx = bcx.insn_ctxt("make_free_glue");
+    let ccx = bcx.ccx();
+    let bcx = match ty::get(t).struct {
+      ty::ty_box(body_mt) => {
+        let v = Load(bcx, v);
+        let body = GEPi(bcx, v, [0u, abi::box_field_body]);
+        // Cast away the addrspace of the box pointer.
+        let body = PointerCast(bcx, body, T_ptr(type_of(ccx, body_mt.ty)));
+        let bcx = drop_ty(bcx, body, body_mt.ty);
+        trans_free(bcx, v)
+      }
+      ty::ty_opaque_box => {
+        let v = Load(bcx, v);
+        let td = Load(bcx, GEPi(bcx, v, [0u, abi::box_field_tydesc]));
+        let valptr = GEPi(bcx, v, [0u, abi::box_field_body]);
+        // Generate code that, dynamically, indexes into the
+        // tydesc and calls the drop glue that got set dynamically
+        call_tydesc_glue_full(bcx, valptr, td, abi::tydesc_field_drop_glue,
+                              None);
+        trans_free(bcx, v)
+      }
+      ty::ty_uniq(*) => {
+        uniq::make_free_glue(bcx, v, t)
+      }
+      ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) |
+      ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => {
+        make_free_glue(bcx, v,
+                       tvec::expand_boxed_vec_ty(bcx.tcx(), t));
+        return;
+      }
+      ty::ty_fn(_) => {
+        closure::make_fn_glue(bcx, v, t, free_ty)
+      }
+      ty::ty_opaque_closure_ptr(ck) => {
+        closure::make_opaque_cbox_free_glue(bcx, ck, v)
+      }
+      ty::ty_class(did, ref substs) => {
+        // Call the dtor if there is one
+        do option::map_default(ty::ty_dtor(bcx.tcx(), did), bcx) |dt_id| {
+            trans_class_drop(bcx, v, dt_id, did, substs)
+        }
+      }
+      _ => bcx
+    };
+    build_return(bcx);
+}
+
+fn trans_class_drop(bcx: block,
+                    v0: ValueRef,
+                    dtor_did: ast::def_id,
+                    class_did: ast::def_id,
+                    substs: &ty::substs) -> block {
+    let drop_flag = GEPi(bcx, v0, struct_dtor());
+    do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| {
+        let mut bcx = cx;
+
+        // Find and call the actual destructor
+        let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did,
+                                     class_did, substs.tps);
+
+        // The second argument is the "self" argument for drop
+        let params = lib::llvm::fn_ty_param_tys(
+            llvm::LLVMGetElementType(llvm::LLVMTypeOf(dtor_addr)));
+
+        // Class dtors have no explicit args, so the params should
+        // just consist of the output pointer and the environment
+        // (self)
+        assert(params.len() == 2u);
+        let self_arg = PointerCast(bcx, v0, params[1u]);
+        let args = ~[bcx.fcx.llretptr, self_arg];
+        Call(bcx, dtor_addr, args);
+
+        // Drop the fields
+        let field_tys =
+            ty::class_items_as_mutable_fields(bcx.tcx(), class_did,
+                                              substs);
+        for vec::eachi(field_tys) |i, fld| {
+            let llfld_a = GEPi(bcx, v0, struct_field(i));
+            bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
+        }
+
+        Store(bcx, C_u8(0u), drop_flag);
+        bcx
+    }
+}
+
+
+fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
+    // NB: v0 is an *alias* of type t here, not a direct value.
+    let _icx = bcx.insn_ctxt("make_drop_glue");
+    let ccx = bcx.ccx();
+    let bcx = match ty::get(t).struct {
+      ty::ty_box(_) | ty::ty_opaque_box |
+      ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) => {
+        decr_refcnt_maybe_free(bcx, Load(bcx, v0), t)
+      }
+      ty::ty_uniq(_) |
+      ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => {
+        free_ty(bcx, v0, t)
+      }
+      ty::ty_unboxed_vec(_) => {
+        tvec::make_drop_glue_unboxed(bcx, v0, t)
+      }
+      ty::ty_class(did, ref substs) => {
+        let tcx = bcx.tcx();
+        match ty::ty_dtor(tcx, did) {
+          Some(dtor) => {
+            trans_class_drop(bcx, v0, dtor, did, substs)
+          }
+          None => {
+            // No dtor? Just the default case
+            iter_structural_ty(bcx, v0, t, drop_ty)
+          }
+        }
+      }
+      ty::ty_fn(_) => {
+        closure::make_fn_glue(bcx, v0, t, drop_ty)
+      }
+      ty::ty_trait(_, _, _) => {
+        let llbox = Load(bcx, GEPi(bcx, v0, [0u, 1u]));
+        decr_refcnt_maybe_free(bcx, llbox, ty::mk_opaque_box(ccx.tcx))
+      }
+      ty::ty_opaque_closure_ptr(ck) => {
+        closure::make_opaque_cbox_drop_glue(bcx, ck, v0)
+      }
+      _ => {
+        if ty::type_needs_drop(ccx.tcx, t) &&
+            ty::type_is_structural(t) {
+            iter_structural_ty(bcx, v0, t, drop_ty)
+        } else { bcx }
+      }
+    };
+    build_return(bcx);
+}
+
+fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t) -> block {
+    let _icx = bcx.insn_ctxt("decr_refcnt_maybe_free");
+    let ccx = bcx.ccx();
+
+    do with_cond(bcx, IsNotNull(bcx, box_ptr)) |bcx| {
+        let rc_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_refcnt]);
+        let rc = Sub(bcx, Load(bcx, rc_ptr), C_int(ccx, 1));
+        Store(bcx, rc, rc_ptr);
+        let zero_test = ICmp(bcx, lib::llvm::IntEQ, C_int(ccx, 0), rc);
+        with_cond(bcx, zero_test, |bcx| free_ty_immediate(bcx, box_ptr, t))
+    }
+}
+
+
+fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) {
+    let _icx = bcx.insn_ctxt("make_take_glue");
+    // NB: v is a *pointer* to type t here, not a direct value.
+    let bcx = match ty::get(t).struct {
+      ty::ty_box(_) | ty::ty_opaque_box |
+      ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => {
+        incr_refcnt_of_boxed(bcx, Load(bcx, v)); bcx
+      }
+      ty::ty_uniq(_) => {
+        let Result {bcx, val} = uniq::duplicate(bcx, Load(bcx, v), t);
+        Store(bcx, val, v);
+        bcx
+      }
+      ty::ty_evec(_, ty::vstore_uniq) | ty::ty_estr(ty::vstore_uniq) => {
+        let Result {bcx, val} = tvec::duplicate_uniq(bcx, Load(bcx, v), t);
+        Store(bcx, val, v);
+        bcx
+      }
+      ty::ty_evec(_, ty::vstore_slice(_))
+      | ty::ty_estr(ty::vstore_slice(_)) => {
+        bcx
+      }
+      ty::ty_fn(_) => {
+        closure::make_fn_glue(bcx, v, t, take_ty)
+      }
+      ty::ty_trait(_, _, _) => {
+        let llbox = Load(bcx, GEPi(bcx, v, [0u, 1u]));
+        incr_refcnt_of_boxed(bcx, llbox);
+        bcx
+      }
+      ty::ty_opaque_closure_ptr(ck) => {
+        closure::make_opaque_cbox_take_glue(bcx, ck, v)
+      }
+      _ if ty::type_is_structural(t) => {
+        iter_structural_ty(bcx, v, t, take_ty)
+      }
+      _ => bcx
+    };
+
+    build_return(bcx);
+}
+
+fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) {
+    let _icx = cx.insn_ctxt("incr_refcnt_of_boxed");
+    let ccx = cx.ccx();
+    let rc_ptr = GEPi(cx, box_ptr, [0u, abi::box_field_refcnt]);
+    let rc = Load(cx, rc_ptr);
+    let rc = Add(cx, rc, C_int(ccx, 1));
+    Store(cx, rc, rc_ptr);
+}
+
+
+// Chooses the addrspace for newly declared types.
+fn declare_tydesc_addrspace(ccx: @crate_ctxt, t: ty::t) -> addrspace {
+    if !ty::type_needs_drop(ccx.tcx, t) {
+        return default_addrspace;
+    } else if ty::type_is_immediate(t) {
+        // For immediate types, we don't actually need an addrspace, because
+        // e.g. boxed types include pointers to their contents which are
+        // already correctly tagged with addrspaces.
+        return default_addrspace;
+    } else {
+        return ccx.next_addrspace();
+    }
+}
+
+// Generates the declaration for (but doesn't emit) a type descriptor.
+fn declare_tydesc(ccx: @crate_ctxt, t: ty::t) -> @tydesc_info {
+    let _icx = ccx.insn_ctxt("declare_tydesc");
+    // If emit_tydescs already ran, then we shouldn't be creating any new
+    // tydescs.
+    assert !ccx.finished_tydescs;
+
+    let llty = type_of(ccx, t);
+
+    if ccx.sess.count_type_sizes() {
+        io::println(fmt!("%u\t%s",
+                         llsize_of_real(ccx, llty),
+                         ty_to_str(ccx.tcx, t)));
+    }
+
+    let llsize = llsize_of(ccx, llty);
+    let llalign = llalign_of(ccx, llty);
+    let addrspace = declare_tydesc_addrspace(ccx, t);
+    //XXX this triggers duplicate LLVM symbols
+    let name = if false /*ccx.sess.opts.debuginfo*/ {
+        mangle_internal_name_by_type_only(ccx, t, ~"tydesc")
+    } else { mangle_internal_name_by_seq(ccx, ~"tydesc") };
+    note_unique_llvm_symbol(ccx, name);
+    log(debug, fmt!("+++ declare_tydesc %s %s", ty_to_str(ccx.tcx, t), name));
+    let gvar = str::as_c_str(name, |buf| {
+        llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type, buf)
+    });
+    let inf =
+        @{ty: t,
+          tydesc: gvar,
+          size: llsize,
+          align: llalign,
+          addrspace: addrspace,
+          mut take_glue: None,
+          mut drop_glue: None,
+          mut free_glue: None,
+          mut visit_glue: None};
+    log(debug, ~"--- declare_tydesc " + ppaux::ty_to_str(ccx.tcx, t));
+    return inf;
+}
+
+type glue_helper = fn@(block, ValueRef, ty::t);
+
+fn declare_generic_glue(ccx: @crate_ctxt, t: ty::t, llfnty: TypeRef,
+                        name: ~str) -> ValueRef {
+    let _icx = ccx.insn_ctxt("declare_generic_glue");
+    let name = name;
+    let mut fn_nm;
+    //XXX this triggers duplicate LLVM symbols
+    if false /*ccx.sess.opts.debuginfo*/ {
+        fn_nm = mangle_internal_name_by_type_only(ccx, t, (~"glue_" + name));
+    } else {
+        fn_nm = mangle_internal_name_by_seq(ccx, (~"glue_" + name));
+    }
+    note_unique_llvm_symbol(ccx, fn_nm);
+    let llfn = decl_cdecl_fn(ccx.llmod, fn_nm, llfnty);
+    set_glue_inlining(llfn, t);
+    return llfn;
+}
+
+fn make_generic_glue_inner(ccx: @crate_ctxt, t: ty::t,
+                           llfn: ValueRef, helper: glue_helper) -> ValueRef {
+    let _icx = ccx.insn_ctxt("make_generic_glue_inner");
+    let fcx = new_fn_ctxt(ccx, ~[], llfn, None);
+    lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage);
+    ccx.stats.n_glues_created += 1u;
+    // All glue functions take values passed *by alias*; this is a
+    // requirement since in many contexts glue is invoked indirectly and
+    // the caller has no idea if it's dealing with something that can be
+    // passed by value.
+    //
+    // llfn is expected be declared to take a parameter of the appropriate
+    // type, so we don't need to explicitly cast the function parameter.
+
+    let bcx = top_scope_block(fcx, None);
+    let lltop = bcx.llbb;
+    let llrawptr0 = llvm::LLVMGetParam(llfn, 3u as c_uint);
+    helper(bcx, llrawptr0, t);
+    finish_fn(fcx, lltop);
+    return llfn;
+}
+
+fn make_generic_glue(ccx: @crate_ctxt, t: ty::t, llfn: ValueRef,
+                     helper: glue_helper, name: ~str)
+    -> ValueRef {
+    let _icx = ccx.insn_ctxt("make_generic_glue");
+    if !ccx.sess.trans_stats() {
+        return make_generic_glue_inner(ccx, t, llfn, helper);
+    }
+
+    let start = time::get_time();
+    let llval = make_generic_glue_inner(ccx, t, llfn, helper);
+    let end = time::get_time();
+    log_fn_time(ccx, ~"glue " + name + ~" " + ty_to_short_str(ccx.tcx, t),
+                start, end);
+    return llval;
+}
+
+fn emit_tydescs(ccx: @crate_ctxt) {
+    let _icx = ccx.insn_ctxt("emit_tydescs");
+    // As of this point, allow no more tydescs to be created.
+    ccx.finished_tydescs = true;
+    for ccx.tydescs.each |key, val| {
+        let glue_fn_ty = T_ptr(T_generic_glue_fn(ccx));
+        let ti = val;
+
+        // Each of the glue functions needs to be cast to a generic type
+        // before being put into the tydesc because we only have a singleton
+        // tydesc type. Then we'll recast each function to its real type when
+        // calling it.
+        let take_glue =
+            match copy ti.take_glue {
+              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
+              Some(v) => {
+                ccx.stats.n_real_glues += 1u;
+                llvm::LLVMConstPointerCast(v, glue_fn_ty)
+              }
+            };
+        let drop_glue =
+            match copy ti.drop_glue {
+              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
+              Some(v) => {
+                ccx.stats.n_real_glues += 1u;
+                llvm::LLVMConstPointerCast(v, glue_fn_ty)
+              }
+            };
+        let free_glue =
+            match copy ti.free_glue {
+              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
+              Some(v) => {
+                ccx.stats.n_real_glues += 1u;
+                llvm::LLVMConstPointerCast(v, glue_fn_ty)
+              }
+            };
+        let visit_glue =
+            match copy ti.visit_glue {
+              None => { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) }
+              Some(v) => {
+                ccx.stats.n_real_glues += 1u;
+                llvm::LLVMConstPointerCast(v, glue_fn_ty)
+              }
+            };
+
+        let shape = shape_of(ccx, key);
+        let shape_tables =
+            llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables,
+                                       T_ptr(T_i8()));
+
+        let tydesc =
+            C_named_struct(ccx.tydesc_type,
+                           ~[ti.size, // size
+                             ti.align, // align
+                             take_glue, // take_glue
+                             drop_glue, // drop_glue
+                             free_glue, // free_glue
+                             visit_glue, // visit_glue
+                             C_shape(ccx, shape), // shape
+                             shape_tables]); // shape_tables
+
+        let gvar = ti.tydesc;
+        llvm::LLVMSetInitializer(gvar, tydesc);
+        llvm::LLVMSetGlobalConstant(gvar, True);
+        lib::llvm::SetLinkage(gvar, lib::llvm::InternalLinkage);
+
+        // Index tydesc by addrspace.
+        if ti.addrspace > gc_box_addrspace {
+            let llty = T_ptr(ccx.tydesc_type);
+            let addrspace_name = #fmt("_gc_addrspace_metadata_%u",
+                                      ti.addrspace as uint);
+            let addrspace_gvar = str::as_c_str(addrspace_name, |buf| {
+                llvm::LLVMAddGlobal(ccx.llmod, llty, buf)
+            });
+            lib::llvm::SetLinkage(addrspace_gvar, lib::llvm::InternalLinkage);
+            llvm::LLVMSetInitializer(addrspace_gvar, gvar);
+        }
+    };
+}
diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs
index 81e57898091..ae128a10f7c 100644
--- a/src/rustc/middle/trans/impl.rs
+++ b/src/rustc/middle/trans/impl.rs
@@ -3,10 +3,9 @@ use base::*;
 use common::*;
 use type_of::*;
 use build::*;
-use driver::session::session;
+use driver::session::{session, expect};
 use syntax::{ast, ast_map};
 use ast_map::{path, path_mod, path_name, node_id_to_str};
-use driver::session::expect;
 use syntax::ast_util::local_def;
 use metadata::csearch;
 use back::{link, abi};
@@ -15,8 +14,11 @@ use lib::llvm::{ValueRef, TypeRef};
 use lib::llvm::llvm::LLVMGetParam;
 use std::map::hashmap;
 use util::ppaux::{ty_to_str, tys_to_str};
-
+use callee::*;
 use syntax::print::pprust::expr_to_str;
+use expr::{SaveIn, Ignore};
+
+fn macros() { include!("macros.rs"); } // FIXME(#3114): Macro import/export.
 
 /**
 The main "translation" pass for methods.  Generates code
@@ -91,14 +93,13 @@ fn trans_method(ccx: @crate_ctxt,
 }
 
 fn trans_self_arg(bcx: block, base: @ast::expr,
-                  mentry: typeck::method_map_entry) -> result {
+                  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},
-                                T_ptr(type_of::type_of(bcx.ccx(), basety)),
-                                base, temp_cleanups, None, mentry.derefs);
+    let result = trans_arg_expr(bcx, {mode: mode, ty: basety}, base,
+                                &mut temp_cleanups, None, mentry.derefs);
 
     // by-ref self argument should not require cleanup in the case of
     // other arguments failing:
@@ -112,57 +113,61 @@ fn trans_self_arg(bcx: block, base: @ast::expr,
 
 fn trans_method_callee(bcx: block, callee_id: ast::node_id,
                        self: @ast::expr, mentry: typeck::method_map_entry)
-    -> lval_maybe_callee {
+    -> Callee
+{
     let _icx = bcx.insn_ctxt("impl::trans_method_callee");
     match mentry.origin {
-      typeck::method_static(did) => {
-
-
-        let {bcx, val} = trans_self_arg(bcx, self, mentry);
-        {env: self_env(val, node_id_type(bcx, self.id), None,
-                       mentry.self_mode),
-         .. lval_static_fn(bcx, did, callee_id)}
-      }
-      typeck::method_param({trait_id:trait_id, method_num:off,
-                            param_num:p, bound_num:b}) => {
-        match bcx.fcx.param_substs {
-          Some(substs) => {
-            let vtbl = find_vtable_in_fn_ctxt(substs, p, b);
-            trans_monomorphized_callee(bcx, callee_id, self, mentry,
-                                       trait_id, off, vtbl)
-          }
-          // how to get rid of this?
-          None => fail ~"trans_method_callee: missing param_substs"
+        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);
+
+            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
+                })
+            }
+        }
+        typeck::method_param({trait_id:trait_id, method_num:off,
+                              param_num:p, bound_num:b}) => {
+            match bcx.fcx.param_substs {
+                Some(substs) => {
+                    let vtbl = find_vtable_in_fn_ctxt(substs, p, b);
+                    trans_monomorphized_callee(bcx, callee_id, self, mentry,
+                                               trait_id, off, vtbl)
+                }
+                // how to get rid of this?
+                None => fail ~"trans_method_callee: missing param_substs"
+            }
+        }
+        typeck::method_trait(_, off) => {
+            trans_trait_callee(bcx, callee_id, off, self, mentry.derefs)
         }
-      }
-      typeck::method_trait(_, off) => {
-        let {bcx, val} = trans_temp_expr(bcx, self);
-        let fty = node_id_type(bcx, callee_id);
-        let self_ty = node_id_type(bcx, self.id);
-        let {bcx, val, _} = autoderef(bcx, self.id, val, self_ty,
-                                      uint::max_value);
-        trans_trait_callee(bcx, val, fty, off)
-      }
     }
 }
 
-fn trans_static_method_callee(bcx: block, method_id: ast::def_id,
-                              callee_id: ast::node_id) -> lval_maybe_callee {
+fn trans_static_method_callee(bcx: block,
+                              method_id: ast::def_id,
+                              callee_id: ast::node_id) -> FnData
+{
     let _icx = bcx.insn_ctxt("impl::trans_static_method_callee");
     let ccx = bcx.ccx();
 
     let mname = if method_id.crate == ast::local_crate {
         match bcx.tcx().items.get(method_id.node) {
-          ast_map::node_trait_method(trait_method, _, _) => {
-            ast_util::trait_method_to_ty_method(*trait_method).ident
-          }
-          _ => fail ~"callee is not a trait method"
+            ast_map::node_trait_method(trait_method, _, _) => {
+                ast_util::trait_method_to_ty_method(*trait_method).ident
+            }
+            _ => fail ~"callee is not a trait method"
         }
     } else {
         let path = csearch::get_item_path(bcx.tcx(), method_id);
         match path[path.len()-1] {
-          path_name(s) => { s }
-          path_mod(_) => { fail ~"path doesn't have a name?" }
+            path_name(s) => { s }
+            path_mod(_) => { fail ~"path doesn't have a name?" }
         }
     };
     debug!("trans_static_method_callee: method_id=%?, callee_id=%?, \
@@ -172,26 +177,28 @@ fn trans_static_method_callee(bcx: block, method_id: ast::def_id,
         bcx.fcx, ccx.maps.vtable_map.get(callee_id));
 
     match vtbls[0] { // is index 0 always the one we want?
-      typeck::vtable_static(impl_did, impl_substs, sub_origins) => {
-
-        let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
-        let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did);
-        let node_substs = node_id_type_params(bcx, callee_id);
-        let ty_substs
-            = vec::append(impl_substs,
-                          vec::tailn(node_substs,
-                                     node_substs.len() - n_m_tps));
-
-        let lval = lval_static_fn_inner(bcx, mth_id, callee_id, ty_substs,
-                                        Some(sub_origins));
-        {env: null_env,
-         val: PointerCast(bcx, lval.val, T_ptr(type_of_fn_from_ty(
-             ccx, node_id_type(bcx, callee_id)))),
-         .. lval}
-      }
-      _ => {
-        fail ~"vtable_param left in monomorphized function's vtable substs";
-      }
+        typeck::vtable_static(impl_did, impl_substs, sub_origins) => {
+
+            let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
+            let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did);
+            let node_substs = node_id_type_params(bcx, callee_id);
+            let ty_substs
+                = vec::append(impl_substs,
+                              vec::tailn(node_substs,
+                                         node_substs.len() - n_m_tps));
+
+            let FnData {llfn: lval} =
+                trans_fn_ref_with_vtables(bcx, mth_id, callee_id,
+                                          ty_substs, Some(sub_origins));
+
+            let callee_ty = node_id_type(bcx, callee_id);
+            let llty = T_ptr(type_of_fn_from_ty(ccx, callee_ty));
+            FnData {llfn: PointerCast(bcx, lval, llty)}
+        }
+        _ => {
+            fail ~"vtable_param left in monomorphized \
+                   function's vtable substs";
+        }
     }
 }
 
@@ -231,67 +238,138 @@ fn method_ty_param_count(ccx: @crate_ctxt, m_id: ast::def_id,
     }
 }
 
-fn trans_monomorphized_callee(bcx: block, callee_id: ast::node_id,
+fn trans_monomorphized_callee(bcx: block,
+                              callee_id: ast::node_id,
                               base: @ast::expr,
                               mentry: typeck::method_map_entry,
-                              trait_id: ast::def_id, n_method: uint,
+                              trait_id: ast::def_id,
+                              n_method: uint,
                               vtbl: typeck::vtable_origin)
-    -> lval_maybe_callee {
+    -> Callee
+{
     let _icx = bcx.insn_ctxt("impl::trans_monomorphized_callee");
     match vtbl {
       typeck::vtable_static(impl_did, impl_substs, sub_origins) => {
-        let ccx = bcx.ccx();
-        let mname = ty::trait_methods(ccx.tcx, trait_id)[n_method].ident;
-        let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
-        let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did);
-        let node_substs = node_id_type_params(bcx, callee_id);
-        let ty_substs
-            = vec::append(impl_substs,
-                          vec::tailn(node_substs,
-                                     node_substs.len() - n_m_tps));
-        let {bcx, val} = trans_self_arg(bcx, base, mentry);
-        let lval = lval_static_fn_inner(bcx, mth_id, callee_id, ty_substs,
-                                        Some(sub_origins));
-        {env: self_env(val, node_id_type(bcx, base.id),
-                       None, mentry.self_mode),
-         val: PointerCast(bcx, lval.val, T_ptr(type_of_fn_from_ty(
-             ccx, node_id_type(bcx, callee_id)))),
-         .. lval}
+          let ccx = bcx.ccx();
+          let mname = ty::trait_methods(ccx.tcx, trait_id)[n_method].ident;
+          let mth_id = method_with_name(bcx.ccx(), impl_did, mname);
+
+          // obtain the `self` value:
+          let Result {bcx, val: llself_val} =
+              trans_self_arg(bcx, base, mentry);
+
+          // create a concatenated set of substitutions which includes
+          // those from the impl and those from the method:
+          let n_m_tps = method_ty_param_count(ccx, mth_id, impl_did);
+          let node_substs = node_id_type_params(bcx, callee_id);
+          let ty_substs
+              = vec::append(impl_substs,
+                            vec::tailn(node_substs,
+                                       node_substs.len() - n_m_tps));
+          debug!("n_m_tps=%?", n_m_tps);
+          debug!("impl_substs=%?", impl_substs.map(|t| bcx.ty_to_str(t)));
+          debug!("node_substs=%?", node_substs.map(|t| bcx.ty_to_str(t)));
+          debug!("ty_substs=%?", ty_substs.map(|t| bcx.ty_to_str(t)));
+
+          // translate the function
+          let callee = trans_fn_ref_with_vtables(
+              bcx, mth_id, callee_id, ty_substs, Some(sub_origins));
+
+          // create a llvalue that represents the fn ptr
+          let fn_ty = node_id_type(bcx, callee_id);
+          let llfn_ty = T_ptr(type_of_fn_from_ty(ccx, fn_ty));
+          let llfn_val = PointerCast(bcx, callee.llfn, llfn_ty);
+
+          // combine the self environment with the rest
+          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
+              })
+          }
       }
       typeck::vtable_trait(*) => {
-        let {bcx, val} = trans_temp_expr(bcx, base);
-        let fty = node_id_type(bcx, callee_id);
-        trans_trait_callee(bcx, val, fty, n_method)
+          trans_trait_callee(bcx, callee_id, n_method, base, mentry.derefs)
       }
       typeck::vtable_param(*) => {
-        fail ~"vtable_param left in monomorphized function's vtable substs";
+          fail ~"vtable_param left in monomorphized function's vtable substs";
       }
     }
 }
 
-// Method callee where the vtable comes from a boxed trait
-fn trans_trait_callee(bcx: block, val: ValueRef,
-                      callee_ty: ty::t, n_method: uint)
-    -> lval_maybe_callee {
+fn trans_trait_callee(bcx: block,
+                      callee_id: ast::node_id,
+                      n_method: uint,
+                      self_expr: @ast::expr,
+                      autoderefs: uint)
+    -> Callee
+{
+    //!
+    //
+    // Create a method callee where the method is coming from a trait
+    // instance (e.g., @Trait type).  In this case, we must pull the
+    // fn pointer out of the vtable that is packaged up with the
+    // @Trait instance.  @Traits are represented as a pair, so we first
+    // evaluate the self expression (expected a by-ref result) and then
+    // extract the self data and vtable out of the pair.
+
+    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)
+}
+
+fn trans_trait_callee_from_llval(bcx: block,
+                                 callee_ty: ty::t,
+                                 n_method: uint,
+                                 llpair: ValueRef)
+    -> Callee
+{
+    //!
+    //
+    // Same as `trans_trait_callee()` above, except that it is given
+    // a by-ref pointer to the @Trait pair.
+
     let _icx = bcx.insn_ctxt("impl::trans_trait_callee");
     let ccx = bcx.ccx();
-    let vtable = Load(bcx, PointerCast(bcx, GEPi(bcx, val, [0u, 0u]),
-                                       T_ptr(T_ptr(T_vtable()))));
-    let llbox = Load(bcx, GEPi(bcx, val, [0u, 1u]));
-    // FIXME[impl] I doubt this is alignment-safe (#2534)
-    let self = GEPi(bcx, llbox, [0u, abi::box_field_body]);
-    let env = self_env(self, ty::mk_opaque_box(bcx.tcx()), Some(llbox),
-                       // XXX: is this bogosity?
-                       ast::by_ref);
-    let llfty = type_of::type_of_fn_from_ty(ccx, callee_ty);
-    let vtable = PointerCast(bcx, vtable,
-                             T_ptr(T_array(T_ptr(llfty), n_method + 1u)));
-    let mptr = Load(bcx, GEPi(bcx, vtable, [0u, n_method]));
-    {bcx: bcx, val: mptr, kind: lv_owned, env: env}
+    let mut bcx = bcx;
+
+    // Load the vtable from the @Trait pair
+    let llvtable = Load(bcx,
+                      PointerCast(bcx,
+                                  GEPi(bcx, llpair, [0u, 0u]),
+                                  T_ptr(T_ptr(T_vtable()))));
+
+    // Load the box from the @Trait pair and GEP over the box header:
+    let llbox = Load(bcx, GEPi(bcx, llpair, [0u, 1u]));
+    let llself = GEPi(bcx, llbox, [0u, abi::box_field_body]);
+
+    // Load the function from the vtable and cast it to the expected type.
+    let llcallee_ty = type_of::type_of_fn_from_ty(ccx, callee_ty);
+    let mptr = Load(bcx, GEPi(bcx, llvtable, [0u, n_method]));
+    let mptr = PointerCast(bcx, mptr, T_ptr(llcallee_ty));
+
+    return Callee {
+        bcx: bcx,
+        data: Method(MethodData {
+            llfn: mptr,
+            llself: llself,
+            self_ty: ty::mk_opaque_box(bcx.tcx()),
+            self_mode: ast::by_ref, // XXX: is this bogosity?
+            /* XXX: Some(llbox) */
+        })
+    };
 }
 
 fn find_vtable_in_fn_ctxt(ps: param_substs, n_param: uint, n_bound: uint)
-    -> typeck::vtable_origin {
+    -> typeck::vtable_origin
+{
     let mut vtable_off = n_bound, i = 0u;
     // Vtables are stored in a flat array, finding the right one is
     // somewhat awkward
@@ -339,17 +417,18 @@ fn resolve_vtable_in_fn_ctxt(fcx: fn_ctxt, vt: typeck::vtable_origin)
 
 fn vtable_id(ccx: @crate_ctxt, origin: typeck::vtable_origin) -> mono_id {
     match origin {
-      typeck::vtable_static(impl_id, substs, sub_vtables) => {
-        make_mono_id(ccx, impl_id, substs,
-                     if (*sub_vtables).len() == 0u { None }
-                     else { Some(sub_vtables) }, None)
-      }
-      typeck::vtable_trait(trait_id, substs) => {
-        @{def: trait_id,
-          params: vec::map(substs, |t| mono_precise(t, None))}
-      }
-      // can't this be checked at the callee?
-      _ => fail ~"vtable_id"
+        typeck::vtable_static(impl_id, substs, sub_vtables) => {
+            monomorphize::make_mono_id(
+                ccx, impl_id, substs,
+                if (*sub_vtables).len() == 0u { None }
+                else { Some(sub_vtables) }, None)
+        }
+        typeck::vtable_trait(trait_id, substs) => {
+            @{def: trait_id,
+              params: vec::map(substs, |t| mono_precise(t, None))}
+        }
+        // can't this be checked at the callee?
+        _ => fail ~"vtable_id"
     }
 }
 
@@ -385,9 +464,10 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: ~[ty::t],
     let tcx = ccx.tcx;
 
     // XXX: This should support multiple traits.
-    let trt_id = expect(ccx.sess,
-                        ty::ty_to_def_id(ty::impl_traits(tcx, impl_id)[0]),
-                        || ~"make_impl_vtable: non-trait-type implemented");
+    let trt_id = driver::session::expect(
+        tcx.sess,
+        ty::ty_to_def_id(ty::impl_traits(tcx, impl_id)[0]),
+        || ~"make_impl_vtable: non-trait-type implemented");
 
     let has_tps = (*ty::lookup_item_type(ccx.tcx, impl_id).bounds).len() > 0u;
     make_vtable(ccx, vec::map(*ty::trait_methods(tcx, trt_id), |im| {
@@ -400,9 +480,10 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: ~[ty::t],
                 // If the method is in another crate, need to make an inlined
                 // copy first
                 if m_id.crate != ast::local_crate {
-                    m_id = maybe_instantiate_inline(ccx, m_id);
+                    m_id = inline::maybe_instantiate_inline(ccx, m_id);
                 }
-                monomorphic_fn(ccx, m_id, substs, Some(vtables), None).val
+                monomorphize::monomorphic_fn(ccx, m_id, substs,
+                                             Some(vtables), None).val
             } else if m_id.crate == ast::local_crate {
                 get_item_val(ccx, m_id.node)
             } else {
@@ -412,23 +493,42 @@ fn make_impl_vtable(ccx: @crate_ctxt, impl_id: ast::def_id, substs: ~[ty::t],
     }))
 }
 
-fn trans_cast(bcx: block, val: @ast::expr, id: ast::node_id, dest: dest)
-    -> block {
+fn trans_trait_cast(bcx: block,
+                    val: @ast::expr,
+                    id: ast::node_id,
+                    dest: expr::Dest)
+    -> block
+{
     let _icx = bcx.insn_ctxt("impl::trans_cast");
-    if dest == ignore { return trans_expr(bcx, val, ignore); }
+
+    let lldest = match dest {
+        Ignore => {
+            return expr::trans_into(bcx, val, Ignore);
+        }
+        SaveIn(dest) => dest
+    };
+
     let ccx = bcx.ccx();
     let v_ty = expr_ty(bcx, val);
+
+    // Allocate an @ box and store the value into it
     let {bcx: bcx, box: llbox, body: body} = malloc_boxed(bcx, v_ty);
     add_clean_free(bcx, llbox, heap_shared);
-    let bcx = trans_expr_save_in(bcx, val, body);
+    let bcx = expr::trans_into(bcx, val, SaveIn(body));
     revoke_clean(bcx, llbox);
-    let result = get_dest_addr(dest);
-    Store(bcx, llbox, PointerCast(bcx, GEPi(bcx, result, [0u, 1u]),
+
+    // Store the @ box into the pair
+    Store(bcx, llbox, PointerCast(bcx,
+                                  GEPi(bcx, lldest, [0u, 1u]),
                                   T_ptr(val_ty(llbox))));
+
+    // Store the vtable into the pair
     let orig = ccx.maps.vtable_map.get(id)[0];
     let orig = resolve_vtable_in_fn_ctxt(bcx.fcx, orig);
     let vtable = get_vtable(bcx.ccx(), orig);
-    Store(bcx, vtable, PointerCast(bcx, GEPi(bcx, result, [0u, 0u]),
+    Store(bcx, vtable, PointerCast(bcx,
+                                   GEPi(bcx, lldest, [0u, 0u]),
                                    T_ptr(val_ty(vtable))));
+
     bcx
 }
diff --git a/src/rustc/middle/trans/inline.rs b/src/rustc/middle/trans/inline.rs
new file mode 100644
index 00000000000..6de7e77a394
--- /dev/null
+++ b/src/rustc/middle/trans/inline.rs
@@ -0,0 +1,87 @@
+use common::*;
+use syntax::ast;
+use syntax::ast_util::local_def;
+use syntax::ast_map::{path, path_mod, path_name};
+use base::{trans_item, get_item_val, self_arg, trans_fn,
+              impl_self, get_insn_ctxt};
+
+fn maybe_instantiate_inline(ccx: @crate_ctxt, fn_id: ast::def_id)
+    -> ast::def_id
+{
+    let _icx = ccx.insn_ctxt("maybe_instantiate_inline");
+    match ccx.external.find(fn_id) {
+      Some(Some(node_id)) => {
+        // Already inline
+        debug!("maybe_instantiate_inline(%s): already inline as node id %d",
+               ty::item_path_str(ccx.tcx, fn_id), node_id);
+        local_def(node_id)
+      }
+      Some(None) => fn_id, // Not inlinable
+      None => { // Not seen yet
+        match csearch::maybe_get_item_ast(
+            ccx.tcx, fn_id,
+            |a,b,c,d| {
+                astencode::decode_inlined_item(a, b, ccx.maps, c, d)
+            }) {
+
+          csearch::not_found => {
+            ccx.external.insert(fn_id, None);
+            fn_id
+          }
+          csearch::found(ast::ii_item(item)) => {
+            ccx.external.insert(fn_id, Some(item.id));
+            trans_item(ccx, *item);
+            local_def(item.id)
+          }
+          csearch::found(ast::ii_ctor(ctor, _, _, _)) => {
+            ccx.external.insert(fn_id, Some(ctor.node.id));
+            local_def(ctor.node.id)
+          }
+          csearch::found(ast::ii_foreign(item)) => {
+            ccx.external.insert(fn_id, Some(item.id));
+            local_def(item.id)
+          }
+          csearch::found_parent(parent_id, ast::ii_item(item)) => {
+            ccx.external.insert(parent_id, Some(item.id));
+            let mut my_id = 0;
+            match item.node {
+              ast::item_enum(_, _) => {
+                let vs_here = ty::enum_variants(ccx.tcx, local_def(item.id));
+                let vs_there = ty::enum_variants(ccx.tcx, parent_id);
+                do vec::iter2(*vs_here, *vs_there) |here, there| {
+                    if there.id == fn_id { my_id = here.id.node; }
+                    ccx.external.insert(there.id, Some(here.id.node));
+                }
+              }
+              _ => ccx.sess.bug(~"maybe_instantiate_inline: item has a \
+                    non-enum parent")
+            }
+            trans_item(ccx, *item);
+            local_def(my_id)
+          }
+          csearch::found_parent(_, _) => {
+              ccx.sess.bug(~"maybe_get_item_ast returned a found_parent \
+               with a non-item parent");
+          }
+          csearch::found(ast::ii_method(impl_did, mth)) => {
+            ccx.external.insert(fn_id, Some(mth.id));
+            let {bounds: impl_bnds, region_param: _, ty: impl_ty} =
+                ty::lookup_item_type(ccx.tcx, impl_did);
+            if (*impl_bnds).len() + mth.tps.len() == 0u {
+                let llfn = get_item_val(ccx, mth.id);
+                let path = vec::append(
+                    ty::item_path(ccx.tcx, impl_did),
+                    ~[path_name(mth.ident)]);
+                trans_fn(ccx, path, mth.decl, mth.body,
+                         llfn, impl_self(impl_ty), None, mth.id);
+            }
+            local_def(mth.id)
+          }
+          csearch::found(ast::ii_dtor(dtor, _, _, _)) => {
+              ccx.external.insert(fn_id, Some(dtor.node.id));
+              local_def(dtor.node.id)
+          }
+        }
+      }
+    }
+}
diff --git a/src/rustc/middle/trans/macros.rs b/src/rustc/middle/trans/macros.rs
new file mode 100644
index 00000000000..6a813b7c1b2
--- /dev/null
+++ b/src/rustc/middle/trans/macros.rs
@@ -0,0 +1,45 @@
+{
+
+macro_rules! unpack_datum(
+    ($bcx: ident, $inp: expr) => (
+        {
+            let db = $inp;
+            $bcx = db.bcx;
+            db.datum
+        }
+    )
+);
+
+macro_rules! unpack_result(
+    ($bcx: ident, $inp: expr) => (
+        {
+            let db = $inp;
+            $bcx = db.bcx;
+            db.val
+        }
+    )
+);
+
+macro_rules! trace_span(
+    ($bcx: ident, $sp: expr, $str: expr) => (
+        {
+            let bcx = $bcx;
+            if bcx.sess().trace() {
+                trans_trace(bcx, Some($sp), $str);
+            }
+        }
+    )
+);
+
+macro_rules! trace(
+    ($bcx: ident, $str: expr) => (
+        {
+            let bcx = $bcx;
+            if bcx.sess().trace() {
+                trans_trace(bcx, None, $str);
+            }
+        }
+    )
+);
+
+}
\ No newline at end of file
diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs
new file mode 100644
index 00000000000..962aed37ff1
--- /dev/null
+++ b/src/rustc/middle/trans/monomorphize.rs
@@ -0,0 +1,276 @@
+use common::*;
+use syntax::ast;
+use syntax::ast_util::local_def;
+use syntax::ast_map::{path, path_mod, path_name};
+use base::{trans_item, get_item_val, no_self, self_arg, trans_fn,
+              impl_self, decl_internal_cdecl_fn,
+              set_inline_hint_if_appr, set_inline_hint,
+              trans_enum_variant, trans_class_ctor, trans_class_dtor,
+              get_insn_ctxt};
+use syntax::parse::token::special_idents;
+use type_of::type_of_fn_from_ty;
+use back::link::mangle_exported_name;
+
+fn monomorphic_fn(ccx: @crate_ctxt,
+                  fn_id: ast::def_id,
+                  real_substs: ~[ty::t],
+                  vtables: Option<typeck::vtable_res>,
+                  ref_id: Option<ast::node_id>)
+    -> {val: ValueRef, must_cast: bool}
+{
+    let _icx = ccx.insn_ctxt("monomorphic_fn");
+    let mut must_cast = false;
+    let substs = vec::map(real_substs, |t| {
+        match normalize_for_monomorphization(ccx.tcx, t) {
+          Some(t) => { must_cast = true; t }
+          None => t
+        }
+    });
+
+    for real_substs.each() |s| { assert !ty::type_has_params(s); }
+    for substs.each() |s| { assert !ty::type_has_params(s); }
+    let param_uses = type_use::type_uses_for(ccx, fn_id, substs.len());
+    let hash_id = make_mono_id(ccx, fn_id, substs, vtables, Some(param_uses));
+    if vec::any(hash_id.params,
+                |p| match p { mono_precise(_, _) => false, _ => true }) {
+        must_cast = true;
+    }
+
+    #debug["monomorphic_fn(fn_id=%? (%s), real_substs=%?, substs=%?, \
+           hash_id = %?",
+           fn_id, ty::item_path_str(ccx.tcx, fn_id),
+           real_substs.map(|s| ty_to_str(ccx.tcx, s)),
+           substs.map(|s| ty_to_str(ccx.tcx, s)), hash_id];
+
+    match ccx.monomorphized.find(hash_id) {
+      Some(val) => {
+        debug!("leaving monomorphic fn %s",
+               ty::item_path_str(ccx.tcx, fn_id));
+        return {val: val, must_cast: must_cast};
+      }
+      None => ()
+    }
+
+    let tpt = ty::lookup_item_type(ccx.tcx, fn_id);
+    let mut llitem_ty = tpt.ty;
+
+    let map_node = session::expect(ccx.sess, ccx.tcx.items.find(fn_id.node),
+     || fmt!("While monomorphizing %?, couldn't find it in the item map \
+        (may have attempted to monomorphize an item defined in a different \
+        crate?)", fn_id));
+    // Get the path so that we can create a symbol
+    let (pt, name, span) = match map_node {
+      ast_map::node_item(i, pt) => (pt, i.ident, i.span),
+      ast_map::node_variant(v, enm, pt) => (pt, v.node.name, enm.span),
+      ast_map::node_method(m, _, pt) => (pt, m.ident, m.span),
+      ast_map::node_foreign_item(i, ast::foreign_abi_rust_intrinsic, pt)
+      => (pt, i.ident, i.span),
+      ast_map::node_foreign_item(*) => {
+        // Foreign externs don't have to be monomorphized.
+        return {val: get_item_val(ccx, fn_id.node),
+                must_cast: true};
+      }
+      ast_map::node_ctor(nm, _, ct, _, pt) => (pt, nm, ct.span),
+      ast_map::node_dtor(_, dtor, _, pt) =>
+          (pt, special_idents::dtor, dtor.span),
+      ast_map::node_trait_method(*) => {
+        ccx.tcx.sess.bug(~"Can't monomorphize a trait method")
+      }
+      ast_map::node_expr(*) => {
+        ccx.tcx.sess.bug(~"Can't monomorphize an expr")
+      }
+      ast_map::node_stmt(*) => {
+        ccx.tcx.sess.bug(~"Can't monomorphize a stmt")
+      }
+      ast_map::node_export(*) => {
+          ccx.tcx.sess.bug(~"Can't monomorphize an export")
+      }
+      ast_map::node_arg(*) => ccx.tcx.sess.bug(~"Can't monomorphize an arg"),
+      ast_map::node_block(*) => {
+          ccx.tcx.sess.bug(~"Can't monomorphize a block")
+      }
+      ast_map::node_local(*) => {
+          ccx.tcx.sess.bug(~"Can't monomorphize a local")
+      }
+    };
+    let mono_ty = ty::subst_tps(ccx.tcx, substs, llitem_ty);
+    let llfty = type_of_fn_from_ty(ccx, mono_ty);
+
+    let depth = option::get_default(ccx.monomorphizing.find(fn_id), 0u);
+    // Random cut-off -- code that needs to instantiate the same function
+    // recursively more than ten times can probably safely be assumed to be
+    // causing an infinite expansion.
+    if depth > 10u {
+        ccx.sess.span_fatal(
+            span, ~"overly deep expansion of inlined function");
+    }
+    ccx.monomorphizing.insert(fn_id, depth + 1u);
+
+    let pt = vec::append(*pt,
+                         ~[path_name(ccx.names(ccx.sess.str_of(name)))]);
+    let s = mangle_exported_name(ccx, pt, mono_ty);
+
+    let mk_lldecl = || {
+        let lldecl = decl_internal_cdecl_fn(ccx.llmod, s, llfty);
+        ccx.monomorphized.insert(hash_id, lldecl);
+        lldecl
+    };
+
+    let psubsts = Some({tys: substs, vtables: vtables, bounds: tpt.bounds});
+    let lldecl = match map_node {
+      ast_map::node_item(i@@{node: ast::item_fn(decl, _, _, body), _}, _) => {
+        let d = mk_lldecl();
+        set_inline_hint_if_appr(i.attrs, d);
+        trans_fn(ccx, pt, decl, body, d, no_self, psubsts, fn_id.node);
+        d
+      }
+      ast_map::node_item(*) => {
+          ccx.tcx.sess.bug(~"Can't monomorphize this kind of item")
+      }
+      ast_map::node_foreign_item(i, _, _) => {
+          let d = mk_lldecl();
+          foreign::trans_intrinsic(ccx, d, i, pt, option::get(psubsts),
+                                ref_id);
+          d
+      }
+      ast_map::node_variant(v, enum_item, _) => {
+        let tvs = ty::enum_variants(ccx.tcx, local_def(enum_item.id));
+        let this_tv = option::get(vec::find(*tvs, |tv| {
+            tv.id.node == fn_id.node}));
+        let d = mk_lldecl();
+        set_inline_hint(d);
+        match v.node.kind {
+            ast::tuple_variant_kind(args) => {
+                trans_enum_variant(ccx, enum_item.id, v, args,
+                                   this_tv.disr_val, (*tvs).len() == 1u,
+                                   psubsts, d);
+            }
+            ast::struct_variant_kind(_) =>
+                ccx.tcx.sess.bug(~"can't monomorphize struct variants"),
+            ast::enum_variant_kind(_) =>
+                ccx.tcx.sess.bug(~"can't monomorphize enum variants")
+        }
+        d
+      }
+      ast_map::node_method(mth, _, _) => {
+        let d = mk_lldecl();
+        set_inline_hint_if_appr(mth.attrs, d);
+        impl::trans_method(ccx, pt, mth, psubsts, d);
+        d
+      }
+      ast_map::node_ctor(_, tps, ctor, parent_id, _) => {
+        // ctors don't have attrs, at least not right now
+        let d = mk_lldecl();
+        let tp_tys = ty::ty_params_to_tys(ccx.tcx, tps);
+        trans_class_ctor(ccx, pt, ctor.node.dec, ctor.node.body, d,
+               option::get_default(psubsts,
+                        {tys:tp_tys, vtables: None, bounds: @~[]}),
+                         fn_id.node, parent_id, ctor.span);
+        d
+      }
+      ast_map::node_dtor(_, dtor, _, pt) => {
+        let parent_id = match ty::ty_to_def_id(ty::node_id_to_type(ccx.tcx,
+                                              dtor.node.self_id)) {
+                Some(did) => did,
+                None      => ccx.sess.span_bug(dtor.span, ~"Bad self ty in \
+                                                            dtor")
+        };
+        trans_class_dtor(ccx, *pt, dtor.node.body,
+          dtor.node.id, psubsts, Some(hash_id), parent_id)
+      }
+      // Ugh -- but this ensures any new variants won't be forgotten
+      ast_map::node_expr(*) |
+      ast_map::node_stmt(*) |
+      ast_map::node_trait_method(*) |
+      ast_map::node_export(*) |
+      ast_map::node_arg(*) |
+      ast_map::node_block(*) |
+      ast_map::node_local(*) => {
+        ccx.tcx.sess.bug(fmt!("Can't monomorphize a %?", map_node))
+      }
+    };
+    ccx.monomorphizing.insert(fn_id, depth);
+
+    debug!("leaving monomorphic fn %s", ty::item_path_str(ccx.tcx, fn_id));
+    {val: lldecl, must_cast: must_cast}
+}
+
+fn normalize_for_monomorphization(tcx: ty::ctxt, ty: ty::t) -> Option<ty::t> {
+    // FIXME[mono] could do this recursively. is that worthwhile? (#2529)
+    match ty::get(ty).struct {
+      ty::ty_box(*) => {
+        Some(ty::mk_opaque_box(tcx))
+      }
+      ty::ty_fn(ref fty) => {
+        Some(ty::mk_fn(tcx, {purity: ast::impure_fn,
+                             proto: fty.proto,
+                             bounds: @~[],
+                             inputs: ~[],
+                             output: ty::mk_nil(tcx),
+                             ret_style: ast::return_val}))
+      }
+      ty::ty_trait(_, _, _) => {
+        Some(ty::mk_fn(tcx, {purity: ast::impure_fn,
+                             proto: ty::proto_vstore(ty::vstore_box),
+                             bounds: @~[],
+                             inputs: ~[],
+                             output: ty::mk_nil(tcx),
+                             ret_style: ast::return_val}))
+      }
+      ty::ty_ptr(_) => Some(ty::mk_uint(tcx)),
+      _ => None
+    }
+}
+
+fn make_mono_id(ccx: @crate_ctxt, item: ast::def_id, substs: ~[ty::t],
+                vtables: Option<typeck::vtable_res>,
+                param_uses: Option<~[type_use::type_uses]>) -> mono_id {
+    let precise_param_ids = match vtables {
+      Some(vts) => {
+        let bounds = ty::lookup_item_type(ccx.tcx, item).bounds;
+        let mut i = 0u;
+        vec::map2(*bounds, substs, |bounds, subst| {
+            let mut v = ~[];
+            for vec::each(*bounds) |bound| {
+                match bound {
+                  ty::bound_trait(_) => {
+                    vec::push(v, impl::vtable_id(ccx, vts[i]));
+                    i += 1u;
+                  }
+                  _ => ()
+                }
+            }
+            (subst, if v.len() > 0u { Some(v) } else { None })
+        })
+      }
+      None => {
+        vec::map(substs, |subst| (subst, None))
+      }
+    };
+    let param_ids = match param_uses {
+      Some(uses) => {
+        vec::map2(precise_param_ids, uses, |id, uses| {
+            match id {
+                (a, b@Some(_)) => mono_precise(a, b),
+              (subst, None) => {
+                if uses == 0u { mono_any }
+                else if uses == type_use::use_repr &&
+                        !ty::type_needs_drop(ccx.tcx, subst) {
+                    let llty = type_of::type_of(ccx, subst);
+                    let size = shape::llsize_of_real(ccx, llty);
+                    let align = shape::llalign_of_pref(ccx, llty);
+                    // Special value for nil to prevent problems with undef
+                    // return pointers.
+                    if size == 1u && ty::type_is_nil(subst) {
+                        mono_repr(0u, 0u)
+                    } else { mono_repr(size, align) }
+                } else { mono_precise(subst, None) }
+              }
+            }
+        })
+      }
+      None => precise_param_ids.map(|x| { let (a, b) = x;
+                mono_precise(a, b) })
+    };
+    @{def: item, params: param_ids}
+}
diff --git a/src/rustc/middle/trans/reachable.rs b/src/rustc/middle/trans/reachable.rs
index 234a81a0a28..a9a7f4e4e9f 100644
--- a/src/rustc/middle/trans/reachable.rs
+++ b/src/rustc/middle/trans/reachable.rs
@@ -144,7 +144,7 @@ fn traverse_public_item(cx: ctx, item: @item) {
 }
 
 fn mk_ty_visitor() -> visit::vt<ctx> {
-    visit::mk_vt(@{visit_ty: traverse_ty,.. *visit::default_visitor()})
+    visit::mk_vt(@{visit_ty: traverse_ty, ..*visit::default_visitor()})
 }
 
 fn traverse_ty(ty: @ty, cx: ctx, v: visit::vt<ctx>) {
@@ -200,7 +200,7 @@ fn traverse_inline_body(cx: ctx, body: blk) {
      visit::visit_block(body, cx, visit::mk_vt(@{
         visit_expr: traverse_expr,
         visit_item: traverse_item,
-        .. *visit::default_visitor()
+         ..*visit::default_visitor()
     }));
 }
 
@@ -219,7 +219,7 @@ fn traverse_all_resources_and_impls(cx: ctx, crate_mod: _mod) {
               _ => ()
             }
         },
-        .. *visit::default_visitor()
+        ..*visit::default_visitor()
     }));
 }
 
diff --git a/src/rustc/middle/trans/reflect.rs b/src/rustc/middle/trans/reflect.rs
index 786d5eec5cb..c6d35e85826 100644
--- a/src/rustc/middle/trans/reflect.rs
+++ b/src/rustc/middle/trans/reflect.rs
@@ -9,6 +9,9 @@ use base::*;
 use type_of::*;
 use ast::def_id;
 use util::ppaux::ty_to_str;
+use datum::*;
+use callee::ArgVals;
+use expr::SaveIn;
 
 enum reflector = {
     visitor_val: ValueRef,
@@ -44,7 +47,7 @@ impl reflector {
     fn c_tydesc(t: ty::t) -> ValueRef {
         let bcx = self.bcx;
         let static_ti = get_tydesc(bcx.ccx(), t);
-        lazily_emit_all_tydesc_glue(bcx.ccx(), static_ti);
+        glue::lazily_emit_all_tydesc_glue(bcx.ccx(), static_ti);
         PointerCast(bcx, static_ti.tydesc, T_ptr(self.tydesc_ty))
     }
 
@@ -60,25 +63,21 @@ impl reflector {
             *self.visitor_methods));
         let mth_ty = ty::mk_fn(tcx, self.visitor_methods[mth_idx].fty);
         let v = self.visitor_val;
-        let get_lval = |bcx| {
-            let callee =
-                impl::trans_trait_callee(bcx, v, mth_ty, mth_idx);
-            debug!("calling mth ty %s, lltype %s",
-                   ty_to_str(bcx.ccx().tcx, mth_ty),
-                   val_str(bcx.ccx().tn, callee.val));
-            callee
-        };
         debug!("passing %u args:", vec::len(args));
         let bcx = self.bcx;
         for args.eachi |i, a| {
             debug!("arg %u: %s", i, val_str(bcx.ccx().tn, a));
         }
-        let d = empty_dest_cell();
-        let bcx =
-            trans_call_inner(self.bcx, None, mth_ty, ty::mk_bool(tcx),
-                             get_lval, arg_vals(args), by_val(d));
+        let bool_ty = ty::mk_bool(tcx);
+        let scratch = scratch_datum(bcx, bool_ty, false);
+        let bcx = callee::trans_call_inner(
+            self.bcx, None, mth_ty, bool_ty,
+            |bcx| impl::trans_trait_callee_from_llval(bcx, mth_ty,
+                                                      mth_idx, v),
+            ArgVals(args), SaveIn(scratch.val));
+        let result = scratch.to_value_llval(bcx);
         let next_bcx = sub_block(bcx, ~"next");
-        CondBr(bcx, *d, next_bcx.llbb, self.final_bcx.llbb);
+        CondBr(bcx, result, next_bcx.llbb, self.final_bcx.llbb);
         self.bcx = next_bcx
     }
 
diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs
index 0473c62c99c..33072f87db9 100644
--- a/src/rustc/middle/trans/tvec.rs
+++ b/src/rustc/middle/trans/tvec.rs
@@ -2,15 +2,15 @@ use syntax::ast;
 use driver::session::session;
 use lib::llvm::{ValueRef, TypeRef};
 use back::abi;
-use base::{call_memmove,
-              INIT, copy_val, load_if_immediate, get_tydesc,
-              sub_block, do_spill_noroot,
-              dest, non_gc_box_cast, move_val, lval_owned};
 use syntax::codemap::span;
 use shape::llsize_of;
 use build::*;
 use common::*;
 use util::ppaux::ty_to_str;
+use expr::{Dest, SaveIn, Ignore};
+use datum::*;
+use syntax::print::pprust::{expr_to_str};
+use util::common::indenter;
 
 // Boxed vector types are in some sense currently a "shorthand" for a box
 // containing an unboxed vector. This expands a boxed vector type into such an
@@ -43,11 +43,10 @@ fn get_alloc(bcx: block, vptr: ValueRef) -> ValueRef {
 }
 
 fn get_bodyptr(bcx: block, vptr: ValueRef) -> ValueRef {
-    non_gc_box_cast(bcx, GEPi(bcx, vptr, [0u, abi::box_field_body]))
+    base::non_gc_box_cast(bcx, GEPi(bcx, vptr, [0u, abi::box_field_body]))
 }
 
-fn get_dataptr(bcx: block, vptr: ValueRef)
-    -> ValueRef {
+fn get_dataptr(bcx: block, vptr: ValueRef) -> ValueRef {
     let _icx = bcx.insn_ctxt("tvec::get_dataptr");
     GEPi(bcx, vptr, [0u, abi::vec_elt_elems, 0u])
 }
@@ -60,7 +59,7 @@ fn pointer_add(bcx: block, ptr: ValueRef, bytes: ValueRef) -> ValueRef {
 }
 
 fn alloc_raw(bcx: block, unit_ty: ty::t,
-              fill: ValueRef, alloc: ValueRef, heap: heap) -> result {
+              fill: ValueRef, alloc: ValueRef, heap: heap) -> Result {
     let _icx = bcx.insn_ctxt("tvec::alloc_uniq");
     let ccx = bcx.ccx();
 
@@ -71,14 +70,14 @@ fn alloc_raw(bcx: block, unit_ty: ty::t,
         base::malloc_general_dyn(bcx, vecbodyty, heap, vecsize);
     Store(bcx, fill, GEPi(bcx, body, [0u, abi::vec_elt_fill]));
     Store(bcx, alloc, GEPi(bcx, body, [0u, abi::vec_elt_alloc]));
-    return {bcx: bcx, val: box};
+    return rslt(bcx, box);
 }
 fn alloc_uniq_raw(bcx: block, unit_ty: ty::t,
-                  fill: ValueRef, alloc: ValueRef) -> result {
+                  fill: ValueRef, alloc: ValueRef) -> Result {
     alloc_raw(bcx, unit_ty, fill, alloc, heap_exchange)
 }
 
-fn alloc_vec(bcx: block, unit_ty: ty::t, elts: uint, heap: heap) -> result {
+fn alloc_vec(bcx: block, unit_ty: ty::t, elts: uint, heap: heap) -> Result {
     let _icx = bcx.insn_ctxt("tvec::alloc_uniq");
     let ccx = bcx.ccx();
     let llunitty = type_of::type_of(ccx, unit_ty);
@@ -87,23 +86,24 @@ fn alloc_vec(bcx: block, unit_ty: ty::t, elts: uint, heap: heap) -> result {
     let fill = Mul(bcx, C_uint(ccx, elts), unit_sz);
     let alloc = if elts < 4u { Mul(bcx, C_int(ccx, 4), unit_sz) }
                 else { fill };
-    let {bcx: bcx, val: vptr} = alloc_raw(bcx, unit_ty, fill, alloc, heap);
-    return {bcx: bcx, val: vptr};
+    let Result {bcx: bcx, val: vptr} =
+        alloc_raw(bcx, unit_ty, fill, alloc, heap);
+    return rslt(bcx, vptr);
 }
 
-fn duplicate_uniq(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> result {
+fn duplicate_uniq(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> Result {
     let _icx = bcx.insn_ctxt("tvec::duplicate_uniq");
 
     let fill = get_fill(bcx, get_bodyptr(bcx, vptr));
     let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty);
-    let {bcx, val: newptr} = alloc_uniq_raw(bcx, unit_ty, fill, fill);
+    let Result {bcx, val: newptr} = alloc_uniq_raw(bcx, unit_ty, fill, fill);
 
     let data_ptr = get_dataptr(bcx, get_bodyptr(bcx, vptr));
     let new_data_ptr = get_dataptr(bcx, get_bodyptr(bcx, newptr));
-    call_memmove(bcx, new_data_ptr, data_ptr, fill);
+    base::call_memmove(bcx, new_data_ptr, data_ptr, fill);
 
     let bcx = if ty::type_needs_drop(bcx.tcx(), unit_ty) {
-        iter_vec_raw(bcx, new_data_ptr, vec_ty, fill, base::take_ty)
+        iter_vec_raw(bcx, new_data_ptr, vec_ty, fill, glue::take_ty)
     } else { bcx };
     return rslt(bcx, newptr);
 }
@@ -113,250 +113,349 @@ fn make_drop_glue_unboxed(bcx: block, vptr: ValueRef, vec_ty: ty::t) ->
     let _icx = bcx.insn_ctxt("tvec::make_drop_glue_unboxed");
     let tcx = bcx.tcx(), unit_ty = ty::sequence_element_type(tcx, vec_ty);
     if ty::type_needs_drop(tcx, unit_ty) {
-        iter_vec_unboxed(bcx, vptr, vec_ty, base::drop_ty)
+        iter_vec_unboxed(bcx, vptr, vec_ty, glue::drop_ty)
     } else { bcx }
 }
 
-enum evec_elements {
-    individual_evec(~[@ast::expr]),
-    repeating_evec(@ast::expr, uint)
+struct VecTypes {
+    vec_ty: ty::t;
+    unit_ty: ty::t;
+    llunit_ty: TypeRef;
+    llunit_size: ValueRef;
 }
 
-fn trans_evec(bcx: block, elements: evec_elements,
-              vst: ast::vstore, id: ast::node_id, dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("tvec::trans_evec");
+impl VecTypes {
+    fn to_str(ccx: @crate_ctxt) -> ~str {
+        fmt!("VecTypes {vec_ty=%s, unit_ty=%s, llunit_ty=%s, llunit_size=%s}",
+             ty_to_str(ccx.tcx, self.vec_ty),
+             ty_to_str(ccx.tcx, self.unit_ty),
+             ty_str(ccx.tn, self.llunit_ty),
+             val_str(ccx.tn, self.llunit_size))
+    }
+}
+
+fn trans_fixed_vstore(bcx: block,
+                      vstore_expr: @ast::expr,
+                      content_expr: @ast::expr,
+                      dest: expr::Dest) -> block
+{
+    //!
+    //
+    // [...]/_ allocates a fixed-size array and moves it around "by value".
+    // In this case, it means that the caller has already given us a location
+    // to store the array of the suitable size, so all we have to do is
+    // generate the content.
+
+    debug!("trans_fixed_vstore(vstore_expr=%s, dest=%?)",
+           bcx.expr_to_str(vstore_expr), dest.to_str(bcx.ccx()));
+    let _indenter = indenter();
+
+    let vt = vec_types_from_expr(bcx, vstore_expr);
+
+    return match dest {
+        Ignore => write_content(bcx, &vt, vstore_expr, content_expr, dest),
+        SaveIn(lldest) => {
+            // lldest will have type *[T x N], but we want the type *T,
+            // so use GEP to convert:
+            let lldest = GEPi(bcx, lldest, [0, 0]);
+            write_content(bcx, &vt, vstore_expr, content_expr, SaveIn(lldest))
+        }
+    };
+}
+
+fn trans_slice_vstore(bcx: block,
+                      vstore_expr: @ast::expr,
+                      content_expr: @ast::expr,
+                      dest: expr::Dest) -> block
+{
+    //!
+    //
+    // &[...] allocates memory on the stack and writes the values into it,
+    // returning a slice (pair of ptr, len).  &"..." is similar except that
+    // the memory can be statically allocated.
+
     let ccx = bcx.ccx();
-    let mut bcx = bcx;
 
-    // Handle the ignored case.
-    if dest == base::ignore {
-        match elements {
-            individual_evec(args) => {
-                for vec::each(args) |arg| {
-                    bcx = base::trans_expr(bcx, arg, base::ignore);
-                }
-            }
-            repeating_evec(element, _) => {
-                bcx = base::trans_expr(bcx, element, base::ignore);
-            }
+    debug!("trans_slice_vstore(vstore_expr=%s, dest=%s)",
+           bcx.expr_to_str(vstore_expr), dest.to_str(ccx));
+    let _indenter = indenter();
+
+    // Handle the &"..." case:
+    match content_expr.node {
+        ast::expr_lit(@{node: ast::lit_str(s), span: _}) => {
+            return trans_lit_str(bcx, content_expr, s, dest);
         }
-        return bcx;
+        _ => {}
     }
 
-    // Figure out the number of elements we need.
-    let count;
-    match elements {
-        individual_evec(args) => count = args.len(),
-        repeating_evec(_, len) => count = len
+    // Handle the &[...] case:
+    let vt = vec_types_from_expr(bcx, vstore_expr);
+    let count = elements_required(bcx, content_expr);
+    debug!("vt=%s, count=%?", vt.to_str(ccx), count);
+
+    // Make a fixed-length backing array and allocate it on the stack.
+    let llcount = C_uint(ccx, count);
+    let llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount);
+
+    // Arrange for the backing array to be cleaned up.
+    let fixed_ty = ty::mk_evec(bcx.tcx(),
+                               {ty: vt.unit_ty, mutbl: ast::m_mutbl},
+                               ty::vstore_fixed(count));
+    let llfixed_ty = T_ptr(type_of::type_of(bcx.ccx(), fixed_ty));
+    let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty);
+    add_clean(bcx, llfixed_casted, fixed_ty);
+
+    // Generate the content into the backing array.
+    let bcx = write_content(bcx, &vt, vstore_expr,
+                            content_expr, SaveIn(llfixed));
+
+    // Finally, create the slice pair itself.
+    match dest {
+        Ignore => {}
+        SaveIn(lldest) => {
+            Store(bcx, llfixed, GEPi(bcx, lldest, [0u, abi::slice_elt_base]));
+            let lllen = Mul(bcx, llcount, vt.llunit_size);
+            Store(bcx, lllen, GEPi(bcx, lldest, [0u, abi::slice_elt_len]));
+        }
     }
 
-    let vec_ty = node_id_type(bcx, id);
-    let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty);
-    let llunitty = type_of::type_of(ccx, unit_ty);
-    let unit_sz = llsize_of(ccx, llunitty);
+    return bcx;
+}
+
+fn trans_lit_str(bcx: block,
+                 lit_expr: @ast::expr,
+                 lit_str: @~str,
+                 dest: Dest) -> block
+{
+    //!
+    //
+    // Literal strings translate to slices into static memory.  This is
+    // different from trans_slice_vstore() above because it does need to copy
+    // the content anywhere.
+
+    debug!("trans_lit_str(lit_expr=%s, dest=%s)",
+           bcx.expr_to_str(lit_expr),
+           dest.to_str(bcx.ccx()));
+    let _indenter = indenter();
+
+    match dest {
+        Ignore => bcx,
+        SaveIn(lldest) => {
+            let bytes = lit_str.len() + 1; // count null-terminator too
+            let llbytes = C_uint(bcx.ccx(), bytes);
+            let llcstr = C_cstr(bcx.ccx(), *lit_str);
+            let llcstr = llvm::LLVMConstPointerCast(llcstr, T_ptr(T_i8()));
+            Store(bcx, llcstr, GEPi(bcx, lldest, [0u, abi::slice_elt_base]));
+            Store(bcx, llbytes, GEPi(bcx, lldest, [0u, abi::slice_elt_len]));
+            bcx
+        }
+    }
+}
+
+
+fn trans_uniq_or_managed_vstore(bcx: block,
+                            heap: heap,
+                            vstore_expr: @ast::expr,
+                            content_expr: @ast::expr) -> DatumBlock
+{
+    //!
+    //
+    // @[...] or ~[...] (also @"..." or ~"...") allocate boxes in the
+    // appropriate heap and write the array elements into them.
+
+    debug!("trans_uniq_or_managed_vstore(vstore_expr=%s, heap=%?)",
+           bcx.expr_to_str(vstore_expr), heap);
+    let _indenter = indenter();
+
+    let vt = vec_types_from_expr(bcx, vstore_expr);
+    let count = elements_required(bcx, content_expr);
+
+    let Result {bcx, val} = alloc_vec(bcx, vt.unit_ty, count, heap);
+    add_clean_free(bcx, val, heap);
+    let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val));
+
+    debug!("alloc_vec() returned val=%s, dataptr=%s",
+           bcx.val_str(val), bcx.val_str(dataptr));
+
+    let bcx = write_content(bcx, &vt, vstore_expr,
+                            content_expr, SaveIn(dataptr));
+
+    revoke_clean(bcx, val);
 
-    let mut {bcx, val, dataptr} =
-        match vst {
-          ast::vstore_fixed(_) => {
-            // Destination should be pre-allocated for us.
-            let v = match dest {
-              base::save_in(v) => {
-                PointerCast(bcx, v, T_ptr(llunitty))
-              }
-              _ => {
-                bcx.ccx().sess.bug(~"bad dest for vstore_fixed \
-                                    in tvec::trans_evec");
-              }
-            };
-            {bcx: bcx, val: v, dataptr: v}
-          }
-          ast::vstore_slice(_) => {
-            // Make a fake type to use for the cleanup
-            let ty = ty::mk_evec(bcx.tcx(),
-                                 {ty: unit_ty, mutbl: ast::m_mutbl},
-                                 ty::vstore_fixed(count));
-            let llty = T_ptr(type_of::type_of(bcx.ccx(), ty));
-
-            let n = C_uint(ccx, count);
-            let vp = base::arrayalloca(bcx, llunitty, n);
-            // Cast to the fake type we told cleanup to expect.
-            let vp0 = BitCast(bcx, vp, llty);
-            add_clean(bcx, vp0, ty);
-
-            let len = Mul(bcx, n, unit_sz);
-
-            let p = base::alloca(bcx, T_struct(~[T_ptr(llunitty),
-                                                ccx.int_type]));
-            Store(bcx, vp, GEPi(bcx, p, [0u, abi::slice_elt_base]));
-            Store(bcx, len, GEPi(bcx, p, [0u, abi::slice_elt_len]));
-
-            {bcx: bcx, val: p, dataptr: vp}
-          }
-          ast::vstore_uniq => {
-            let {bcx, val} = alloc_vec(bcx, unit_ty, count, heap_exchange);
-            add_clean_free(bcx, val, heap_exchange);
-            let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val));
-            {bcx: bcx, val: val, dataptr: dataptr}
-          }
-          ast::vstore_box => {
-            let {bcx, val} = alloc_vec(bcx, unit_ty, count, heap_shared);
-            add_clean_free(bcx, val, heap_shared);
-            let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val));
-            {bcx: bcx, val: val, dataptr: dataptr}
-          }
-        };
-
-
-    // Store the individual elements.
-    let mut i = 0u, temp_cleanups = ~[val];
-    debug!("trans_evec: v: %s, dataptr: %s",
-           val_str(ccx.tn, val),
-           val_str(ccx.tn, dataptr));
-    match elements {
-        individual_evec(args) => {
-            for vec::each(args) |e| {
-                let lleltptr = InBoundsGEP(bcx, dataptr, ~[C_uint(ccx, i)]);
-                bcx = base::trans_expr_save_in(bcx, e, lleltptr);
-                add_clean_temp_mem(bcx, lleltptr, unit_ty);
-                vec::push(temp_cleanups, lleltptr);
-                i += 1u;
+    return immediate_rvalue_bcx(bcx, val, vt.vec_ty);
+}
+
+fn write_content(bcx: block,
+                 vt: &VecTypes,
+                 vstore_expr: @ast::expr,
+                 content_expr: @ast::expr,
+                 dest: Dest) -> block
+{
+    let _icx = bcx.insn_ctxt("tvec::write_content");
+    let mut bcx = bcx;
+
+    debug!("write_content(vt=%s, dest=%s, vstore_expr=%?)",
+           vt.to_str(bcx.ccx()),
+           dest.to_str(bcx.ccx()),
+           bcx.expr_to_str(vstore_expr));
+    let _indenter = indenter();
+
+    match content_expr.node {
+        ast::expr_lit(@{node: ast::lit_str(s), span: _}) => {
+            match dest {
+                Ignore => {
+                    return bcx;
+                }
+                SaveIn(lldest) => {
+                    let bytes = s.len() + 1; // copy null-terminator too
+                    let llbytes = C_uint(bcx.ccx(), bytes);
+                    let llcstr = C_cstr(bcx.ccx(), *s);
+                    base::call_memmove(bcx, lldest, llcstr, llbytes);
+                    return bcx;
+                }
             }
         }
-        repeating_evec(e, len) => {
-            // We make temporary space in the hope that this will be
-            // friendlier to LLVM alias analysis.
-            let lltmpspace = base::alloca(bcx, llunitty);
-            bcx = base::trans_expr_save_in(bcx, e, lltmpspace);
-            add_clean_temp_mem(bcx, lltmpspace, unit_ty);
-            vec::push(temp_cleanups, lltmpspace);
-            for len.timesi |i| {
-                let lleltptr = InBoundsGEP(bcx, dataptr, ~[C_uint(ccx, i)]);
-                if i == len - 1 {
-                    // Move the last one in.
-                    bcx = move_val(bcx, INIT, lleltptr,
-                                   lval_owned(bcx, lltmpspace), unit_ty);
-                } else {
-                    // Copy all but the last one in.
-                    let llval = load_if_immediate(bcx, lltmpspace, unit_ty);
-                    bcx = copy_val(bcx, INIT, lleltptr, llval, unit_ty);
+        ast::expr_vec(elements, _) => {
+            match dest {
+                Ignore => {
+                    for elements.each |element| {
+                        bcx = expr::trans_into(bcx, element, Ignore);
+                    }
+                }
+
+                SaveIn(lldest) => {
+                    let mut temp_cleanups = ~[];
+                    for elements.eachi |i, element| {
+                        let lleltptr = GEPi(bcx, lldest, [i]);
+                        debug!("writing index %? with lleltptr=%?",
+                               i, bcx.val_str(lleltptr));
+                        bcx = expr::trans_into(bcx, element,
+                                               SaveIn(lleltptr));
+                        add_clean_temp_mem(bcx, lleltptr, vt.unit_ty);
+                        vec::push(temp_cleanups, lleltptr);
+                    }
+                    for vec::each(temp_cleanups) |cleanup| {
+                        revoke_clean(bcx, cleanup);
+                    }
                 }
-                add_clean_temp_mem(bcx, lleltptr, unit_ty);
-                vec::push(temp_cleanups, lleltptr);
             }
+            return bcx;
+        }
+        ast::expr_repeat(element, count_expr, _) => {
+            match dest {
+                Ignore => {
+                    return expr::trans_into(bcx, element, Ignore);
+                }
+                SaveIn(lldest) => {
+                    let count = ty::eval_repeat_count(bcx.tcx(), count_expr,
+                                                      count_expr.span);
+                    if count == 0 {
+                        return bcx;
+                    }
+
+                    let tmpdatum = unpack_datum!(bcx, {
+                        expr::trans_to_datum(bcx, element)
+                    });
+
+                    let mut temp_cleanups = ~[];
+
+                    for uint::range(0, count) |i| {
+                        let lleltptr = GEPi(bcx, lldest, [i]);
+                        if i < count - 1 {
+                            // Copy all but the last one in.
+                            bcx = tmpdatum.copy_to(bcx, INIT, lleltptr);
+                        } else {
+                            // Move the last one in.
+                            bcx = tmpdatum.move_to(bcx, INIT, lleltptr);
+                        }
+                        add_clean_temp_mem(bcx, lleltptr, vt.unit_ty);
+                        vec::push(temp_cleanups, lleltptr);
+                    }
+
+                    for vec::each(temp_cleanups) |cleanup| {
+                        revoke_clean(bcx, cleanup);
+                    }
+
+                    return bcx;
+                }
+            }
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(content_expr.span,
+                                    ~"Unexpected evec content");
         }
     }
+}
 
-    for vec::each(temp_cleanups) |cln| { revoke_clean(bcx, cln); }
+fn vec_types_from_expr(bcx: block, vec_expr: @ast::expr) -> VecTypes {
+    let vec_ty = node_id_type(bcx, vec_expr.id);
+    vec_types(bcx, vec_ty)
+}
 
-    match vst {
-      ast::vstore_fixed(_) => {
-        // We wrote into the destination in the fixed case.
-        return bcx;
-      }
-      ast::vstore_slice(_) => {
-        return base::store_in_dest(bcx, Load(bcx, val), dest);
-      }
-      _ => {
-        return base::store_in_dest(bcx, val, dest);
-      }
-    }
+fn vec_types(bcx: block, vec_ty: ty::t) -> VecTypes {
+    let ccx = bcx.ccx();
+    let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty);
+    let llunit_ty = type_of::type_of(ccx, unit_ty);
+    let llunit_size = llsize_of(ccx, llunit_ty);
+    VecTypes {vec_ty: vec_ty,
+              unit_ty: unit_ty,
+              llunit_ty: llunit_ty,
+              llunit_size: llunit_size}
 }
 
-fn trans_vstore(bcx: block, e: @ast::expr,
-                v: ast::vstore, dest: dest) -> block {
-    match e.node {
-      ast::expr_lit(@{node: ast::lit_str(s), span: _}) => {
-        return trans_estr(bcx, s, Some(v), dest);
-      }
-      ast::expr_vec(es, _) => {
-        return trans_evec(bcx, individual_evec(es), v, e.id, dest);
-      }
-      ast::expr_repeat(element, count_expr, _) => {
-        let count = ty::eval_repeat_count(bcx.tcx(), count_expr, e.span);
-        return trans_evec(bcx, repeating_evec(element, count), v, e.id, dest);
-      }
-      _ => {
-        bcx.sess().span_bug(e.span, ~"vstore on non-sequence type");
-      }
+fn elements_required(bcx: block, content_expr: @ast::expr) -> uint {
+    //! Figure out the number of elements we need to store this content
+
+    match content_expr.node {
+        ast::expr_lit(@{node: ast::lit_str(s), span: _}) => s.len() + 1,
+        ast::expr_vec(es, _) => es.len(),
+        ast::expr_repeat(_, count_expr, _) => {
+            ty::eval_repeat_count(bcx.tcx(), count_expr, content_expr.span)
+        }
+        _ => bcx.tcx().sess.span_bug(content_expr.span,
+                                     ~"Unexpected evec content")
     }
 }
 
-fn get_base_and_len(cx: block, v: ValueRef, e_ty: ty::t)
-    -> (ValueRef, ValueRef) {
+fn get_base_and_len(bcx: block,
+                    llval: ValueRef,
+                    vec_ty: ty::t) -> (ValueRef, ValueRef) {
+    //!
+    //
+    // Converts a vector into the slice pair.  The vector should be stored in
+    // `llval` which should be either immediate or by-ref as appropriate for
+    // the vector type.  If you have a datum, you would probably prefer to
+    // call `Datum::get_base_and_len()` which will handle any conversions for
+    // you.
 
-    let ccx = cx.ccx();
-    let tcx = ccx.tcx;
-    let vec_ty = ty::type_autoderef(tcx, e_ty);
-    let unit_ty = ty::sequence_element_type(tcx, vec_ty);
-    let llunitty = type_of::type_of(ccx, unit_ty);
-    let unit_sz = llsize_of(ccx, llunitty);
+    let ccx = bcx.ccx();
+    let vt = vec_types(bcx, vec_ty);
 
-    let vstore = match ty::get(vec_ty).struct {
+    let vstore = match ty::get(vt.vec_ty).struct {
       ty::ty_estr(vst) | ty::ty_evec(_, vst) => vst,
       _ => ty::vstore_uniq
     };
 
     match vstore {
-      ty::vstore_fixed(n) => {
-        let base = GEPi(cx, v, [0u, 0u]);
-        let n = if ty::type_is_str(e_ty) { n + 1u } else { n };
-        let len = Mul(cx, C_uint(ccx, n), unit_sz);
-        (base, len)
-      }
-      ty::vstore_slice(_) => {
-        let base = Load(cx, GEPi(cx, v, [0u, abi::slice_elt_base]));
-        let len = Load(cx, GEPi(cx, v, [0u, abi::slice_elt_len]));
-        (base, len)
-      }
-      ty::vstore_uniq | ty::vstore_box => {
-        debug!("get_base_and_len: %s", val_str(ccx.tn, v));
-        let body = tvec::get_bodyptr(cx, v);
-        (tvec::get_dataptr(cx, body), tvec::get_fill(cx, body))
-      }
+        ty::vstore_fixed(n) => {
+            let base = GEPi(bcx, llval, [0u, 0u]);
+            let n = if ty::type_is_str(vec_ty) { n + 1u } else { n };
+            let len = Mul(bcx, C_uint(ccx, n), vt.llunit_size);
+            (base, len)
+        }
+        ty::vstore_slice(_) => {
+            let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base]));
+            let len = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len]));
+            (base, len)
+        }
+        ty::vstore_uniq | ty::vstore_box => {
+            let body = tvec::get_bodyptr(bcx, llval);
+            (tvec::get_dataptr(bcx, body), tvec::get_fill(bcx, body))
+        }
     }
 }
 
-fn trans_estr(bcx: block, s: @~str, vstore: Option<ast::vstore>,
-              dest: dest) -> block {
-    let _icx = bcx.insn_ctxt("tvec::trans_estr");
-    if dest == base::ignore { return bcx; }
-    let ccx = bcx.ccx();
-
-    let c = match vstore {
-      Some(ast::vstore_fixed(_)) => {
-        // "hello"/_  =>  "hello"/5  =>  ~[i8 x 6] in llvm
-        debug!("trans_estr: fixed: %s", *s);
-        C_postr(*s)
-      }
-
-      Some(ast::vstore_slice(_)) | None => {
-        // "hello"  =>  (*i8, 6u) in llvm
-        debug!("trans_estr: slice '%s'", *s);
-        C_estr_slice(ccx, *s)
-      }
-
-      Some(ast::vstore_uniq) => {
-        let cs = PointerCast(bcx, C_cstr(ccx, *s), T_ptr(T_i8()));
-        let len = C_uint(ccx, str::len(*s));
-        let c = Call(bcx, ccx.upcalls.str_new_uniq, ~[cs, len]);
-        PointerCast(bcx, c,
-                    T_unique_ptr(T_unique(ccx, T_vec(ccx, T_i8()))))
-      }
-
-      Some(ast::vstore_box) => {
-        let cs = PointerCast(bcx, C_cstr(ccx, *s), T_ptr(T_i8()));
-        let len = C_uint(ccx, str::len(*s));
-        let c = Call(bcx, ccx.upcalls.str_new_shared, ~[cs, len]);
-        PointerCast(bcx, c,
-                    T_box_ptr(T_box(ccx, T_vec(ccx, T_i8()))))
-      }
-    };
-
-    debug!("trans_estr: type: %s", val_str(ccx.tn, c));
-    base::store_in_dest(bcx, c, dest)
-}
-
-type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> result;
+type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> Result;
 
 type iter_vec_block = fn(block, ValueRef, ty::t) -> block;
 
@@ -373,21 +472,21 @@ fn iter_vec_raw(bcx: block, data_ptr: ValueRef, vec_ty: ty::t,
     let data_end_ptr = pointer_add(bcx, data_ptr, fill);
 
     // Now perform the iteration.
-    let header_cx = sub_block(bcx, ~"iter_vec_loop_header");
-    Br(bcx, header_cx.llbb);
+    let header_bcx = base::sub_block(bcx, ~"iter_vec_loop_header");
+    Br(bcx, header_bcx.llbb);
     let data_ptr =
-        Phi(header_cx, val_ty(data_ptr), ~[data_ptr], ~[bcx.llbb]);
+        Phi(header_bcx, val_ty(data_ptr), ~[data_ptr], ~[bcx.llbb]);
     let not_yet_at_end =
-        ICmp(header_cx, lib::llvm::IntULT, data_ptr, data_end_ptr);
-    let body_cx = sub_block(header_cx, ~"iter_vec_loop_body");
-    let next_cx = sub_block(header_cx, ~"iter_vec_next");
-    CondBr(header_cx, not_yet_at_end, body_cx.llbb, next_cx.llbb);
-    let body_cx = f(body_cx, data_ptr, unit_ty);
-    AddIncomingToPhi(data_ptr, InBoundsGEP(body_cx, data_ptr,
+        ICmp(header_bcx, lib::llvm::IntULT, data_ptr, data_end_ptr);
+    let body_bcx = base::sub_block(header_bcx, ~"iter_vec_loop_body");
+    let next_bcx = base::sub_block(header_bcx, ~"iter_vec_next");
+    CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb);
+    let body_bcx = f(body_bcx, data_ptr, unit_ty);
+    AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr,
                                            ~[C_int(bcx.ccx(), 1)]),
-                     body_cx.llbb);
-    Br(body_cx, header_cx.llbb);
-    return next_cx;
+                     body_bcx.llbb);
+    Br(body_bcx, header_bcx.llbb);
+    return next_bcx;
 
 }
 
diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs
index 2a4322eb215..40bf644c626 100644
--- a/src/rustc/middle/trans/type_of.rs
+++ b/src/rustc/middle/trans/type_of.rs
@@ -7,6 +7,7 @@ use std::map::hashmap;
 
 export type_of;
 export type_of_dtor;
+export type_of_explicit_arg;
 export type_of_explicit_args;
 export type_of_fn_from_ty;
 export type_of_fn;
@@ -14,18 +15,19 @@ export type_of_glue_fn;
 export type_of_non_gc_box;
 export type_of_rooted;
 
-fn type_of_explicit_args(cx: @crate_ctxt,
-                         inputs: ~[ty::arg]) -> ~[TypeRef] {
-    do vec::map(inputs) |arg| {
-        let arg_ty = arg.ty;
-        let llty = type_of(cx, arg_ty);
-        match ty::resolved_mode(cx.tcx, arg.mode) {
-          ast::by_val => llty,
-          _ => T_ptr(llty)
-        }
+fn type_of_explicit_arg(ccx: @crate_ctxt, arg: ty::arg) -> TypeRef {
+    let arg_ty = arg.ty;
+    let llty = type_of(ccx, arg_ty);
+    match ty::resolved_mode(ccx.tcx, arg.mode) {
+        ast::by_val => llty,
+        _ => T_ptr(llty)
     }
 }
 
+fn type_of_explicit_args(ccx: @crate_ctxt, inputs: ~[ty::arg]) -> ~[TypeRef] {
+    inputs.map(|arg| type_of_explicit_arg(ccx, arg))
+}
+
 fn type_of_fn(cx: @crate_ctxt, inputs: ~[ty::arg],
               output: ty::t) -> TypeRef {
     let mut atys: ~[TypeRef] = ~[];
@@ -145,7 +147,10 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
             let mt_ty = f.mt.ty;
             vec::push(tys, type_of(cx, mt_ty));
         }
-        T_struct(tys)
+
+        // n.b.: introduce an extra layer of indirection to match
+        // structs
+        T_struct(~[T_struct(tys)])
       }
       ty::ty_fn(_) => T_fn_pair(cx, type_of_fn_from_ty(cx, t)),
       ty::ty_trait(_, _, _) => T_opaque_trait(cx),
@@ -188,12 +193,13 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
             type_of(cx, t)
         };
 
+        // include a byte flag if there is a dtor so that we know when we've
+        // been dropped
         if ty::ty_dtor(cx.tcx, did) != None {
-            // resource type
-            tys = ~[T_i8(), T_struct(tys)];
+            common::set_struct_body(llty, ~[T_struct(tys), T_i8()]);
+        } else {
+            common::set_struct_body(llty, ~[T_struct(tys)]);
         }
-
-        common::set_struct_body(llty, tys);
       }
       _ => ()
     }
diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs
index 10b7992c0f2..8048a34c219 100644
--- a/src/rustc/middle/trans/type_use.rs
+++ b/src/rustc/middle/trans/type_use.rs
@@ -40,7 +40,7 @@ fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
       None => ()
     }
     let fn_id_loc = if fn_id.crate == local_crate { fn_id }
-                    else { base::maybe_instantiate_inline(ccx, fn_id) };
+                    else { inline::maybe_instantiate_inline(ccx, fn_id) };
     // Conservatively assume full use for recursive loops
     ccx.type_use_cache.insert(fn_id, vec::from_elem(n_tps, 3u));
 
@@ -82,25 +82,23 @@ fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
                                  abi, _) => {
         if abi == foreign_abi_rust_intrinsic {
             let flags = match cx.ccx.sess.str_of(i.ident) {
-              ~"size_of" |  ~"pref_align_of" | ~"min_align_of" |
-              ~"init" |  ~"reinterpret_cast" |
-              ~"move_val" | ~"move_val_init" => {
-                use_repr
-              }
-              ~"get_tydesc" | ~"needs_drop" => {
-                use_tydesc
-              }
-              ~"atomic_xchg"     | ~"atomic_xadd"     | ~"atomic_xsub" |
-              ~"atomic_xchg_acq" | ~"atomic_xadd_acq" | ~"atomic_xsub_acq" |
-              ~"atomic_xchg_rel" | ~"atomic_xadd_rel" | ~"atomic_xsub_rel" =>
-              { 0u }
-              ~"visit_tydesc" | ~"forget" | ~"addr_of" |
-              ~"frame_address" | ~"morestack_addr" => {
-                0u
-              }
-              // would be cool to make these an enum instead of strings!
-              _ => fail fmt!("unknown intrinsic in type_use %?",
-                             cx.ccx.sess.str_of(i.ident))
+                ~"size_of"  | ~"pref_align_of"    | ~"min_align_of" |
+                ~"init"     | ~"reinterpret_cast" |
+                ~"move_val" | ~"move_val_init" => use_repr,
+
+                ~"get_tydesc" | ~"needs_drop" => use_tydesc,
+
+                ~"atomic_xchg"     | ~"atomic_xadd"     |
+                ~"atomic_xsub"     | ~"atomic_xchg_acq" |
+                ~"atomic_xadd_acq" | ~"atomic_xsub_acq" |
+                ~"atomic_xchg_rel" | ~"atomic_xadd_rel" |
+                ~"atomic_xsub_rel" => 0,
+
+                ~"visit_tydesc"  | ~"forget" | ~"addr_of" |
+                ~"frame_address" | ~"morestack_addr" => 0,
+
+                // would be cool to make these an enum instead of strings!
+                _ => fail ~"unknown intrinsic in type_use"
             };
             for uint::range(0u, n_tps) |n| { cx.uses[n] |= flags;}
         }
@@ -288,7 +286,7 @@ fn handle_body(cx: ctx, body: blk) {
             }
         },
         visit_item: |_i, _cx, _v| { },
-        .. *visit::default_visitor()
+        ..*visit::default_visitor()
     });
     v.visit_block(body, cx, v);
 }
diff --git a/src/rustc/middle/trans/uniq.rs b/src/rustc/middle/trans/uniq.rs
index cd6710f274c..6ab91c4a1d7 100644
--- a/src/rustc/middle/trans/uniq.rs
+++ b/src/rustc/middle/trans/uniq.rs
@@ -4,52 +4,41 @@ use common::*;
 use build::*;
 use base::*;
 use shape::llsize_of;
+use datum::immediate_rvalue;
 
 export make_free_glue, autoderef, duplicate;
 
-fn make_free_glue(bcx: block, vptrptr: ValueRef, t: ty::t)
+fn make_free_glue(bcx: block, vptrptr: ValueRef, box_ty: ty::t)
     -> block {
     let _icx = bcx.insn_ctxt("uniq::make_free_glue");
-    let vptr = Load(bcx, vptrptr);
-    do with_cond(bcx, IsNotNull(bcx, vptr)) |bcx| {
-        let content_ty = content_ty(t);
-        let body_ptr = opaque_box_body(bcx, content_ty, vptr);
-        let bcx = drop_ty(bcx, body_ptr, content_ty);
-        trans_unique_free(bcx, vptr)
+    let box_datum = immediate_rvalue(Load(bcx, vptrptr), box_ty);
+
+    let not_null = IsNotNull(bcx, box_datum.val);
+    do with_cond(bcx, not_null) |bcx| {
+        let body_datum = box_datum.box_body(bcx);
+        let bcx = glue::drop_ty(bcx, body_datum.to_ref_llval(bcx),
+                                body_datum.ty);
+        glue::trans_unique_free(bcx, box_datum.val)
     }
 }
 
-fn content_ty(t: ty::t) -> ty::t {
-    match ty::get(t).struct {
-      ty::ty_uniq({ty: ct, _}) => ct,
-      _ => core::unreachable()
-    }
-}
+fn duplicate(bcx: block, src_box: ValueRef, src_ty: ty::t) -> Result {
+    let _icx = bcx.insn_ctxt("uniq::duplicate");
 
-fn autoderef(bcx: block, v: ValueRef, t: ty::t) -> {v: ValueRef, t: ty::t} {
-    let content_ty = content_ty(t);
-    let v = opaque_box_body(bcx, content_ty, v);
-    return {v: v, t: content_ty};
-}
+    // Load the body of the source (*src)
+    let src_datum = immediate_rvalue(src_box, src_ty);
+    let body_datum = src_datum.box_body(bcx);
 
-fn duplicate(bcx: block, v: ValueRef, t: ty::t) -> result {
-    let _icx = bcx.insn_ctxt("uniq::duplicate");
-    let content_ty = content_ty(t);
+    // Malloc space in exchange heap and copy src into it
     let {bcx: bcx, box: dst_box, body: dst_body} =
-        malloc_unique(bcx, content_ty);
-
-    let src_box = v;
-    let src_body = opaque_box_body(bcx, content_ty, src_box);
-    let src_body = load_if_immediate(bcx, src_body, content_ty);
-    debug!("ST: %?", val_str(bcx.ccx().tn, src_body));
-    debug!("DT: %?", val_str(bcx.ccx().tn, dst_body));
-    let bcx = copy_val(bcx, INIT, dst_body, src_body, content_ty);
+        malloc_unique(bcx, body_datum.ty);
+    body_datum.copy_to(bcx, datum::INIT, dst_body);
 
+    // Copy the type descriptor
     let src_tydesc_ptr = GEPi(bcx, src_box,
                               [0u, back::abi::box_field_tydesc]);
     let dst_tydesc_ptr = GEPi(bcx, dst_box,
                               [0u, back::abi::box_field_tydesc]);
-
     let td = Load(bcx, src_tydesc_ptr);
     Store(bcx, td, dst_tydesc_ptr);
 
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index 5debbf6b006..85ef0797437 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -11,7 +11,8 @@ use syntax::ast_util;
 use syntax::ast_util::{is_local, local_def, new_def_hash};
 use syntax::codemap::span;
 use metadata::csearch;
-use util::ppaux::{region_to_str, explain_region, vstore_to_str};
+use util::ppaux::{region_to_str, explain_region, vstore_to_str,
+                  note_and_explain_region};
 use middle::lint;
 use middle::lint::{get_lint_level, allow};
 use syntax::ast::*;
@@ -36,12 +37,13 @@ export def_has_ty_params;
 export expr_has_ty_params;
 export expr_ty;
 export expr_ty_params_and_ty;
-export expr_is_lval;
+export expr_is_lval, expr_kind;
+export ExprKind, LvalueExpr, RvalueDatumExpr, RvalueDpsExpr, RvalueStmtExpr;
 export field_ty;
 export fold_ty, fold_sty_to_ty, fold_region, fold_regions;
 export fold_regions_and_ty, walk_regions_and_ty;
 export field;
-export field_idx;
+export field_idx, field_idx_strict;
 export get_field;
 export get_fields;
 export get_element_type;
@@ -120,7 +122,7 @@ export kind_is_owned;
 export proto_kind, kind_lteq, type_kind;
 export operators;
 export type_err, terr_vstore_kind;
-export type_err_to_str;
+export type_err_to_str, note_and_explain_type_err;
 export expected_found;
 export type_needs_drop;
 export type_is_empty;
@@ -2382,7 +2384,7 @@ fn node_id_to_type(cx: ctxt, id: ast::node_id) -> t {
     match smallintmap::find(*cx.node_types, id as uint) {
        Some(t) => t,
        None => cx.sess.bug(
-           fmt!("node_id_to_type: unbound node ID %s",
+           fmt!("node_id_to_type: no type for node `%s`",
                 ast_map::node_id_to_str(cx.items, id,
                                         cx.sess.parse_sess.interner)))
     }
@@ -2535,13 +2537,156 @@ fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map,
     }
 }
 
-fn expr_is_lval(method_map: typeck::method_map, e: @ast::expr) -> bool {
-    match e.node {
-      ast::expr_path(_) | ast::expr_unary(ast::deref, _) => true,
-      ast::expr_field(_, _, _) | ast::expr_index(_, _) => {
-        !method_map.contains_key(e.id)
-      }
-      _ => false
+fn resolve_expr(tcx: ctxt, expr: @ast::expr) -> ast::def {
+    match tcx.def_map.find(expr.id) {
+        Some(def) => def,
+        None => {
+            tcx.sess.span_bug(expr.span, fmt!(
+                "No def-map entry for expr %?", expr.id));
+        }
+    }
+}
+
+fn expr_is_lval(tcx: ctxt,
+                method_map: typeck::method_map,
+                e: @ast::expr) -> bool {
+    match expr_kind(tcx, method_map, e) {
+        LvalueExpr => true,
+        RvalueDpsExpr | RvalueDatumExpr | RvalueStmtExpr => false
+    }
+}
+
+/// We categorize expressions into three kinds.  The distinction between
+/// lvalue/rvalue is fundamental to the language.  The distinction between the
+/// two kinds of rvalues is an artifact of trans which reflects how we will
+/// generate code for that kind of expression.  See trans/expr.rs for more
+/// information.
+enum ExprKind {
+    LvalueExpr,
+    RvalueDpsExpr,
+    RvalueDatumExpr,
+    RvalueStmtExpr
+}
+
+fn expr_kind(tcx: ctxt,
+             method_map: typeck::method_map,
+             expr: @ast::expr) -> ExprKind {
+    if method_map.contains_key(expr.id) {
+        // Overloaded operations are generally calls, and hence they are
+        // generated via DPS.  However, assign_op (e.g., `x += y`) is an
+        // exception, as its result is always unit.
+        return match expr.node {
+            ast::expr_assign_op(*) => RvalueStmtExpr,
+            _ => RvalueDpsExpr
+        };
+    }
+
+    match expr.node {
+        ast::expr_path(*) => {
+            match resolve_expr(tcx, expr) {
+                ast::def_fn(*) | ast::def_static_method(*) |
+                ast::def_variant(*) => RvalueDpsExpr,
+
+                // Note: there is actually a good case to be made that
+                // def_args, particularly those of immediate type, ought to
+                // considered rvalues.
+                ast::def_const(*) |
+                ast::def_binding(*) |
+                ast::def_upvar(*) |
+                ast::def_arg(*) |
+                ast::def_local(*) |
+                ast::def_self(*) => LvalueExpr,
+
+                move def => {
+                    tcx.sess.span_bug(expr.span, fmt!(
+                        "Uncategorized def for expr %?: %?",
+                        expr.id, def));
+                }
+            }
+        }
+
+        ast::expr_unary(ast::deref, _) |
+        ast::expr_field(*) |
+        ast::expr_index(*) => {
+            LvalueExpr
+        }
+
+        ast::expr_call(*) |
+        ast::expr_rec(*) |
+        ast::expr_struct(*) |
+        ast::expr_tup(*) |
+        ast::expr_if(*) |
+        ast::expr_match(*) |
+        ast::expr_fn(*) |
+        ast::expr_fn_block(*) |
+        ast::expr_loop_body(*) |
+        ast::expr_do_body(*) |
+        ast::expr_block(*) |
+        ast::expr_copy(*) |
+        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_vec(*) => {
+            RvalueDpsExpr
+        }
+
+        ast::expr_cast(*) => {
+            match smallintmap::find(*tcx.node_types, expr.id as uint) {
+                Some(t) => {
+                    if ty::type_is_immediate(t) {
+                        RvalueDatumExpr
+                    } else {
+                        RvalueDpsExpr
+                    }
+                }
+                None => {
+                    // Technically, it should not happen that the expr is not
+                    // present within the table.  However, it DOES happen
+                    // during type check, because the final types from the
+                    // expressions are not yet recorded in the tcx.  At that
+                    // time, though, we are only interested in knowing lvalue
+                    // vs rvalue.  It would be better to base this decision on
+                    // the AST type in cast node---but (at the time of this
+                    // writing) it's not easy to distinguish casts to traits
+                    // from other casts based on the AST.  This should be
+                    // easier in the future, when casts to traits would like
+                    // like @Foo, ~Foo, or &Foo.
+                    RvalueDatumExpr
+                }
+            }
+        }
+
+        ast::expr_break(*) |
+        ast::expr_again(*) |
+        ast::expr_ret(*) |
+        ast::expr_log(*) |
+        ast::expr_fail(*) |
+        ast::expr_assert(*) |
+        ast::expr_while(*) |
+        ast::expr_loop(*) |
+        ast::expr_assign(*) |
+        ast::expr_move(*) |
+        ast::expr_swap(*) |
+        ast::expr_assign_op(*) => {
+            RvalueStmtExpr
+        }
+
+        ast::expr_lit(_) | // Note: lit_str is carved out above
+        ast::expr_unary(*) |
+        ast::expr_addr_of(*) |
+        ast::expr_binary(*) |
+        ast::expr_vstore(_, ast::vstore_box) |
+        ast::expr_vstore(_, ast::vstore_uniq) => {
+            RvalueDatumExpr
+        }
+
+        ast::expr_mac(*) => {
+            tcx.sess.span_bug(
+                expr.span,
+                ~"macro expression remains after expansion");
+        }
     }
 }
 
@@ -2553,12 +2698,21 @@ fn stmt_node_id(s: @ast::stmt) -> ast::node_id {
     }
 }
 
-fn field_idx(id: ast::ident, fields: ~[field]) -> Option<uint> {
+fn field_idx(id: ast::ident, fields: &[field]) -> Option<uint> {
     let mut i = 0u;
     for fields.each |f| { if f.ident == id { return Some(i); } i += 1u; }
     return None;
 }
 
+fn field_idx_strict(tcx: ty::ctxt, id: ast::ident, fields: &[field]) -> uint {
+    let mut i = 0u;
+    for fields.each |f| { if f.ident == id { return i; } i += 1u; }
+    tcx.sess.bug(fmt!(
+        "No field named `%s` found in the list of fields `%?`",
+        tcx.sess.str_of(id),
+        fields.map(|f| tcx.sess.str_of(f.ident))));
+}
+
 fn get_field(tcx: ctxt, rec_ty: t, id: ast::ident) -> field {
     match vec::find(get_fields(rec_ty), |f| f.ident == id) {
       Some(f) => f,
@@ -2730,6 +2884,15 @@ fn ty_sort_str(cx: ctxt, t: t) -> ~str {
 }
 
 fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
+    /*!
+     *
+     * Explains the source of a type err in a short,
+     * human readable way.  This is meant to be placed in
+     * parentheses after some larger message.  You should
+     * also invoke `note_and_explain_type_err()` afterwards
+     * to present additional details, particularly when
+     * it comes to lifetime-related errors. */
+
     fn terr_vstore_kind_to_str(k: terr_vstore_kind) -> ~str {
         match k {
             terr_vec => ~"[]",
@@ -2795,20 +2958,14 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
         fmt!("expected argument mode %s, but found %s",
              mode_to_str(values.expected), mode_to_str(values.found))
       }
-      terr_regions_does_not_outlive(subregion, superregion) => {
-        fmt!("%s does not necessarily outlive %s",
-                    explain_region(cx, superregion),
-                    explain_region(cx, subregion))
+      terr_regions_does_not_outlive(*) => {
+        fmt!("lifetime mismatch")
       }
-      terr_regions_not_same(region1, region2) => {
-        fmt!("%s is not the same as %s",
-                    explain_region(cx, region1),
-                    explain_region(cx, region2))
+      terr_regions_not_same(*) => {
+        fmt!("lifetimes are not the same")
       }
-      terr_regions_no_overlap(region1, region2) => {
-        fmt!("%s does not intersect %s",
-                    explain_region(cx, region1),
-                    explain_region(cx, region2))
+      terr_regions_no_overlap(*) => {
+        fmt!("lifetimes do not intersect")
       }
       terr_vstores_differ(k, values) => {
         fmt!("%s storage differs: expected %s but found %s",
@@ -2835,6 +2992,27 @@ fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
     }
 }
 
+fn note_and_explain_type_err(cx: ctxt, err: &type_err) {
+    match *err {
+        terr_regions_does_not_outlive(subregion, superregion) => {
+            note_and_explain_region(cx, ~"", subregion, ~"...");
+            note_and_explain_region(cx, ~"...does not necessarily outlive ",
+                                    superregion, ~"");
+        }
+        terr_regions_not_same(region1, region2) => {
+            note_and_explain_region(cx, ~"", region1, ~"...");
+            note_and_explain_region(cx, ~"...is not the same lifetime as ",
+                                    region2, ~"");
+        }
+        terr_regions_no_overlap(region1, region2) => {
+            note_and_explain_region(cx, ~"", region1, ~"...");
+            note_and_explain_region(cx, ~"...does not overlap ",
+                                    region2, ~"");
+        }
+        _ => {}
+    }
+}
+
 fn def_has_ty_params(def: ast::def) -> bool {
     match def {
       ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_, _)
diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs
index 87012054779..3f4ec261698 100644
--- a/src/rustc/middle/typeck.rs
+++ b/src/rustc/middle/typeck.rs
@@ -225,12 +225,13 @@ fn require_same_types(
     }
 
     match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
-      result::Ok(()) => true,
-      result::Err(ref terr) => {
-        l_tcx.sess.span_err(span, msg() + ~": " +
-            ty::type_err_to_str(l_tcx, terr));
-        false
-      }
+        result::Ok(()) => true,
+        result::Err(ref terr) => {
+            l_tcx.sess.span_err(span, msg() + ~": " +
+                                ty::type_err_to_str(l_tcx, terr));
+            ty::note_and_explain_type_err(l_tcx, terr);
+            false
+        }
     }
 }
 
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index ef3b2290983..d74bf4f208e 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -667,6 +667,7 @@ impl @fn_ctxt {
                  self.infcx().ty_to_str(e),
                  self.infcx().ty_to_str(a),
                  ty::type_err_to_str(self.ccx.tcx, err)));
+        ty::note_and_explain_type_err(self.ccx.tcx, err);
     }
 
     fn mk_subty(a_is_expected: bool, span: span,
@@ -2049,29 +2050,36 @@ fn check_decl_initializer(fcx: @fn_ctxt, nid: ast::node_id,
 
 fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool {
     let mut bot = false;
+    let tcx = fcx.ccx.tcx;
 
-    let t = ty::mk_var(fcx.ccx.tcx, fcx.inh.locals.get(local.node.id));
+    let t = ty::mk_var(tcx, fcx.inh.locals.get(local.node.id));
     fcx.write_ty(local.node.id, t);
+
+    let is_lvalue;
     match local.node.init {
-      Some(init) => {
-        bot = check_decl_initializer(fcx, local.node.id, init);
-      }
-      _ => {/* fall through */ }
+        Some(init) => {
+            bot = check_decl_initializer(fcx, local.node.id, init);
+            is_lvalue = ty::expr_is_lval(tcx, fcx.ccx.method_map, init.expr);
+        }
+        _ => {
+            is_lvalue = true;
+        }
     }
 
     let region =
-        ty::re_scope(fcx.ccx.tcx.region_map.get(local.node.id));
+        ty::re_scope(tcx.region_map.get(local.node.id));
     let pcx = {
         fcx: fcx,
-        map: pat_id_map(fcx.ccx.tcx.def_map, local.node.pat),
+        map: pat_id_map(tcx.def_map, local.node.pat),
         alt_region: region,
         block_region: region,
-        pat_region: region,
-        matching_lvalue: true, // FIXME(#3235) Make this more flexible
-        has_guard: false,
-        mut ever_bound_by_ref: false,
     };
     alt::check_pat(pcx, local.node.pat, t);
+    let has_guard = false;
+    alt::check_legality_of_move_bindings(fcx,
+                                         is_lvalue,
+                                         has_guard,
+                                         [local.node.pat]);
     return bot;
 }
 
diff --git a/src/rustc/middle/typeck/check/alt.rs b/src/rustc/middle/typeck/check/alt.rs
index 31202da4500..0282a9d78c4 100644
--- a/src/rustc/middle/typeck/check/alt.rs
+++ b/src/rustc/middle/typeck/check/alt.rs
@@ -1,4 +1,6 @@
 use syntax::print::pprust;
+use syntax::ast_util::{walk_pat};
+use pat_util::{pat_is_variant};
 
 fn check_alt(fcx: @fn_ctxt,
              expr: @ast::expr,
@@ -9,7 +11,7 @@ fn check_alt(fcx: @fn_ctxt,
 
     let pattern_ty = fcx.infcx().next_ty_var();
     bot = check_expr_with(fcx, discrim, pattern_ty);
-    let is_lvalue = ty::expr_is_lval(fcx.ccx.method_map, discrim);
+    let is_lvalue = ty::expr_is_lval(tcx, fcx.ccx.method_map, discrim);
 
     // Typecheck the patterns first, so that we get types for all the
     // bindings.
@@ -18,17 +20,16 @@ fn check_alt(fcx: @fn_ctxt,
             fcx: fcx,
             map: pat_id_map(tcx.def_map, arm.pats[0]),
             alt_region: ty::re_scope(expr.id),
-            block_region: ty::re_scope(arm.body.node.id),
-            pat_region: ty::re_scope(expr.id),
-            // The following three fields determine whether 'move' is allowed.
-            matching_lvalue: is_lvalue,
-            has_guard: arm.guard.is_some(),
-            // Each arm is freshly allowed to decide whether it can 'move'.
-            mut ever_bound_by_ref: false,
+            block_region: ty::re_scope(arm.body.node.id)
         };
 
         for arm.pats.each |p| { check_pat(pcx, p, pattern_ty);}
+        check_legality_of_move_bindings(fcx,
+                                        is_lvalue,
+                                        arm.guard.is_some(),
+                                        arm.pats);
     }
+
     // Now typecheck the blocks.
     let mut result_ty = fcx.infcx().next_ty_var();
     let mut arm_non_bot = false;
@@ -47,20 +48,72 @@ fn check_alt(fcx: @fn_ctxt,
     return bot;
 }
 
+fn check_legality_of_move_bindings(fcx: @fn_ctxt,
+                                   is_lvalue: bool,
+                                   has_guard: bool,
+                                   pats: &[@ast::pat])
+{
+    let tcx = fcx.tcx();
+    let def_map = tcx.def_map;
+    let mut by_ref = None;
+    let mut any_by_move = false;
+    for pats.each |pat| {
+        do pat_util::pat_bindings(def_map, pat) |bm, _id, span, _path| {
+            match bm {
+                ast::bind_by_ref(_) | ast::bind_by_implicit_ref => {
+                    by_ref = Some(span);
+                }
+                ast::bind_by_move => {
+                    any_by_move = true;
+                }
+                _ => { }
+            }
+        }
+    }
+
+    if !any_by_move { return; } // pointless micro-optimization
+    for pats.each |pat| {
+        do walk_pat(pat) |p| {
+            if !pat_is_variant(def_map, p) {
+                match p.node {
+                    ast::pat_ident(ast::bind_by_move, _, sub) => {
+                        // check legality of moving out of the enum
+                        if sub.is_some() {
+                            tcx.sess.span_err(
+                                p.span,
+                                ~"cannot bind by-move with sub-bindings");
+                        } else if has_guard {
+                            tcx.sess.span_err(
+                                p.span,
+                                ~"cannot bind by-move into a pattern guard");
+                        } else if by_ref.is_some() {
+                            tcx.sess.span_err(
+                                p.span,
+                                ~"cannot bind by-move and by-ref \
+                                  in the same pattern");
+                            tcx.sess.span_note(
+                                by_ref.get(),
+                                ~"by-ref binding occurs here");
+                        } else if is_lvalue {
+                            tcx.sess.span_err(
+                                p.span,
+                                ~"cannot bind by-move when \
+                                  matching an lvalue");
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+    }
+}
+
+
 type pat_ctxt = {
     fcx: @fn_ctxt,
     map: pat_id_map,
-    alt_region: ty::region,
-    block_region: ty::region,
-    /* Equal to either alt_region or block_region. */
-    pat_region: ty::region,
-    /* Moving out is only permitted when matching rvalues. */
-    matching_lvalue: bool,
-    /* Moving out is not permitted with guards. */
-    has_guard: bool,
-    /* If a pattern binding binds by-reference ever, then binding by-move in
-     * the same arm is disallowed (no "ref x @ some(move y)", etc etc). */
-    mut ever_bound_by_ref: bool,
+    alt_region: ty::region,   // Region for the alt as a whole
+    block_region: ty::region, // Region for the block of the arm
 };
 
 fn check_pat_variant(pcx: pat_ctxt, pat: @ast::pat, path: @ast::path,
@@ -175,7 +228,6 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
 
         match bm {
           ast::bind_by_ref(mutbl) => {
-            pcx.ever_bound_by_ref = true;
             // if the binding is like
             //    ref x | ref const x | ref mut x
             // then the type of x is &M T where M is the mutability
@@ -193,26 +245,8 @@ fn check_pat(pcx: pat_ctxt, pat: @ast::pat, expected: ty::t) {
           }
           ast::bind_by_move => {
             demand::eqtype(fcx, pat.span, expected, typ);
-            // check legality of moving out of the enum
-            if sub.is_some() {
-                tcx.sess.span_err(pat.span,
-                    ~"cannot bind by-move with sub-bindings");
-            }
-            if pcx.has_guard {
-                tcx.sess.span_err(pat.span,
-                    ~"cannot bind by-move into a pattern guard");
-            }
-            if pcx.ever_bound_by_ref {
-                tcx.sess.span_err(pat.span,
-                    ~"cannot bind by-move and by-ref in the same pattern");
-            }
-            if pcx.matching_lvalue {
-                tcx.sess.span_err(pat.span,
-                    ~"cannot bind by-move when matching an lvalue");
-            }
           }
           ast::bind_by_implicit_ref => {
-            pcx.ever_bound_by_ref = true;
             demand::eqtype(fcx, pat.span, expected, typ);
           }
         }
diff --git a/src/rustc/middle/typeck/check/regionck.rs b/src/rustc/middle/typeck/check/regionck.rs
index c980b6670a5..6b7a8b4e402 100644
--- a/src/rustc/middle/typeck/check/regionck.rs
+++ b/src/rustc/middle/typeck/check/regionck.rs
@@ -286,12 +286,14 @@ fn constrain_free_variables(
                 ~"captured variable does not outlive the enclosing closure");
             note_and_explain_region(
                 tcx,
-                ~"captured variable is valid for",
-                en_region);
+                ~"captured variable is valid for ",
+                en_region,
+                ~"");
             note_and_explain_region(
                 tcx,
-                ~"closure is valid for",
-                region);
+                ~"closure is valid for ",
+                region,
+                ~"");
           }
         }
     }
@@ -337,8 +339,9 @@ fn constrain_regions_in_type(
                 fmt!("reference is not valid outside of its lifetime"));
             note_and_explain_region(
                 tcx,
-                ~"the reference is only valid for",
-                region);
+                ~"the reference is only valid for ",
+                region,
+                ~"");
             rcx.errors_reported += 1u;
           }
           result::Ok(()) => {
diff --git a/src/rustc/middle/typeck/infer/region_var_bindings.rs b/src/rustc/middle/typeck/infer/region_var_bindings.rs
index ff0afdb4816..70dd5777e3d 100644
--- a/src/rustc/middle/typeck/infer/region_var_bindings.rs
+++ b/src/rustc/middle/typeck/infer/region_var_bindings.rs
@@ -1104,21 +1104,23 @@ impl RegionVarBindings {
 
                     note_and_explain_region(
                         self.tcx,
-                        ~"first, the lifetime cannot outlive",
-                        upper_bound.region);
+                        ~"first, the lifetime cannot outlive ",
+                        upper_bound.region,
+                        ~"...");
 
                     self.tcx.sess.span_note(
                         upper_bound.span,
-                        fmt!("due to the following expression"));
+                        fmt!("...due to the following expression"));
 
                     note_and_explain_region(
                         self.tcx,
-                        ~"but, the lifetime must be valid for",
-                        lower_bound.region);
+                        ~"but, the lifetime must be valid for ",
+                        lower_bound.region,
+                        ~"...");
 
                     self.tcx.sess.span_note(
                         lower_bound.span,
-                        fmt!("due to the following expression"));
+                        fmt!("...due to the following expression"));
 
                     return;
                 }
@@ -1154,21 +1156,23 @@ impl RegionVarBindings {
 
                     note_and_explain_region(
                         self.tcx,
-                        ~"first, the lifetime must be contained by",
-                        upper_bound_1.region);
+                        ~"first, the lifetime must be contained by ",
+                        upper_bound_1.region,
+                        ~"...");
 
                     self.tcx.sess.span_note(
                         upper_bound_1.span,
-                        fmt!("due to the following expression"));
+                        fmt!("...due to the following expression"));
 
                     note_and_explain_region(
                         self.tcx,
-                        ~"but, the lifetime must also be contained by",
-                        upper_bound_2.region);
+                        ~"but, the lifetime must also be contained by ",
+                        upper_bound_2.region,
+                        ~"...");
 
                     self.tcx.sess.span_note(
                         upper_bound_2.span,
-                        fmt!("due to the following expression"));
+                        fmt!("...due to the following expression"));
 
                     return;
                   }
diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc
index 5be0ea6d8d8..70f617fe4a5 100644
--- a/src/rustc/rustc.rc
+++ b/src/rustc/rustc.rc
@@ -35,6 +35,13 @@ use back_ = back;
 
 mod middle {
     mod trans {
+        mod inline;
+        mod monomorphize;
+        mod controlflow;
+        mod glue;
+        mod datum;
+        mod callee;
+        mod expr;
         mod common;
         mod consts;
         mod type_of;
diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs
index 2b452f1d8b1..ba7b9ffa478 100644
--- a/src/rustc/util/ppaux.rs
+++ b/src/rustc/util/ppaux.rs
@@ -21,16 +21,19 @@ use syntax::{ast, ast_util};
 use syntax::ast_map;
 use driver::session::session;
 
-fn note_and_explain_region(cx: ctxt, prefix: ~str, region: ty::region) {
+fn note_and_explain_region(cx: ctxt,
+                           prefix: ~str,
+                           region: ty::region,
+                           suffix: ~str) {
     match explain_region_and_span(cx, region) {
       (str, Some(span)) => {
         cx.sess.span_note(
             span,
-            fmt!("%s %s", prefix, str));
+            fmt!("%s%s%s", prefix, str, suffix));
       }
       (str, None) => {
         cx.sess.note(
-            fmt!("%s %s", prefix, str));
+            fmt!("%s%s%s", prefix, str, suffix));
       }
     }
 }
@@ -55,7 +58,7 @@ fn explain_region_and_span(cx: ctxt, region: ty::region)
           Some(ast_map::node_expr(expr)) => {
             match expr.node {
               ast::expr_call(*) => explain_span(cx, ~"call", expr.span),
-              ast::expr_match(*) => explain_span(cx, ~"alt", expr.span),
+              ast::expr_match(*) => explain_span(cx, ~"match", expr.span),
               _ => explain_span(cx, ~"expression", expr.span)
             }
           }
diff --git a/src/test/bench/graph500-bfs.rs b/src/test/bench/graph500-bfs.rs
index 1dd116b2b41..1402c9e8604 100644
--- a/src/test/bench/graph500-bfs.rs
+++ b/src/test/bench/graph500-bfs.rs
@@ -174,9 +174,9 @@ fn bfs2(graph: graph, key: node_id) -> bfs_result {
             match c {
               white => {
                 let i = i as node_id;
-                
+
                 let neighbors = graph[i];
-                
+
                 let mut color = white;
 
                 do neighbors.each() |k| {
@@ -217,7 +217,8 @@ fn pbfs(&&graph: arc::ARC<graph>, key: node_id) -> bfs_result {
         black(node_id)
     };
 
-    let mut colors = do vec::from_fn((*arc::get(&graph)).len()) |i| {
+    let graph_vec = arc::get(&graph); // FIXME #3387 requires this temp
+    let mut colors = do vec::from_fn(graph_vec.len()) |i| {
         if i as node_id == key {
             gray(key)
         }
@@ -243,7 +244,8 @@ fn pbfs(&&graph: arc::ARC<graph>, key: node_id) -> bfs_result {
 
         let color = arc::ARC(colors);
 
-        colors = do par::mapi_factory(*arc::get(&color)) {
+        let color_vec = arc::get(&color); // FIXME #3387 requires this temp
+        colors = do par::mapi_factory(*color_vec) {
             let colors = arc::clone(&color);
             let graph = arc::clone(&graph);
             fn~(i: uint, c: color) -> color {
@@ -253,11 +255,11 @@ fn pbfs(&&graph: arc::ARC<graph>, key: node_id) -> bfs_result {
                 match c {
                   white => {
                     let i = i as node_id;
-                    
+
                     let neighbors = graph[i];
-                    
+
                     let mut color = white;
-                    
+
                     do neighbors.each() |k| {
                         if is_gray(colors[k]) {
                             color = gray(k);
diff --git a/src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs b/src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs
new file mode 100644
index 00000000000..71f4934effd
--- /dev/null
+++ b/src/test/compile-fail/bind-by-move-neither-can-live-while-the-other-survives-4.rs
@@ -0,0 +1,9 @@
+struct X { x: (); drop { error!("destructor runs"); } }
+
+fn main() {
+    let x = Some((X { x: () }, X { x: () }));
+    match move x {
+        Some((move _y, ref _z)) => { }, //~ ERROR cannot bind by-move and by-ref in the same pattern
+        None => fail
+    }
+}
diff --git a/src/test/compile-fail/borrowck-confuse-region.rs b/src/test/compile-fail/borrowck-confuse-region.rs
index 0f7323358d7..a320cfae1b0 100644
--- a/src/test/compile-fail/borrowck-confuse-region.rs
+++ b/src/test/compile-fail/borrowck-confuse-region.rs
@@ -6,9 +6,11 @@
 
 
 fn get() -> &int {
+    //~^ NOTE borrowed pointer must be valid for the anonymous lifetime #1 defined on
+    //~^^ NOTE ...but borrowed value is only valid for the block at
     let x = 3;
     return &x;
-    //~^ ERROR illegal borrow: borrowed pointer must be valid for the anonymous lifetime #1 defined on the block at 8:17, but the borrowed value is only valid for the block at 8:17
+    //~^ ERROR illegal borrow
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs
new file mode 100644
index 00000000000..ef7c8da4b19
--- /dev/null
+++ b/src/test/compile-fail/borrowck-loan-in-overloaded-op.rs
@@ -0,0 +1,15 @@
+// xfail-test #3387
+
+enum foo = ~uint;
+
+impl foo: Add<foo, foo> {
+    pure fn add(f: foo) -> foo {
+        foo(~(**self + **f))
+    }
+}
+
+fn main() {
+    let x = foo(~3);
+    let _y = x + move x;
+    //~^ ERROR moving out of immutable local variable prohibited due to outstanding loan
+}
diff --git a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
index 57e195a9075..b2852784e18 100644
--- a/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
+++ b/src/test/compile-fail/regions-infer-borrow-scope-within-loop.rs
@@ -7,7 +7,7 @@ fn foo(cond: fn() -> bool, box: fn() -> @int) {
 
 	// Here we complain because the resulting region
 	// of this borrow is the fn body as a whole.
-        y = borrow(x); //~ ERROR illegal borrow: managed value would have to be rooted
+        y = borrow(x); //~ ERROR illegal borrow: cannot root managed value long enough
 
         assert *x == *y;
         if cond() { break; }
diff --git a/src/test/run-pass/autoderef-method-on-trait-monomorphized.rs b/src/test/run-pass/autoderef-method-on-trait-monomorphized.rs
new file mode 100644
index 00000000000..85868f44cfd
--- /dev/null
+++ b/src/test/run-pass/autoderef-method-on-trait-monomorphized.rs
@@ -0,0 +1,16 @@
+trait double {
+    fn double() -> uint;
+}
+
+impl uint: double {
+    fn double() -> uint { self * 2u }
+}
+
+fn is_equal<D: double>(x: @D, exp: uint) {
+    assert x.double() == exp;
+}
+
+fn main() {
+    let x = @(3u as double);
+    is_equal(x, 6);
+}
diff --git a/src/test/run-pass/autoderef-method-on-trait.rs b/src/test/run-pass/autoderef-method-on-trait.rs
new file mode 100644
index 00000000000..68dde1e25a5
--- /dev/null
+++ b/src/test/run-pass/autoderef-method-on-trait.rs
@@ -0,0 +1,12 @@
+trait double {
+    fn double() -> uint;
+}
+
+impl uint: double {
+    fn double() -> uint { self * 2u }
+}
+
+fn main() {
+    let x = @(3u as double);
+    assert x.double() == 6u;
+}
diff --git a/src/test/run-pass/const-fields-and-indexing.rs b/src/test/run-pass/const-fields-and-indexing.rs
index 8a02ea13186..387ca032f6a 100644
--- a/src/test/run-pass/const-fields-and-indexing.rs
+++ b/src/test/run-pass/const-fields-and-indexing.rs
@@ -6,6 +6,10 @@ const q : int = y[2];
 const s : {a: int, b: int} = {a: 10, b: 20};
 const t : int = s.b;
 
+const k : {a: int, b: int, c: {d: int, e: int}} = {a: 10, b: 20, c: {d: 30,
+                                                                     e: 40}};
+const m : int = k.c.e;
+
 fn main() {
     io::println(fmt!("%?", p));
     io::println(fmt!("%?", q));
diff --git a/src/test/run-pass/pipe-presentation-examples.rs b/src/test/run-pass/pipe-presentation-examples.rs
index 34e384e3121..cf34c44d500 100644
--- a/src/test/run-pass/pipe-presentation-examples.rs
+++ b/src/test/run-pass/pipe-presentation-examples.rs
@@ -23,8 +23,8 @@ macro_rules! select_if (
     } => {
         if $index == $count {
             match move pipes::try_recv($port) {
-              $(Some($message($($(move $x,)+)* next)) => {
-                let $next = unsafe { let x <- *ptr::addr_of(next); x };
+              $(Some($message($($(move $x,)+)* move next)) => {
+                let $next = next;
                 $e
               })+
               _ => fail