about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-01-17 07:56:45 -0800
committerbors <bors@rust-lang.org>2014-01-17 07:56:45 -0800
commit4098327b1fe1112ddf661b587be9eeec1d80adde (patch)
tree3d1a8d2613c1101d46657fd5de25d32403e9626b
parent1e1871f35eb83ae6a63952a145e5132deffded2c (diff)
parentb520c2f28002db0e4120797d823380914871bac4 (diff)
downloadrust-4098327b1fe1112ddf661b587be9eeec1d80adde.tar.gz
rust-4098327b1fe1112ddf661b587be9eeec1d80adde.zip
auto merge of #11585 : nikomatsakis/rust/issue-3511-rvalue-lifetimes, r=pcwalton
Major changes:

- Define temporary scopes in a syntax-based way that basically defaults
  to the innermost statement or conditional block, except for in
  a `let` initializer, where we default to the innermost block. Rules
  are documented in the code, but not in the manual (yet).
  See new test run-pass/cleanup-value-scopes.rs for examples.
- Refactors Datum to better define cleanup roles.
- Refactor cleanup scopes to not be tied to basic blocks, permitting
  us to have a very large number of scopes (one per AST node).
- Introduce nascent documentation in trans/doc.rs covering datums and
  cleanup in a more comprehensive way.

r? @pcwalton
-rw-r--r--src/librustc/lib/llvm.rs7
-rw-r--r--src/librustc/middle/astencode.rs6
-rw-r--r--src/librustc/middle/borrowck/gather_loans/lifetime.rs22
-rw-r--r--src/librustc/middle/borrowck/gather_loans/mod.rs7
-rw-r--r--src/librustc/middle/borrowck/move_data.rs4
-rw-r--r--src/librustc/middle/mem_categorization.rs19
-rw-r--r--src/librustc/middle/pat_util.rs11
-rw-r--r--src/librustc/middle/region.rs541
-rw-r--r--src/librustc/middle/resolve.rs2
-rw-r--r--src/librustc/middle/trans/_match.rs471
-rw-r--r--src/librustc/middle/trans/adt.rs19
-rw-r--r--src/librustc/middle/trans/asm.rs26
-rw-r--r--src/librustc/middle/trans/base.rs901
-rw-r--r--src/librustc/middle/trans/build.rs11
-rw-r--r--src/librustc/middle/trans/callee.rs408
-rw-r--r--src/librustc/middle/trans/cleanup.rs948
-rw-r--r--src/librustc/middle/trans/closure.rs17
-rw-r--r--src/librustc/middle/trans/common.rs474
-rw-r--r--src/librustc/middle/trans/controlflow.rs382
-rw-r--r--src/librustc/middle/trans/datum.rs1122
-rw-r--r--src/librustc/middle/trans/debuginfo.rs4
-rw-r--r--src/librustc/middle/trans/doc.rs237
-rw-r--r--src/librustc/middle/trans/expr.rs1652
-rw-r--r--src/librustc/middle/trans/foreign.rs19
-rw-r--r--src/librustc/middle/trans/glue.rs89
-rw-r--r--src/librustc/middle/trans/inline.rs1
-rw-r--r--src/librustc/middle/trans/intrinsic.rs41
-rw-r--r--src/librustc/middle/trans/meth.rs120
-rw-r--r--src/librustc/middle/trans/mod.rs2
-rw-r--r--src/librustc/middle/trans/monomorphize.rs1
-rw-r--r--src/librustc/middle/trans/reflect.rs25
-rw-r--r--src/librustc/middle/trans/tvec.rs194
-rw-r--r--src/librustc/middle/trans/type_of.rs6
-rw-r--r--src/librustc/middle/trans/write_guard.rs41
-rw-r--r--src/librustc/middle/ty.rs5
-rw-r--r--src/librustc/middle/typeck/check/mod.rs29
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs98
-rw-r--r--src/librustc/util/ppaux.rs8
-rw-r--r--src/librustdoc/html/render.rs3
-rw-r--r--src/libstd/ascii.rs13
-rw-r--r--src/libstd/comm/mod.rs2
-rw-r--r--src/libstd/io/mem.rs6
-rw-r--r--src/libstd/io/process.rs14
-rw-r--r--src/libstd/option.rs19
-rw-r--r--src/libstd/path/posix.rs13
-rw-r--r--src/libstd/path/windows.rs14
-rw-r--r--src/libstd/rt/crate_map.rs18
-rw-r--r--src/libstd/unstable/intrinsics.rs6
-rw-r--r--src/libstd/vec.rs29
-rw-r--r--src/libsyntax/ast_util.rs6
-rw-r--r--src/libsyntax/ext/expand.rs30
-rw-r--r--src/libsyntax/ext/format.rs15
-rw-r--r--src/libsyntax/opt_vec.rs47
-rw-r--r--src/libsyntax/visit.rs11
-rw-r--r--src/rustllvm/RustWrapper.cpp11
-rw-r--r--src/test/compile-fail/borrowck-borrow-from-temporary.rs22
-rw-r--r--src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs31
-rw-r--r--src/test/compile-fail/borrowck-rvalues-mutable-bad.rs38
-rw-r--r--src/test/compile-fail/cleanup-rvalue-scopes-cf.rs45
-rw-r--r--src/test/debug-info/destructured-local.rs6
-rw-r--r--src/test/run-pass/borrowck-rvalues-mutable.rs12
-rw-r--r--src/test/run-pass/cleanup-arm-conditional.rs43
-rw-r--r--src/test/run-pass/cleanup-rvalue-for-scope.rs70
-rw-r--r--src/test/run-pass/cleanup-rvalue-scopes.rs137
-rw-r--r--src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs39
-rw-r--r--src/test/run-pass/cleanup-shortcircuit.rs30
-rw-r--r--src/test/run-pass/intrinsic-move-val.rs12
-rw-r--r--src/test/run-pass/issue-10626.rs4
-rw-r--r--src/test/run-pass/issue-9382.rs8
-rw-r--r--src/test/run-pass/move-1.rs1
-rw-r--r--src/test/run-pass/regions-dependent-let-ref.rs19
-rw-r--r--src/test/run-pass/uninit-empty-types.rs24
72 files changed, 5039 insertions, 3729 deletions
diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs
index 98f67b6442b..c259fa6a618 100644
--- a/src/librustc/lib/llvm.rs
+++ b/src/librustc/lib/llvm.rs
@@ -1698,6 +1698,7 @@ pub mod llvm {
 
         pub fn LLVMDICompositeTypeSetTypeArray(CompositeType: ValueRef, TypeArray: ValueRef);
         pub fn LLVMTypeToString(Type: TypeRef) -> *c_char;
+        pub fn LLVMValueToString(value_ref: ValueRef) -> *c_char;
 
         pub fn LLVMIsAArgument(value_ref: ValueRef) -> ValueRef;
 
@@ -1847,8 +1848,10 @@ impl TypeNames {
 
     pub fn val_to_str(&self, val: ValueRef) -> ~str {
         unsafe {
-            let ty = Type::from_ref(llvm::LLVMTypeOf(val));
-            self.type_to_str(ty)
+            let s = llvm::LLVMValueToString(val);
+            let ret = from_c_str(s);
+            free(s as *c_void);
+            ret
         }
     }
 }
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 3e0436a8ec2..1c1482bcc79 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -12,13 +12,14 @@
 use c = metadata::common;
 use cstore = metadata::cstore;
 use driver::session::Session;
-use e = metadata::encoder;
 use metadata::decoder;
+use e = metadata::encoder;
+use middle::freevars::freevar_entry;
+use middle::region;
 use metadata::tydecode;
 use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter,
                          RegionParameter};
 use metadata::tyencode;
-use middle::freevars::freevar_entry;
 use middle::typeck::{method_origin, method_map_entry};
 use middle::{ty, typeck, moves};
 use middle;
@@ -153,6 +154,7 @@ pub fn decode_inlined_item(cdata: @cstore::crate_metadata,
         debug!("< Decoded inlined fn: {}::{}",
                ast_map::path_to_str(path, token::get_ident_interner()),
                tcx.sess.str_of(ident));
+        region::resolve_inlined_item(tcx.sess, &tcx.region_maps, &ii);
         decode_side_tables(xcx, ast_doc);
         match ii {
           ast::IIItem(i) => {
diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
index 16a7f4c3e7e..9a4869fb6d1 100644
--- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs
+++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs
@@ -67,6 +67,9 @@ impl<'a> GuaranteeLifetimeContext<'a> {
 
     fn check(&self, cmt: mc::cmt, discr_scope: Option<ast::NodeId>) -> R {
         //! Main routine. Walks down `cmt` until we find the "guarantor".
+        debug!("guarantee_lifetime.check(cmt={}, loan_region={})",
+               cmt.repr(self.bccx.tcx),
+               self.loan_region.repr(self.bccx.tcx));
 
         match cmt.cat {
             mc::cat_rvalue(..) |
@@ -220,7 +223,7 @@ impl<'a> GuaranteeLifetimeContext<'a> {
 
         // If inside of a match arm, expand the rooting to the entire
         // match. See the detailed discussion in `check()` above.
-        let mut root_scope = match discr_scope {
+        let root_scope = match discr_scope {
             None => root_scope,
             Some(id) => {
                 if self.bccx.is_subscope_of(root_scope, id) {
@@ -231,17 +234,6 @@ impl<'a> GuaranteeLifetimeContext<'a> {
             }
         };
 
-        // FIXME(#3511) grow to the nearest cleanup scope---this can
-        // cause observable errors if freezing!
-        if !self.bccx.tcx.region_maps.is_cleanup_scope(root_scope) {
-            debug!("{:?} is not a cleanup scope, adjusting", root_scope);
-
-            let cleanup_scope =
-                self.bccx.tcx.region_maps.cleanup_scope(root_scope);
-
-            root_scope = cleanup_scope;
-        }
-
         // Add a record of what is required
         let rm_key = root_map_key {id: cmt_deref.id, derefs: derefs};
         let root_info = RootInfo {scope: root_scope};
@@ -301,8 +293,8 @@ impl<'a> GuaranteeLifetimeContext<'a> {
         // See the SCOPE(LV) function in doc.rs
 
         match cmt.cat {
-            mc::cat_rvalue(cleanup_scope_id) => {
-                ty::ReScope(cleanup_scope_id)
+            mc::cat_rvalue(temp_scope) => {
+                temp_scope
             }
             mc::cat_copied_upvar(_) => {
                 ty::ReScope(self.item_scope_id)
@@ -313,7 +305,7 @@ impl<'a> GuaranteeLifetimeContext<'a> {
             mc::cat_local(local_id) |
             mc::cat_arg(local_id) |
             mc::cat_self(local_id) => {
-                self.bccx.tcx.region_maps.encl_region(local_id)
+                ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id))
             }
             mc::cat_deref(_, _, mc::unsafe_ptr(..)) => {
                 ty::ReStatic
diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs
index b1337fca0c8..00e648db732 100644
--- a/src/librustc/middle/borrowck/gather_loans/mod.rs
+++ b/src/librustc/middle/borrowck/gather_loans/mod.rs
@@ -662,8 +662,9 @@ impl<'a> GatherLoanCtxt<'a> {
         //! with immutable `&` pointers, because borrows of such pointers
         //! do not require restrictions and hence do not cause a loan.
 
-        let lexical_scope = self.bccx.tcx.region_maps.encl_scope(lp.node_id());
-        if self.bccx.tcx.region_maps.is_subscope_of(lexical_scope, loan_scope) {
+        let rm = &self.bccx.tcx.region_maps;
+        let lexical_scope = rm.var_scope(lp.node_id());
+        if rm.is_subscope_of(lexical_scope, loan_scope) {
             lexical_scope
         } else {
             assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
@@ -688,7 +689,7 @@ impl<'a> GatherLoanCtxt<'a> {
             let arg_cmt = mc_ctxt.cat_rvalue(
                 arg.id,
                 arg.pat.span,
-                body.id, // Arguments live only as long as the fn body.
+                ty::ReScope(body.id), // Args live only as long as the fn body.
                 arg_ty);
 
             self.gather_pat(arg_cmt, arg.pat, None);
diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs
index e61ebd39ca7..53cf5646cfb 100644
--- a/src/librustc/middle/borrowck/move_data.rs
+++ b/src/librustc/middle/borrowck/move_data.rs
@@ -471,7 +471,7 @@ impl MoveData {
             for path in paths.get().iter() {
                 match *path.loan_path {
                     LpVar(id) => {
-                        let kill_id = tcx.region_maps.encl_scope(id);
+                        let kill_id = tcx.region_maps.var_scope(id);
                         let path = {
                             let path_map = self.path_map.borrow();
                             *path_map.get().get(&path.loan_path)
@@ -490,7 +490,7 @@ impl MoveData {
                     var_assignments.get().iter().enumerate() {
                 match *self.path_loan_path(assignment.path) {
                     LpVar(id) => {
-                        let kill_id = tcx.region_maps.encl_scope(id);
+                        let kill_id = tcx.region_maps.var_scope(id);
                         dfcx_assign.add_kill(kill_id, assignment_index);
                     }
                     LpExtend(..) => {
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 8cf39d14763..70d4f63a164 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -60,7 +60,7 @@ use syntax::parse::token;
 
 #[deriving(Eq)]
 pub enum categorization {
-    cat_rvalue(ast::NodeId),           // temporary val, argument is its scope
+    cat_rvalue(ty::Region),            // temporary val, argument is its scope
     cat_static_item,
     cat_copied_upvar(CopiedUpvar),     // upvar copied into @fn or ~fn env
     cat_stack_upvar(cmt),              // by ref upvar from ||
@@ -585,21 +585,26 @@ impl mem_categorization_ctxt {
     pub fn cat_rvalue_node<N:ast_node>(&self,
                                        node: &N,
                                        expr_ty: ty::t) -> cmt {
-        self.cat_rvalue(node.id(),
-                        node.span(),
-                        self.tcx.region_maps.cleanup_scope(node.id()),
-                        expr_ty)
+        match self.tcx.region_maps.temporary_scope(node.id()) {
+            Some(scope) => {
+                self.cat_rvalue(node.id(), node.span(),
+                                ty::ReScope(scope), expr_ty)
+            }
+            None => {
+                self.cat_rvalue(node.id(), node.span(), ty::ReStatic, expr_ty)
+            }
+        }
     }
 
     pub fn cat_rvalue(&self,
                       cmt_id: ast::NodeId,
                       span: Span,
-                      cleanup_scope_id: ast::NodeId,
+                      temp_scope: ty::Region,
                       expr_ty: ty::t) -> cmt {
         @cmt_ {
             id:cmt_id,
             span:span,
-            cat:cat_rvalue(cleanup_scope_id),
+            cat:cat_rvalue(temp_scope),
             mutbl:McDeclared,
             ty:expr_ty
         }
diff --git a/src/librustc/middle/pat_util.rs b/src/librustc/middle/pat_util.rs
index 85217a7d55e..50096c012be 100644
--- a/src/librustc/middle/pat_util.rs
+++ b/src/librustc/middle/pat_util.rs
@@ -108,3 +108,14 @@ pub fn pat_contains_bindings(dm: resolve::DefMap, pat: &Pat) -> bool {
     });
     contains_bindings
 }
+
+pub fn simple_identifier<'a>(pat: &'a Pat) -> Option<&'a Path> {
+    match pat.node {
+        PatIdent(BindByValue(_), ref path, None) => {
+            Some(path)
+        }
+        _ => {
+            None
+        }
+    }
+}
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index ef755b12d15..c202684c7ab 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -31,6 +31,7 @@ use syntax::codemap::Span;
 use syntax::{ast, visit};
 use syntax::visit::{Visitor, FnKind};
 use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
+use syntax::ast_util::{stmt_id};
 
 /**
 The region maps encode information about region relationships.
@@ -46,30 +47,30 @@ The region maps encode information about region relationships.
   - the free region map is populated during type check as we check
     each function. See the function `relate_free_regions` for
     more information.
-- `cleanup_scopes` includes scopes where trans cleanups occur
-  - this is intended to reflect the current state of trans, not
-    necessarily how I think things ought to work
+- `temporary_scopes` includes scopes where cleanups for temporaries occur.
+  These are statements and loop/fn bodies.
 */
 pub struct RegionMaps {
     priv scope_map: RefCell<HashMap<ast::NodeId, ast::NodeId>>,
+    priv var_map: RefCell<HashMap<ast::NodeId, ast::NodeId>>,
     priv free_region_map: RefCell<HashMap<FreeRegion, ~[FreeRegion]>>,
-    priv cleanup_scopes: RefCell<HashSet<ast::NodeId>>,
+    priv rvalue_scopes: RefCell<HashMap<ast::NodeId, ast::NodeId>>,
+    priv terminating_scopes: RefCell<HashSet<ast::NodeId>>,
 }
 
 #[deriving(Clone)]
 pub struct Context {
-    // Scope where variables should be parented to
     var_parent: Option<ast::NodeId>,
 
     // Innermost enclosing expression
     parent: Option<ast::NodeId>,
 }
 
-struct RegionResolutionVisitor {
+struct RegionResolutionVisitor<'a> {
     sess: Session,
 
     // Generated maps:
-    region_maps: RegionMaps,
+    region_maps: &'a RegionMaps,
 }
 
 
@@ -91,22 +92,41 @@ impl RegionMaps {
         free_region_map.get().insert(sub, ~[sup]);
     }
 
-    pub fn record_parent(&self, sub: ast::NodeId, sup: ast::NodeId) {
-        debug!("record_parent(sub={:?}, sup={:?})", sub, sup);
+    pub fn record_encl_scope(&self, sub: ast::NodeId, sup: ast::NodeId) {
+        debug!("record_encl_scope(sub={}, sup={})", sub, sup);
         assert!(sub != sup);
 
         let mut scope_map = self.scope_map.borrow_mut();
         scope_map.get().insert(sub, sup);
     }
 
-    pub fn record_cleanup_scope(&self, scope_id: ast::NodeId) {
-        //! Records that a scope is a CLEANUP SCOPE.  This is invoked
-        //! from within regionck.  We wait until regionck because we do
-        //! not know which operators are overloaded until that point,
-        //! and only overloaded operators result in cleanup scopes.
+    pub fn record_var_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) {
+        debug!("record_var_scope(sub={}, sup={})", var, lifetime);
+        assert!(var != lifetime);
 
-        let mut cleanup_scopes = self.cleanup_scopes.borrow_mut();
-        cleanup_scopes.get().insert(scope_id);
+        let mut var_map = self.var_map.borrow_mut();
+        var_map.get().insert(var, lifetime);
+    }
+
+    pub fn record_rvalue_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) {
+        debug!("record_rvalue_scope(sub={}, sup={})", var, lifetime);
+        assert!(var != lifetime);
+
+        let mut rvalue_scopes = self.rvalue_scopes.borrow_mut();
+        rvalue_scopes.get().insert(var, lifetime);
+    }
+
+    pub fn mark_as_terminating_scope(&self, scope_id: ast::NodeId) {
+        /*!
+         * Records that a scope is a TERMINATING SCOPE. Whenever we
+         * create automatic temporaries -- e.g. by an
+         * expression like `a().f` -- they will be freed within
+         * the innermost terminating scope.
+         */
+
+        debug!("record_terminating_scope(scope_id={})", scope_id);
+        let mut terminating_scopes = self.terminating_scopes.borrow_mut();
+        terminating_scopes.get().insert(scope_id);
     }
 
     pub fn opt_encl_scope(&self, id: ast::NodeId) -> Option<ast::NodeId> {
@@ -122,24 +142,51 @@ impl RegionMaps {
         let scope_map = self.scope_map.borrow();
         match scope_map.get().find(&id) {
             Some(&r) => r,
-            None => { fail!("No enclosing scope for id {:?}", id); }
+            None => { fail!("No enclosing scope for id {}", id); }
         }
     }
 
-    pub fn is_cleanup_scope(&self, scope_id: ast::NodeId) -> bool {
-        let cleanup_scopes = self.cleanup_scopes.borrow();
-        cleanup_scopes.get().contains(&scope_id)
+    pub fn var_scope(&self, var_id: ast::NodeId) -> ast::NodeId {
+        /*!
+         * Returns the lifetime of the local variable `var_id`
+         */
+
+        let var_map = self.var_map.borrow();
+        match var_map.get().find(&var_id) {
+            Some(&r) => r,
+            None => { fail!("No enclosing scope for id {}", var_id); }
+        }
     }
 
-    pub fn cleanup_scope(&self, expr_id: ast::NodeId) -> ast::NodeId {
-        //! Returns the scope when temps in expr will be cleaned up
+    pub fn temporary_scope(&self, expr_id: ast::NodeId) -> Option<ast::NodeId> {
+        //! Returns the scope when temp created by expr_id will be cleaned up
+
+        // check for a designated rvalue scope
+        let rvalue_scopes = self.rvalue_scopes.borrow();
+        match rvalue_scopes.get().find(&expr_id) {
+            Some(&s) => {
+                debug!("temporary_scope({}) = {} [custom]", expr_id, s);
+                return Some(s);
+            }
+            None => { }
+        }
 
+        // else, locate the innermost terminating scope
         let mut id = self.encl_scope(expr_id);
-        let cleanup_scopes = self.cleanup_scopes.borrow();
-        while !cleanup_scopes.get().contains(&id) {
-            id = self.encl_scope(id);
+        let terminating_scopes = self.terminating_scopes.borrow();
+        while !terminating_scopes.get().contains(&id) {
+            match self.opt_encl_scope(id) {
+                Some(p) => {
+                    id = p;
+                }
+                None => {
+                    debug!("temporary_scope({}) = None", expr_id);
+                    return None;
+                }
+            }
         }
-        return id;
+        debug!("temporary_scope({}) = {} [enclosing]", expr_id, id);
+        return Some(id);
     }
 
     pub fn encl_region(&self, id: ast::NodeId) -> ty::Region {
@@ -148,6 +195,12 @@ impl RegionMaps {
         ty::ReScope(self.encl_scope(id))
     }
 
+    pub fn var_region(&self, id: ast::NodeId) -> ty::Region {
+        //! Returns the lifetime of the variable `id`.
+
+        ty::ReScope(self.var_scope(id))
+    }
+
     pub fn scopes_intersect(&self, scope1: ast::NodeId, scope2: ast::NodeId)
                             -> bool {
         self.is_subscope_of(scope1, scope2) ||
@@ -168,7 +221,7 @@ impl RegionMaps {
             let scope_map = self.scope_map.borrow();
             match scope_map.get().find(&s) {
                 None => {
-                    debug!("is_subscope_of({:?}, {:?}, s={:?})=false",
+                    debug!("is_subscope_of({}, {}, s={})=false",
                            subscope, superscope, s);
 
                     return false;
@@ -177,7 +230,7 @@ impl RegionMaps {
             }
         }
 
-        debug!("is_subscope_of({:?}, {:?})=true",
+        debug!("is_subscope_of({}, {})=true",
                subscope, superscope);
 
         return true;
@@ -323,67 +376,138 @@ impl RegionMaps {
 }
 
 /// Records the current parent (if any) as the parent of `child_id`.
-fn parent_to_expr(visitor: &mut RegionResolutionVisitor,
-                  cx: Context, child_id: ast::NodeId, sp: Span) {
-    debug!("region::parent_to_expr(span={:?})",
-           visitor.sess.codemap.span_to_str(sp));
-    for parent_id in cx.parent.iter() {
-        visitor.region_maps.record_parent(child_id, *parent_id);
+fn record_superlifetime(visitor: &mut RegionResolutionVisitor,
+                        cx: Context,
+                        child_id: ast::NodeId,
+                        _sp: Span) {
+    for &parent_id in cx.parent.iter() {
+        visitor.region_maps.record_encl_scope(child_id, parent_id);
+    }
+}
+
+/// Records the lifetime of a local variable as `cx.var_parent`
+fn record_var_lifetime(visitor: &mut RegionResolutionVisitor,
+                       cx: Context,
+                       var_id: ast::NodeId,
+                       _sp: Span) {
+    match cx.var_parent {
+        Some(parent_id) => {
+            visitor.region_maps.record_var_scope(var_id, parent_id);
+        }
+        None => {
+            // this can happen in extern fn declarations like
+            //
+            // extern fn isalnum(c: c_int) -> c_int
+        }
     }
 }
 
 fn resolve_block(visitor: &mut RegionResolutionVisitor,
                  blk: &ast::Block,
                  cx: Context) {
-    // Record the parent of this block.
-    parent_to_expr(visitor, cx, blk.id, blk.span);
+    debug!("resolve_block(blk.id={})", blk.id);
 
-    // Descend.
-    let new_cx = Context {var_parent: Some(blk.id),
-                          parent: Some(blk.id)};
-    visit::walk_block(visitor, blk, new_cx);
+    // Record the parent of this block.
+    record_superlifetime(visitor, cx, blk.id, blk.span);
+
+    // We treat the tail expression in the block (if any) somewhat
+    // differently from the statements. The issue has to do with
+    // temporary lifetimes. If the user writes:
+    //
+    //   {
+    //     ... (&foo()) ...
+    //   }
+    //
+
+    let subcx = Context {var_parent: Some(blk.id), parent: Some(blk.id)};
+    visit::walk_block(visitor, blk, subcx);
 }
 
 fn resolve_arm(visitor: &mut RegionResolutionVisitor,
                arm: &ast::Arm,
                cx: Context) {
+    visitor.region_maps.mark_as_terminating_scope(arm.body.id);
+
+    match arm.guard {
+        Some(expr) => {
+            visitor.region_maps.mark_as_terminating_scope(expr.id);
+        }
+        None => { }
+    }
+
     visit::walk_arm(visitor, arm, cx);
 }
 
 fn resolve_pat(visitor: &mut RegionResolutionVisitor,
                pat: &ast::Pat,
                cx: Context) {
-    assert_eq!(cx.var_parent, cx.parent);
-    parent_to_expr(visitor, cx, pat.id, pat.span);
+    record_superlifetime(visitor, cx, pat.id, pat.span);
+
+    // If this is a binding (or maybe a binding, I'm too lazy to check
+    // the def map) then record the lifetime of that binding.
+    match pat.node {
+        ast::PatIdent(..) => {
+            record_var_lifetime(visitor, cx, pat.id, pat.span);
+        }
+        _ => { }
+    }
+
     visit::walk_pat(visitor, pat, cx);
 }
 
 fn resolve_stmt(visitor: &mut RegionResolutionVisitor,
                 stmt: &ast::Stmt,
                 cx: Context) {
-    match stmt.node {
-        ast::StmtDecl(..) => {
-            visit::walk_stmt(visitor, stmt, cx);
-        }
-        ast::StmtExpr(_, stmt_id) |
-        ast::StmtSemi(_, stmt_id) => {
-            parent_to_expr(visitor, cx, stmt_id, stmt.span);
-            let expr_cx = Context {parent: Some(stmt_id), ..cx};
-            visit::walk_stmt(visitor, stmt, expr_cx);
-        }
-        ast::StmtMac(..) => visitor.sess.bug("unexpanded macro")
-    }
+    let stmt_id = stmt_id(stmt);
+    debug!("resolve_stmt(stmt.id={})", stmt_id);
+
+    visitor.region_maps.mark_as_terminating_scope(stmt_id);
+    record_superlifetime(visitor, cx, stmt_id, stmt.span);
+
+    let subcx = Context {parent: Some(stmt_id), ..cx};
+    visit::walk_stmt(visitor, stmt, subcx);
 }
 
 fn resolve_expr(visitor: &mut RegionResolutionVisitor,
                 expr: &ast::Expr,
                 cx: Context) {
-    parent_to_expr(visitor, cx, expr.id, expr.span);
+    debug!("resolve_expr(expr.id={})", expr.id);
+
+    record_superlifetime(visitor, cx, expr.id, expr.span);
 
     let mut new_cx = cx;
     new_cx.parent = Some(expr.id);
     match expr.node {
-        ast::ExprAssignOp(..) | ast::ExprIndex(..) | ast::ExprBinary(..) |
+        // Conditional or repeating scopes are always terminating
+        // scopes, meaning that temporaries cannot outlive them.
+        // This ensures fixed size stacks.
+
+        ast::ExprBinary(_, ast::BiAnd, _, r) |
+        ast::ExprBinary(_, ast::BiOr, _, r) => {
+            // For shortcircuiting operators, mark the RHS as a terminating
+            // scope since it only executes conditionally.
+            visitor.region_maps.mark_as_terminating_scope(r.id);
+        }
+
+        ast::ExprIf(_, then, Some(otherwise)) => {
+            visitor.region_maps.mark_as_terminating_scope(then.id);
+            visitor.region_maps.mark_as_terminating_scope(otherwise.id);
+        }
+
+        ast::ExprIf(_, then, None) => {
+            visitor.region_maps.mark_as_terminating_scope(then.id);
+        }
+
+        ast::ExprLoop(body, _) |
+        ast::ExprWhile(_, body) => {
+            visitor.region_maps.mark_as_terminating_scope(body.id);
+        }
+
+        ast::ExprMatch(..) => {
+            new_cx.var_parent = Some(expr.id);
+        }
+
+        ast::ExprAssignOp(..) | ast::ExprIndex(..) |
         ast::ExprUnary(..) | ast::ExprCall(..) | ast::ExprMethodCall(..) => {
             // FIXME(#6268) Nested method calls
             //
@@ -402,11 +526,7 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor,
             // for an extended explanantion of why this distinction is
             // important.
             //
-            // parent_to_expr(new_cx, expr.callee_id);
-        }
-
-        ast::ExprMatch(..) => {
-            new_cx.var_parent = Some(expr.id);
+            // record_superlifetime(new_cx, expr.callee_id);
         }
 
         _ => {}
@@ -419,9 +539,256 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor,
 fn resolve_local(visitor: &mut RegionResolutionVisitor,
                  local: &ast::Local,
                  cx: Context) {
-    assert_eq!(cx.var_parent, cx.parent);
-    parent_to_expr(visitor, cx, local.id, local.span);
+    debug!("resolve_local(local.id={},local.init={})",
+           local.id,local.init.is_some());
+
+    let blk_id = match cx.var_parent {
+        Some(id) => id,
+        None => {
+            visitor.sess.span_bug(
+                local.span,
+                "Local without enclosing block");
+        }
+    };
+
+    // For convenience in trans, associate with the local-id the var
+    // scope that will be used for any bindings declared in this
+    // pattern.
+    visitor.region_maps.record_var_scope(local.id, blk_id);
+
+    // As an exception to the normal rules governing temporary
+    // lifetimes, initializers in a let have a temporary lifetime
+    // of the enclosing block. This means that e.g. a program
+    // like the following is legal:
+    //
+    //     let ref x = HashMap::new();
+    //
+    // Because the hash map will be freed in the enclosing block.
+    //
+    // We express the rules more formally based on 3 grammars (defined
+    // fully in the helpers below that implement them):
+    //
+    // 1. `E&`, which matches expressions like `&<rvalue>` that
+    //    own a pointer into the stack.
+    //
+    // 2. `P&`, which matches patterns like `ref x` or `(ref x, ref
+    //    y)` that produce ref bindings into the value they are
+    //    matched against or something (at least partially) owned by
+    //    the value they are matched against. (By partially owned,
+    //    I mean that creating a binding into a ref-counted or managed value
+    //    would still count.)
+    //
+    // 3. `ET`, which matches both rvalues like `foo()` as well as lvalues
+    //    based on rvalues like `foo().x[2].y`.
+    //
+    // A subexpression `<rvalue>` that appears in a let initializer
+    // `let pat [: ty] = expr` has an extended temporary lifetime if
+    // any of the following conditions are met:
+    //
+    // A. `pat` matches `P&` and `expr` matches `ET`
+    //    (covers cases where `pat` creates ref bindings into an rvalue
+    //     produced by `expr`)
+    // B. `ty` is a borrowed pointer and `expr` matches `ET`
+    //    (covers cases where coercion creates a borrow)
+    // C. `expr` matches `E&`
+    //    (covers cases `expr` borrows an rvalue that is then assigned
+    //     to memory (at least partially) owned by the binding)
+    //
+    // Here are some examples hopefully giving an intution where each
+    // rule comes into play and why:
+    //
+    // Rule A. `let (ref x, ref y) = (foo().x, 44)`. The rvalue `(22, 44)`
+    // would have an extended lifetime, but not `foo()`.
+    //
+    // Rule B. `let x: &[...] = [foo().x]`. The rvalue `[foo().x]`
+    // would have an extended lifetime, but not `foo()`.
+    //
+    // Rule C. `let x = &foo().x`. The rvalue ``foo()` would have extended
+    // lifetime.
+    //
+    // In some cases, multiple rules may apply (though not to the same
+    // rvalue). For example:
+    //
+    //     let ref x = [&a(), &b()];
+    //
+    // Here, the expression `[...]` has an extended lifetime due to rule
+    // A, but the inner rvalues `a()` and `b()` have an extended lifetime
+    // due to rule C.
+    //
+    // FIXME(#6308) -- Note that `[]` patterns work more smoothly post-DST.
+
+    match local.init {
+        Some(expr) => {
+            record_rvalue_scope_if_borrow_expr(visitor, expr, blk_id);
+
+            if is_binding_pat(local.pat) || is_borrowed_ty(local.ty) {
+                record_rvalue_scope(visitor, expr, blk_id);
+            }
+        }
+
+        None => { }
+    }
+
     visit::walk_local(visitor, local, cx);
+
+    fn is_binding_pat(pat: &ast::Pat) -> bool {
+        /*!
+         * True if `pat` match the `P&` nonterminal:
+         *
+         *     P& = ref X
+         *        | StructName { ..., P&, ... }
+         *        | VariantName(..., P&, ...)
+         *        | [ ..., P&, ... ]
+         *        | ( ..., P&, ... )
+         *        | ~P&
+         *        | box P&
+         */
+
+        match pat.node {
+            ast::PatIdent(ast::BindByRef(_), _, _) => true,
+
+            ast::PatStruct(_, ref field_pats, _) => {
+                field_pats.iter().any(|fp| is_binding_pat(fp.pat))
+            }
+
+            ast::PatVec(ref pats1, ref pats2, ref pats3) => {
+                pats1.iter().any(|&p| is_binding_pat(p)) ||
+                pats2.iter().any(|&p| is_binding_pat(p)) ||
+                pats3.iter().any(|&p| is_binding_pat(p))
+            }
+
+            ast::PatEnum(_, Some(ref subpats)) |
+            ast::PatTup(ref subpats) => {
+                subpats.iter().any(|&p| is_binding_pat(p))
+            }
+
+            ast::PatUniq(subpat) => {
+                is_binding_pat(subpat)
+            }
+
+            _ => false,
+        }
+    }
+
+    fn is_borrowed_ty(ty: &ast::Ty) -> bool {
+        /*!
+         * True if `ty` is a borrowed pointer type
+         * like `&int` or `&[...]`.
+         */
+
+        match ty.node {
+            ast::TyRptr(..) => true,
+            _ => false
+        }
+    }
+
+    fn record_rvalue_scope_if_borrow_expr(visitor: &mut RegionResolutionVisitor,
+                                          expr: &ast::Expr,
+                                          blk_id: ast::NodeId) {
+        /*!
+         * If `expr` matches the `E&` grammar, then records an extended
+         * rvalue scope as appropriate:
+         *
+         *     E& = & ET
+         *        | StructName { ..., f: E&, ... }
+         *        | [ ..., E&, ... ]
+         *        | ( ..., E&, ... )
+         *        | {...; E&}
+         *        | ~E&
+         *        | E& as ...
+         *        | ( E& )
+         */
+
+        match expr.node {
+            ast::ExprAddrOf(_, subexpr) => {
+                record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
+                record_rvalue_scope(visitor, subexpr, blk_id);
+            }
+            ast::ExprStruct(_, ref fields, _) => {
+                for field in fields.iter() {
+                    record_rvalue_scope_if_borrow_expr(
+                        visitor, field.expr, blk_id);
+                }
+            }
+            ast::ExprVstore(subexpr, _) => {
+                visitor.region_maps.record_rvalue_scope(subexpr.id, blk_id);
+                record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
+            }
+            ast::ExprVec(ref subexprs, _) |
+            ast::ExprTup(ref subexprs) => {
+                for &subexpr in subexprs.iter() {
+                    record_rvalue_scope_if_borrow_expr(
+                        visitor, subexpr, blk_id);
+                }
+            }
+            ast::ExprUnary(_, ast::UnUniq, subexpr) => {
+                record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
+            }
+            ast::ExprCast(subexpr, _) |
+            ast::ExprParen(subexpr) => {
+                record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id)
+            }
+            ast::ExprBlock(ref block) => {
+                match block.expr {
+                    Some(subexpr) => {
+                        record_rvalue_scope_if_borrow_expr(
+                            visitor, subexpr, blk_id);
+                    }
+                    None => { }
+                }
+            }
+            _ => {
+            }
+        }
+    }
+
+    fn record_rvalue_scope<'a>(visitor: &mut RegionResolutionVisitor,
+                               expr: &'a ast::Expr,
+                               blk_id: ast::NodeId) {
+        /*!
+         * Applied to an expression `expr` if `expr` -- or something
+         * owned or partially owned by `expr` -- is going to be
+         * indirectly referenced by a variable in a let statement. In
+         * that case, the "temporary lifetime" or `expr` is extended
+         * to be the block enclosing the `let` statement.
+         *
+         * More formally, if `expr` matches the grammar `ET`, record
+         * the rvalue scope of the matching `<rvalue>` as `blk_id`:
+         *
+         *     ET = *ET
+         *        | ET[...]
+         *        | ET.f
+         *        | (ET)
+         *        | <rvalue>
+         *
+         * Note: ET is intended to match "rvalues or
+         * lvalues based on rvalues".
+         */
+
+        let mut expr = expr;
+        loop {
+            // Note: give all the expressions matching `ET` with the
+            // extended temporary lifetime, not just the innermost rvalue,
+            // because in trans if we must compile e.g. `*rvalue()`
+            // into a temporary, we request the temporary scope of the
+            // outer expression.
+            visitor.region_maps.record_rvalue_scope(expr.id, blk_id);
+
+            match expr.node {
+                ast::ExprAddrOf(_, ref subexpr) |
+                ast::ExprUnary(_, ast::UnDeref, ref subexpr) |
+                ast::ExprField(ref subexpr, _, _) |
+                ast::ExprIndex(_, ref subexpr, _) |
+                ast::ExprParen(ref subexpr) => {
+                    let subexpr: &'a @Expr = subexpr; // FIXME(#11586)
+                    expr = &**subexpr;
+                }
+                _ => {
+                    return;
+                }
+            }
+        }
+    }
 }
 
 fn resolve_item(visitor: &mut RegionResolutionVisitor,
@@ -439,22 +806,23 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
               sp: Span,
               id: ast::NodeId,
               cx: Context) {
-    debug!("region::resolve_fn(id={:?}, \
+    debug!("region::resolve_fn(id={}, \
                                span={:?}, \
-                               body.id={:?}, \
-                               cx.parent={:?})",
+                               body.id={}, \
+                               cx.parent={})",
            id,
            visitor.sess.codemap.span_to_str(sp),
            body.id,
            cx.parent);
 
+    visitor.region_maps.mark_as_terminating_scope(body.id);
+
     // The arguments and `self` are parented to the body of the fn.
     let decl_cx = Context {parent: Some(body.id),
-                           var_parent: Some(body.id),
-                           ..cx};
+                           var_parent: Some(body.id)};
     match *fk {
         visit::FkMethod(_, _, method) => {
-            visitor.region_maps.record_parent(method.self_id, body.id);
+            visitor.region_maps.record_var_scope(method.self_id, body.id);
         }
         _ => {}
     }
@@ -471,7 +839,7 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
     visitor.visit_block(body, body_cx);
 }
 
-impl Visitor<Context> for RegionResolutionVisitor {
+impl<'a> Visitor<Context> for RegionResolutionVisitor<'a> {
 
     fn visit_block(&mut self, b: &Block, cx: Context) {
         resolve_block(self, b, cx);
@@ -503,16 +871,33 @@ impl Visitor<Context> for RegionResolutionVisitor {
 }
 
 pub fn resolve_crate(sess: Session, crate: &ast::Crate) -> RegionMaps {
+    let maps = RegionMaps {
+        scope_map: RefCell::new(HashMap::new()),
+        var_map: RefCell::new(HashMap::new()),
+        free_region_map: RefCell::new(HashMap::new()),
+        rvalue_scopes: RefCell::new(HashMap::new()),
+        terminating_scopes: RefCell::new(HashSet::new()),
+    };
+    {
+        let mut visitor = RegionResolutionVisitor {
+            sess: sess,
+            region_maps: &maps
+        };
+        let cx = Context { parent: None, var_parent: None };
+        visit::walk_crate(&mut visitor, crate, cx);
+    }
+    return maps;
+}
+
+pub fn resolve_inlined_item(sess: Session,
+                            region_maps: &RegionMaps,
+                            item: &ast::InlinedItem) {
+    let cx = Context {parent: None,
+                      var_parent: None};
     let mut visitor = RegionResolutionVisitor {
         sess: sess,
-        region_maps: RegionMaps {
-            scope_map: RefCell::new(HashMap::new()),
-            free_region_map: RefCell::new(HashMap::new()),
-            cleanup_scopes: RefCell::new(HashSet::new())
-        }
+        region_maps: region_maps,
     };
-    let cx = Context { parent: None, var_parent: None };
-    visit::walk_crate(&mut visitor, crate, cx);
-    return visitor.region_maps;
+    visit::walk_inlined_item(&mut visitor, item, cx);
 }
 
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 2d1c5b1b4e7..e42f4433653 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -2439,7 +2439,7 @@ impl Resolver {
         match type_result {
             BoundResult(target_module, name_bindings) => {
                 debug!("(resolving single import) found type target: {:?}",
-                        name_bindings.type_def.get().unwrap().type_def);
+                       {name_bindings.type_def.get().unwrap().type_def});
                 import_resolution.type_target.set(
                     Some(Target::new(target_module, name_bindings)));
                 import_resolution.type_id.set(directive.id);
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index a7924946ed1..96367c769fd 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -204,6 +204,8 @@ use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common::*;
 use middle::trans::consts;
 use middle::trans::controlflow;
@@ -221,7 +223,6 @@ use util::ppaux::{Repr, vec_map_to_str};
 
 use std::cell::Cell;
 use std::hashmap::HashMap;
-use std::ptr;
 use std::vec;
 use syntax::ast;
 use syntax::ast::Ident;
@@ -315,16 +316,18 @@ pub enum opt_result<'a> {
 fn trans_opt<'a>(bcx: &'a Block<'a>, o: &Opt) -> opt_result<'a> {
     let _icx = push_ctxt("match::trans_opt");
     let ccx = bcx.ccx();
-    let bcx = bcx;
+    let mut bcx = bcx;
     match *o {
         lit(ExprLit(lit_expr)) => {
-            let datumblock = expr::trans_to_datum(bcx, lit_expr);
-            return single_result(datumblock.to_result());
+            let lit_datum = unpack_datum!(bcx, expr::trans(bcx, lit_expr));
+            let lit_datum = lit_datum.assert_rvalue(bcx); // literals are rvalues
+            let lit_datum = unpack_datum!(bcx, lit_datum.to_appropriate_datum(bcx));
+            return single_result(rslt(bcx, lit_datum.val));
         }
         lit(UnitLikeStructLit(pat_id)) => {
             let struct_ty = ty::node_id_to_type(bcx.tcx(), pat_id);
-            let datumblock = datum::scratch_datum(bcx, struct_ty, "", true);
-            return single_result(datumblock.to_result(bcx));
+            let datum = datum::rvalue_scratch_datum(bcx, struct_ty, "");
+            return single_result(rslt(bcx, datum.val));
         }
         lit(ConstLit(lit_id)) => {
             let (llval, _) = consts::get_const_val(bcx.ccx(), lit_id);
@@ -1007,14 +1010,18 @@ fn extract_variant_args<'a>(
     ExtractedBlock { vals: args, bcx: bcx }
 }
 
-fn match_datum<'a>(bcx: &'a Block<'a>, val: ValueRef, pat_id: ast::NodeId)
-               -> Datum {
-    //! Helper for converting from the ValueRef that we pass around in
-    //! the match code, which is always by ref, into a Datum. Eventually
-    //! we should just pass around a Datum and be done with it.
+fn match_datum(bcx: &Block,
+               val: ValueRef,
+               pat_id: ast::NodeId)
+               -> Datum<Lvalue> {
+    /*!
+     * Helper for converting from the ValueRef that we pass around in
+     * the match code, which is always an lvalue, into a Datum. Eventually
+     * we should just pass around a Datum and be done with it.
+     */
 
     let ty = node_id_type(bcx, pat_id);
-    Datum {val: val, ty: ty, mode: datum::ByRef(RevokeClean)}
+    Datum(val, ty, Lvalue)
 }
 
 
@@ -1054,13 +1061,11 @@ fn extract_vec_elems<'a>(
             ty::mt {ty: vt.unit_ty, mutbl: ast::MutImmutable},
             ty::vstore_slice(ty::ReStatic)
         );
-        let scratch = scratch_datum(bcx, slice_ty, "", false);
+        let scratch = rvalue_scratch_datum(bcx, slice_ty, "");
         Store(bcx, slice_begin,
-            GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])
-        );
+              GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
         Store(bcx, slice_len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
         elems[n] = scratch.val;
-        scratch.add_clean(bcx);
     }
 
     ExtractedBlock { vals: elems, bcx: bcx }
@@ -1176,7 +1181,8 @@ impl<'a> DynamicFailureHandler<'a> {
             _ => (),
         }
 
-        let fail_cx = sub_block(self.bcx, "case_fallthrough");
+        let fcx = self.bcx.fcx;
+        let fail_cx = fcx.new_block(false, "case_fallthrough", None);
         controlflow::trans_fail(fail_cx, Some(self.sp), self.msg);
         self.finished.set(Some(fail_cx.llbb));
         fail_cx.llbb
@@ -1297,30 +1303,31 @@ fn compare_values<'a>(
 fn store_non_ref_bindings<'a>(
                           bcx: &'a Block<'a>,
                           bindings_map: &BindingsMap,
-                          mut opt_temp_cleanups: Option<&mut ~[ValueRef]>)
-                          -> &'a Block<'a> {
+                          opt_cleanup_scope: Option<cleanup::ScopeId>)
+                          -> &'a Block<'a>
+{
     /*!
-     *
-     * For each copy/move binding, copy the value from the value
-     * being matched into its final home.  This code executes once
-     * one of the patterns for a given arm has completely matched.
-     * It adds temporary cleanups to the `temp_cleanups` array,
-     * if one is provided.
+     * For each copy/move binding, copy the value from the value being
+     * matched into its final home.  This code executes once one of
+     * the patterns for a given arm has completely matched.  It adds
+     * cleanups to the `opt_cleanup_scope`, if one is provided.
      */
 
+    let fcx = bcx.fcx;
     let mut bcx = bcx;
     for (_, &binding_info) in bindings_map.iter() {
         match binding_info.trmode {
             TrByValue(lldest) => {
                 let llval = Load(bcx, binding_info.llmatch); // get a T*
-                let datum = Datum {val: llval, ty: binding_info.ty,
-                                   mode: ByRef(ZeroMem)};
-                bcx = datum.store_to(bcx, INIT, lldest);
-                opt_temp_cleanups.mutate(|temp_cleanups| {
-                    add_clean_temp_mem(bcx, lldest, binding_info.ty);
-                    temp_cleanups.push(lldest);
-                    temp_cleanups
-                });
+                let datum = Datum(llval, binding_info.ty, Lvalue);
+                bcx = datum.store_to(bcx, lldest);
+
+                match opt_cleanup_scope {
+                    None => {}
+                    Some(s) => {
+                        fcx.schedule_drop_mem(s, lldest, binding_info.ty);
+                    }
+                }
             }
             TrByRef => {}
         }
@@ -1328,38 +1335,29 @@ fn store_non_ref_bindings<'a>(
     return bcx;
 }
 
-fn insert_lllocals<'a>(
-                   bcx: &'a Block<'a>,
-                   bindings_map: &BindingsMap,
-                   add_cleans: bool)
-                   -> &'a Block<'a> {
+fn insert_lllocals<'a>(bcx: &'a Block<'a>,
+                       bindings_map: &BindingsMap,
+                       cleanup_scope: cleanup::ScopeId)
+                       -> &'a Block<'a> {
     /*!
      * For each binding in `data.bindings_map`, adds an appropriate entry into
-     * the `fcx.lllocals` map.  If add_cleans is true, then adds cleanups for
-     * the bindings.
+     * the `fcx.lllocals` map, scheduling cleanup in `cleanup_scope`.
      */
 
+    let fcx = bcx.fcx;
+
     for (&ident, &binding_info) in bindings_map.iter() {
         let llval = match binding_info.trmode {
             // By value bindings: use the stack slot that we
             // copied/moved the value into
             TrByValue(lldest) => lldest,
+
             // By ref binding: use the ptr into the matched value
             TrByRef => binding_info.llmatch
         };
 
-        let datum = Datum {
-            val: llval,
-            ty: binding_info.ty,
-            mode: ByRef(ZeroMem)
-        };
-
-        if add_cleans {
-            match binding_info.trmode {
-                TrByValue(_) => datum.add_clean(bcx),
-                _ => {}
-            }
-        }
+        let datum = Datum(llval, binding_info.ty, Lvalue);
+        fcx.schedule_drop_mem(cleanup_scope, llval, binding_info.ty);
 
         {
             debug!("binding {:?} to {}",
@@ -1396,24 +1394,23 @@ fn compile_guard<'r,
            vec_map_to_str(vals, |v| bcx.val_to_str(*v)));
     let _indenter = indenter();
 
+    // Lest the guard itself should fail, introduce a temporary cleanup
+    // scope for any non-ref bindings we create.
+    let temp_scope = bcx.fcx.push_custom_cleanup_scope();
+
     let mut bcx = bcx;
-    let mut temp_cleanups = ~[];
-    bcx = store_non_ref_bindings(bcx,
-                                 data.bindings_map,
-                                 Some(&mut temp_cleanups));
-    bcx = insert_lllocals(bcx, data.bindings_map, false);
-
-    let val = unpack_result!(bcx, {
-        with_scope_result(bcx, guard_expr.info(), "guard", |bcx| {
-            expr::trans_to_datum(bcx, guard_expr).to_result()
-        })
-    });
-    let val = bool_to_i1(bcx, val);
+    bcx = store_non_ref_bindings(bcx, data.bindings_map,
+                                 Some(cleanup::CustomScope(temp_scope)));
+    bcx = insert_lllocals(bcx, data.bindings_map,
+                          cleanup::CustomScope(temp_scope));
 
-    // Revoke the temp cleanups now that the guard successfully executed.
-    for llval in temp_cleanups.iter() {
-        revoke_clean(bcx, *llval);
-    }
+    let val = unpack_datum!(bcx, expr::trans(bcx, guard_expr));
+    let val = val.to_llbool(bcx);
+
+    // Cancel cleanups now that the guard successfully executed.  If
+    // the guard was false, we will drop the values explicitly
+    // below. Otherwise, we'll add lvalue cleanups at the end.
+    bcx.fcx.pop_custom_cleanup_scope(temp_scope);
 
     return with_cond(bcx, Not(bcx, val), |bcx| {
         // Guard does not match: free the values we copied,
@@ -1502,6 +1499,7 @@ fn compile_submatch_continue<'r,
                              chk: &FailureHandler,
                              col: uint,
                              val: ValueRef) {
+    let fcx = bcx.fcx;
     let tcx = bcx.tcx();
     let dm = tcx.def_map;
 
@@ -1602,6 +1600,7 @@ fn compile_submatch_continue<'r,
     debug!("options={:?}", opts);
     let mut kind = no_branch;
     let mut test_val = val;
+    debug!("test_val={}", bcx.val_to_str(test_val));
     if opts.len() > 0u {
         match opts[0] {
             var(_, repr) => {
@@ -1621,8 +1620,7 @@ fn compile_submatch_continue<'r,
             },
             vec_len(..) => {
                 let vt = tvec::vec_types(bcx, node_id_type(bcx, pat_id));
-                let unboxed = load_if_immediate(bcx, val, vt.vec_ty);
-                let (_, len) = tvec::get_base_and_len(bcx, unboxed, vt.vec_ty);
+                let (_, len) = tvec::get_base_and_len(bcx, val, vt.vec_ty);
                 test_val = len;
                 kind = compare_vec_len;
             }
@@ -1636,7 +1634,7 @@ fn compile_submatch_continue<'r,
     }
     let else_cx = match kind {
         no_branch | single => bcx,
-        _ => sub_block(bcx, "match_else")
+        _ => bcx.fcx.new_temp_block("match_else")
     };
     let sw = if kind == switch {
         Switch(bcx, test_val, else_cx.llbb, opts.len())
@@ -1657,7 +1655,7 @@ fn compile_submatch_continue<'r,
         let mut branch_chk = None;
         let mut opt_cx = else_cx;
         if !exhaustive || i+1 < len {
-            opt_cx = sub_block(bcx, "match_case");
+            opt_cx = bcx.fcx.new_temp_block("match_case");
             match kind {
               single => Br(bcx, opt_cx.llbb),
               switch => {
@@ -1678,75 +1676,65 @@ fn compile_submatch_continue<'r,
               compare => {
                   let t = node_id_type(bcx, pat_id);
                   let Result {bcx: after_cx, val: matches} = {
-                      with_scope_result(bcx, None, "compaReScope", |bcx| {
-                          match trans_opt(bcx, opt) {
-                              single_result(
-                                  Result {bcx, val}) => {
-                                  compare_values(bcx, test_val, val, t)
-                              }
-                              lower_bound(
-                                  Result {bcx, val}) => {
+                      match trans_opt(bcx, opt) {
+                          single_result(Result {bcx, val}) => {
+                              compare_values(bcx, test_val, val, t)
+                          }
+                          lower_bound(Result {bcx, val}) => {
+                              compare_scalar_types(
+                                  bcx, test_val, val,
+                                  t, ast::BiGe)
+                          }
+                          range_result(Result {val: vbegin, ..},
+                                       Result {bcx, val: vend}) => {
+                              let Result {bcx, val: llge} =
                                   compare_scalar_types(
-                                          bcx, test_val, val,
-                                          t, ast::BiGe)
-                              }
-                              range_result(
-                                  Result {val: vbegin, ..},
-                                  Result {bcx, val: vend}) => {
-                                  let Result {bcx, val: llge} =
-                                      compare_scalar_types(
-                                          bcx, test_val,
-                                          vbegin, t, ast::BiGe);
-                                  let Result {bcx, val: llle} =
-                                      compare_scalar_types(
-                                          bcx, test_val, vend,
-                                          t, ast::BiLe);
-                                  rslt(bcx, And(bcx, llge, llle))
-                              }
+                                  bcx, test_val,
+                                  vbegin, t, ast::BiGe);
+                              let Result {bcx, val: llle} =
+                                  compare_scalar_types(
+                                  bcx, test_val, vend,
+                                  t, ast::BiLe);
+                              rslt(bcx, And(bcx, llge, llle))
                           }
-                      })
+                      }
                   };
-                  bcx = sub_block(after_cx, "compare_next");
+                  bcx = fcx.new_temp_block("compare_next");
                   CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
               }
               compare_vec_len => {
                   let Result {bcx: after_cx, val: matches} = {
-                      with_scope_result(bcx,
-                                        None,
-                                        "compare_vec_len_scope",
-                                        |bcx| {
-                          match trans_opt(bcx, opt) {
-                              single_result(
-                                  Result {bcx, val}) => {
-                                  let value = compare_scalar_values(
-                                      bcx, test_val, val,
-                                      signed_int, ast::BiEq);
-                                  rslt(bcx, value)
-                              }
-                              lower_bound(
-                                  Result {bcx, val: val}) => {
-                                  let value = compare_scalar_values(
-                                      bcx, test_val, val,
-                                      signed_int, ast::BiGe);
-                                  rslt(bcx, value)
-                              }
-                              range_result(
-                                  Result {val: vbegin, ..},
-                                  Result {bcx, val: vend}) => {
-                                  let llge =
-                                      compare_scalar_values(
-                                          bcx, test_val,
-                                          vbegin, signed_int, ast::BiGe);
-                                  let llle =
-                                      compare_scalar_values(
-                                          bcx, test_val, vend,
-                                          signed_int, ast::BiLe);
-                                  rslt(bcx, And(bcx, llge, llle))
-                              }
+                      match trans_opt(bcx, opt) {
+                          single_result(
+                              Result {bcx, val}) => {
+                              let value = compare_scalar_values(
+                                  bcx, test_val, val,
+                                  signed_int, ast::BiEq);
+                              rslt(bcx, value)
                           }
-                      })
+                          lower_bound(
+                              Result {bcx, val: val}) => {
+                              let value = compare_scalar_values(
+                                  bcx, test_val, val,
+                                  signed_int, ast::BiGe);
+                              rslt(bcx, value)
+                          }
+                          range_result(
+                              Result {val: vbegin, ..},
+                              Result {bcx, val: vend}) => {
+                              let llge =
+                                  compare_scalar_values(
+                                  bcx, test_val,
+                                  vbegin, signed_int, ast::BiGe);
+                              let llle =
+                                  compare_scalar_values(
+                                  bcx, test_val, vend,
+                                  signed_int, ast::BiLe);
+                              rslt(bcx, And(bcx, llge, llle))
+                          }
+                      }
                   };
-                  bcx = sub_block(after_cx, "compare_vec_len_next");
+                  bcx = fcx.new_temp_block("compare_vec_len_next");
 
                   // If none of these subcases match, move on to the
                   // next condition.
@@ -1812,9 +1800,7 @@ pub fn trans_match<'a>(
                    dest: Dest)
                    -> &'a Block<'a> {
     let _icx = push_ctxt("match::trans_match");
-    with_scope(bcx, match_expr.info(), "match", |bcx| {
-        trans_match_inner(bcx, discr_expr, arms, dest)
-    })
+    trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest)
 }
 
 fn create_bindings_map(bcx: &Block, pat: @ast::Pat) -> BindingsMap {
@@ -1857,19 +1843,18 @@ fn create_bindings_map(bcx: &Block, pat: @ast::Pat) -> BindingsMap {
     return bindings_map;
 }
 
-fn trans_match_inner<'a>(
-                     scope_cx: &'a Block<'a>,
-                     discr_expr: &ast::Expr,
-                     arms: &[ast::Arm],
-                     dest: Dest)
-                     -> &'a Block<'a> {
+fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
+                         match_id: ast::NodeId,
+                         discr_expr: &ast::Expr,
+                         arms: &[ast::Arm],
+                         dest: Dest) -> &'a Block<'a> {
     let _icx = push_ctxt("match::trans_match_inner");
+    let fcx = scope_cx.fcx;
     let mut bcx = scope_cx;
     let tcx = bcx.tcx();
 
-    let discr_datum = unpack_datum!(bcx, {
-        expr::trans_to_datum(bcx, discr_expr)
-    });
+    let discr_datum = unpack_datum!(bcx, expr::trans_to_lvalue(bcx, discr_expr,
+                                                               "match"));
     if bcx.unreachable.get() {
         return bcx;
     }
@@ -1877,7 +1862,7 @@ fn trans_match_inner<'a>(
     let mut arm_datas = ~[];
     let mut matches = ~[];
     for arm in arms.iter() {
-        let body = scope_block(bcx, arm.body.info(), "case_body");
+        let body = fcx.new_id_block("case_body", arm.body.id);
         let bindings_map = create_bindings_map(bcx, arm.pats[0]);
         let arm_data = ArmData {
             bodycx: body,
@@ -1910,7 +1895,7 @@ fn trans_match_inner<'a>(
             Infallible
         }
     };
-    let lldiscr = discr_datum.to_ref_llval(bcx);
+    let lldiscr = discr_datum.val;
     compile_submatch(bcx, matches, [lldiscr], &chk);
 
     let mut arm_cxs = ~[];
@@ -1926,14 +1911,15 @@ fn trans_match_inner<'a>(
         }
 
         // insert bindings into the lllocals map and add cleanups
-        bcx = insert_lllocals(bcx, arm_data.bindings_map, true);
-
+        let cleanup_scope = fcx.push_custom_cleanup_scope();
+        bcx = insert_lllocals(bcx, arm_data.bindings_map,
+                              cleanup::CustomScope(cleanup_scope));
         bcx = controlflow::trans_block(bcx, arm_data.arm.body, dest);
-        bcx = trans_block_cleanups(bcx, block_cleanups(arm_data.bodycx));
+        bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
         arm_cxs.push(bcx);
     }
 
-    bcx = controlflow::join_blocks(scope_cx, arm_cxs);
+    bcx = scope_cx.fcx.join_blocks(match_id, arm_cxs);
     return bcx;
 }
 
@@ -1944,17 +1930,18 @@ enum IrrefutablePatternBindingMode {
     BindArgument
 }
 
-pub fn store_local<'a>(
-                   bcx: &'a Block<'a>,
-                   pat: @ast::Pat,
-                   opt_init_expr: Option<@ast::Expr>)
-                   -> &'a Block<'a> {
+pub fn store_local<'a>(bcx: &'a Block<'a>,
+                       local: &ast::Local)
+                       -> &'a Block<'a> {
     /*!
      * Generates code for a local variable declaration like
      * `let <pat>;` or `let <pat> = <opt_init_expr>`.
      */
     let _icx = push_ctxt("match::store_local");
     let mut bcx = bcx;
+    let tcx = bcx.tcx();
+    let pat = local.pat;
+    let opt_init_expr = local.init;
 
     return match opt_init_expr {
         Some(init_expr) => {
@@ -1970,9 +1957,11 @@ pub fn store_local<'a>(
             // it assumes it is matching against a valid value.
             match simple_identifier(pat) {
                 Some(path) => {
+                    let var_scope = cleanup::var_scope(tcx, local.id);
                     return mk_binding_alloca(
-                        bcx, pat.id, path, BindLocal,
-                        |bcx, datum| expr::trans_into(bcx, init_expr, expr::SaveIn(datum.val)));
+                        bcx, pat.id, path, BindLocal, var_scope, (),
+                        |(), bcx, v, _| expr::trans_into(bcx, init_expr,
+                                                         expr::SaveIn(v)));
                 }
 
                 None => {}
@@ -1980,17 +1969,15 @@ pub fn store_local<'a>(
 
             // General path.
             let init_datum =
-                unpack_datum!(
-                    bcx,
-                    expr::trans_to_datum(bcx, init_expr));
+                unpack_datum!(bcx, expr::trans_to_lvalue(bcx, init_expr, "let"));
             if ty::type_is_bot(expr_ty(bcx, init_expr)) {
                 create_dummy_locals(bcx, pat)
             } else {
                 if bcx.sess().asm_comments() {
                     add_comment(bcx, "creating zeroable ref llval");
                 }
-                let llptr = init_datum.to_ref_llval(bcx);
-                return bind_irrefutable_pat(bcx, pat, llptr, BindLocal);
+                let var_scope = cleanup::var_scope(tcx, local.id);
+                bind_irrefutable_pat(bcx, pat, init_datum.val, BindLocal, var_scope)
             }
         }
         None => {
@@ -1998,22 +1985,27 @@ pub fn store_local<'a>(
         }
     };
 
-    fn create_dummy_locals<'a>(mut bcx: &'a Block<'a>, pat: @ast::Pat)
-                           -> &'a Block<'a> {
+    fn create_dummy_locals<'a>(mut bcx: &'a Block<'a>,
+                               pat: @ast::Pat)
+                               -> &'a Block<'a> {
         // create dummy memory for the variables if we have no
         // value to store into them immediately
         let tcx = bcx.tcx();
         pat_bindings(tcx.def_map, pat, |_, p_id, _, path| {
-            bcx = mk_binding_alloca(
-                bcx, p_id, path, BindLocal,
-                |bcx, datum| { datum.cancel_clean(bcx); bcx });
-        });
+                let scope = cleanup::var_scope(tcx, p_id);
+                bcx = mk_binding_alloca(
+                    bcx, p_id, path, BindLocal, scope, (),
+                    |(), bcx, llval, ty| { zero_mem(bcx, llval, ty); bcx });
+            });
         bcx
     }
 }
 
-pub fn store_arg<'a>(mut bcx: &'a Block<'a>, pat: @ast::Pat, arg: Datum)
-                 -> &'a Block<'a> {
+pub fn store_arg<'a>(mut bcx: &'a Block<'a>,
+                     pat: @ast::Pat,
+                     arg: Datum<Rvalue>,
+                     arg_scope: cleanup::ScopeId)
+                     -> &'a Block<'a> {
     /*!
      * Generates code for argument patterns like `fn foo(<pat>: T)`.
      * Creates entries in the `llargs` map for each of the bindings
@@ -2026,62 +2018,56 @@ pub fn store_arg<'a>(mut bcx: &'a Block<'a>, pat: @ast::Pat, arg: Datum)
      *   if the argument type is `T`, then `llval` is a `T*`). In some
      *   cases, this code may zero out the memory `llval` points at.
      */
+
     let _icx = push_ctxt("match::store_arg");
 
-    // We always need to cleanup the argument as we exit the fn scope.
-    // Note that we cannot do it before for fear of a fn like
-    //    fn getaddr(~ref x: ~uint) -> *uint {....}
-    // (From test `run-pass/func-arg-ref-pattern.rs`)
-    arg.add_clean(bcx);
-
-    // Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an
-    // alloca, which only is the case on the general path, so lets disable the optimized path when
-    // debug info is enabled.
-    let arg_is_alloca = unsafe { llvm::LLVMIsAAllocaInst(arg.val) != ptr::null() };
-
-    let fast_path = (arg_is_alloca || !bcx.ccx().sess.opts.extra_debuginfo)
-                    && simple_identifier(pat).is_some();
-
-    if fast_path {
-        // Optimized path for `x: T` case. This just adopts
-        // `llval` wholesale as the pointer for `x`, avoiding the
-        // general logic which may copy out of `llval`.
-        let mut llargs = bcx.fcx.llargs.borrow_mut();
-        llargs.get().insert(pat.id, arg);
-    } else {
-        // General path. Copy out the values that are used in the
-        // pattern.
-        let llptr = arg.to_ref_llval(bcx);
-        bcx = bind_irrefutable_pat(bcx, pat, llptr, BindArgument);
-    }
+    match simple_identifier(pat) {
+        Some(path) => {
+            // Generate nicer LLVM for the common case of fn a pattern
+            // like `x: T`
+            mk_binding_alloca(
+                bcx, pat.id, path, BindArgument, arg_scope, arg,
+                |arg, bcx, llval, _| arg.store_to(bcx, llval))
+        }
 
-    return bcx;
+        None => {
+            // General path. Copy out the values that are used in the
+            // pattern.
+            let arg = unpack_datum!(
+                bcx, arg.to_lvalue_datum_in_scope(bcx, "__arg", arg_scope));
+            bind_irrefutable_pat(bcx, pat, arg.val,
+                                 BindArgument, arg_scope)
+        }
+    }
 }
 
-fn mk_binding_alloca<'a>(
-                     bcx: &'a Block<'a>,
-                     p_id: ast::NodeId,
-                     path: &ast::Path,
-                     binding_mode: IrrefutablePatternBindingMode,
-                     populate: |&'a Block<'a>, Datum| -> &'a Block<'a>)
-                     -> &'a Block<'a> {
+fn mk_binding_alloca<'a,A>(bcx: &'a Block<'a>,
+                           p_id: ast::NodeId,
+                           path: &ast::Path,
+                           binding_mode: IrrefutablePatternBindingMode,
+                           cleanup_scope: cleanup::ScopeId,
+                           arg: A,
+                           populate: |A, &'a Block<'a>, ValueRef, ty::t| -> &'a Block<'a>)
+                         -> &'a Block<'a> {
     let var_ty = node_id_type(bcx, p_id);
     let ident = ast_util::path_to_ident(path);
+
+    // Allocate memory on stack for the binding.
     let llval = alloc_ty(bcx, var_ty, bcx.ident(ident));
-    let datum = Datum {
-        val: llval,
-        ty: var_ty,
-        mode: ByRef(ZeroMem)
+
+    // Subtle: be sure that we *populate* the memory *before*
+    // we schedule the cleanup.
+    let bcx = populate(arg, bcx, llval, var_ty);
+    bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty);
+
+    // Now that memory is initialized and has cleanup scheduled,
+    // create the datum and insert into the local variable map.
+    let datum = Datum(llval, var_ty, Lvalue);
+    let mut llmap = match binding_mode {
+        BindLocal => bcx.fcx.lllocals.borrow_mut(),
+        BindArgument => bcx.fcx.llargs.borrow_mut()
     };
-    {
-        let mut llmap = match binding_mode {
-            BindLocal => bcx.fcx.lllocals.borrow_mut(),
-            BindArgument => bcx.fcx.llargs.borrow_mut()
-        };
-        llmap.get().insert(p_id, datum);
-    }
-    let bcx = populate(bcx, datum);
-    datum.add_clean(bcx);
+    llmap.get().insert(p_id, datum);
     bcx
 }
 
@@ -2089,7 +2075,8 @@ fn bind_irrefutable_pat<'a>(
                         bcx: &'a Block<'a>,
                         pat: @ast::Pat,
                         val: ValueRef,
-                        binding_mode: IrrefutablePatternBindingMode)
+                        binding_mode: IrrefutablePatternBindingMode,
+                        cleanup_scope: cleanup::ScopeId)
                         -> &'a Block<'a> {
     /*!
      * A simple version of the pattern matching code that only handles
@@ -2103,10 +2090,7 @@ fn bind_irrefutable_pat<'a>(
      * # Arguments
      * - bcx: starting basic block context
      * - pat: the irrefutable pattern being matched.
-     * - val: a pointer to the value being matched. If pat matches a value
-     *   of type T, then this is a T*. If the value is moved from `pat`,
-     *   then `*pat` will be zeroed; otherwise, it's existing cleanup
-     *   applies.
+     * - val: the value being matched -- must be an lvalue (by ref, with cleanup)
      * - binding_mode: is this for an argument or a local variable?
      */
 
@@ -2133,24 +2117,20 @@ fn bind_irrefutable_pat<'a>(
                 // binding will live and place it into the appropriate
                 // map.
                 bcx = mk_binding_alloca(
-                    bcx, pat.id, path, binding_mode,
-                    |bcx, var_datum| {
+                    bcx, pat.id, path, binding_mode, cleanup_scope, (),
+                    |(), bcx, llval, ty| {
                         match pat_binding_mode {
                             ast::BindByValue(_) => {
                                 // By value binding: move the value that `val`
                                 // points at into the binding's stack slot.
-                                let datum = Datum {
-                                    val: val,
-                                    ty: var_datum.ty,
-                                    mode: ByRef(ZeroMem)
-                                };
-                                datum.store_to(bcx, INIT, var_datum.val)
+                                let d = Datum(val, ty, Lvalue);
+                                d.store_to(bcx, llval)
                             }
 
                             ast::BindByRef(_) => {
                                 // By ref binding: the value of the variable
                                 // is the pointer `val` itself.
-                                Store(bcx, val, var_datum.val);
+                                Store(bcx, val, llval);
                                 bcx
                             }
                         }
@@ -2158,7 +2138,8 @@ fn bind_irrefutable_pat<'a>(
             }
 
             for &inner_pat in inner.iter() {
-                bcx = bind_irrefutable_pat(bcx, inner_pat, val, binding_mode);
+                bcx = bind_irrefutable_pat(bcx, inner_pat, val,
+                                           binding_mode, cleanup_scope);
             }
         }
         ast::PatEnum(_, ref sub_pats) => {
@@ -2176,7 +2157,8 @@ fn bind_irrefutable_pat<'a>(
                     for sub_pat in sub_pats.iter() {
                         for (i, argval) in args.vals.iter().enumerate() {
                             bcx = bind_irrefutable_pat(bcx, sub_pat[i],
-                                                       *argval, binding_mode);
+                                                       *argval, binding_mode,
+                                                       cleanup_scope);
                         }
                     }
                 }
@@ -2193,7 +2175,8 @@ fn bind_irrefutable_pat<'a>(
                                 let fldptr = adt::trans_field_ptr(bcx, repr,
                                                                   val, 0, i);
                                 bcx = bind_irrefutable_pat(bcx, *elem,
-                                                           fldptr, binding_mode);
+                                                           fldptr, binding_mode,
+                                                           cleanup_scope);
                             }
                         }
                     }
@@ -2214,7 +2197,8 @@ fn bind_irrefutable_pat<'a>(
                     let ix = ty::field_idx_strict(tcx, f.ident.name, field_tys);
                     let fldptr = adt::trans_field_ptr(bcx, pat_repr, val,
                                                       discr, ix);
-                    bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, binding_mode);
+                    bcx = bind_irrefutable_pat(bcx, f.pat, fldptr,
+                                               binding_mode, cleanup_scope);
                 }
             })
         }
@@ -2222,16 +2206,17 @@ fn bind_irrefutable_pat<'a>(
             let repr = adt::represent_node(bcx, pat.id);
             for (i, elem) in elems.iter().enumerate() {
                 let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i);
-                bcx = bind_irrefutable_pat(bcx, *elem, fldptr, binding_mode);
+                bcx = bind_irrefutable_pat(bcx, *elem, fldptr,
+                                           binding_mode, cleanup_scope);
             }
         }
         ast::PatUniq(inner) => {
             let llbox = Load(bcx, val);
-            bcx = bind_irrefutable_pat(bcx, inner, llbox, binding_mode);
+            bcx = bind_irrefutable_pat(bcx, inner, llbox, binding_mode, cleanup_scope);
         }
         ast::PatRegion(inner) => {
             let loaded_val = Load(bcx, val);
-            bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode);
+            bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode, cleanup_scope);
         }
         ast::PatVec(..) => {
             bcx.tcx().sess.span_bug(
@@ -2243,14 +2228,4 @@ fn bind_irrefutable_pat<'a>(
     return bcx;
 }
 
-fn simple_identifier<'a>(pat: &'a ast::Pat) -> Option<&'a ast::Path> {
-    match pat.node {
-        ast::PatIdent(ast::BindByValue(_), ref path, None) => {
-            Some(path)
-        }
-        _ => {
-            None
-        }
-    }
-}
 
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 25a69174bb2..91dcae2d1d3 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -629,6 +629,25 @@ pub fn num_args(r: &Repr, discr: Disr) -> uint {
 }
 
 /// Access a field, at a point when the value's case is known.
+pub fn deref_ty(ccx: &CrateContext, r: &Repr) -> ty::t {
+    match *r {
+        CEnum(..) => {
+            ccx.sess.bug("deref of c-like enum")
+        }
+        Univariant(ref st, _) => {
+            st.fields[0]
+        }
+        General(_, ref cases) => {
+            assert!(cases.len() == 1);
+            cases[0].fields[0]
+        }
+        NullablePointer{ .. } => {
+            ccx.sess.bug("deref of nullable ptr")
+        }
+    }
+}
+
+/// Access a field, at a point when the value's case is known.
 pub fn trans_field_ptr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr,
                        ix: uint) -> ValueRef {
     // Note: if this ever needs to generate conditionals (e.g., if we
diff --git a/src/librustc/middle/trans/asm.rs b/src/librustc/middle/trans/asm.rs
index 11f217f3cb4..bae35f68ada 100644
--- a/src/librustc/middle/trans/asm.rs
+++ b/src/librustc/middle/trans/asm.rs
@@ -18,8 +18,10 @@ use lib;
 use middle::trans::build::*;
 use middle::trans::callee;
 use middle::trans::common::*;
-use middle::trans::expr::*;
-use middle::trans::type_of::*;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
+use middle::trans::expr;
+use middle::trans::type_of;
 
 use middle::trans::type_::Type;
 
@@ -28,26 +30,23 @@ use syntax::ast;
 // Take an inline assembly expression and splat it out via LLVM
 pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm)
                         -> &'a Block<'a> {
+    let fcx = bcx.fcx;
     let mut bcx = bcx;
     let mut constraints = ~[];
-    let mut cleanups = ~[];
     let mut output_types = ~[];
 
+    let temp_scope = fcx.push_custom_cleanup_scope();
+
     // Prepare the output operands
     let outputs = ia.outputs.map(|&(c, out)| {
         constraints.push(c);
 
-        let out_datum = unpack_datum!(bcx, trans_to_datum(bcx, out));
-        output_types.push(type_of(bcx.ccx(), out_datum.ty));
+        let out_datum = unpack_datum!(bcx, expr::trans(bcx, out));
+        output_types.push(type_of::type_of(bcx.ccx(), out_datum.ty));
         out_datum.val
 
     });
 
-    for c in cleanups.iter() {
-        revoke_clean(bcx, *c);
-    }
-    cleanups.clear();
-
     // Now the input operands
     let inputs = ia.inputs.map(|&(c, input)| {
         constraints.push(c);
@@ -56,14 +55,13 @@ pub fn trans_inline_asm<'a>(bcx: &'a Block<'a>, ia: &ast::InlineAsm)
             callee::trans_arg_expr(bcx,
                                    expr_ty(bcx, input),
                                    input,
-                                   &mut cleanups,
+                                   cleanup::CustomScope(temp_scope),
                                    callee::DontAutorefArg)
         })
     });
 
-    for c in cleanups.iter() {
-        revoke_clean(bcx, *c);
-    }
+    // no failure occurred preparing operands, no need to cleanup
+    fcx.pop_custom_cleanup_scope(temp_scope);
 
     let mut constraints = constraints.connect(",");
 
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 46f647cc1a6..ef40629946d 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -37,17 +37,18 @@ use metadata::{csearch, encoder};
 use middle::astencode;
 use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
 use middle::lang_items::{MallocFnLangItem, ClosureExchangeMallocFnLangItem};
-use middle::lang_items::{EhPersonalityLangItem};
 use middle::trans::_match;
 use middle::trans::adt;
-use middle::trans::base;
 use middle::trans::build::*;
 use middle::trans::builder::{Builder, noname};
 use middle::trans::callee;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common::*;
 use middle::trans::consts;
 use middle::trans::controlflow;
 use middle::trans::datum;
+// use middle::trans::datum::{Datum, Lvalue, Rvalue, ByRef, ByValue};
 use middle::trans::debuginfo;
 use middle::trans::expr;
 use middle::trans::foreign;
@@ -75,15 +76,12 @@ use std::hashmap::HashMap;
 use std::libc::c_uint;
 use std::vec;
 use std::local_data;
-use syntax::ast::Name;
 use syntax::ast_map::{PathName, PathPrettyName, path_elem_to_str};
 use syntax::ast_util::{local_def, is_local};
 use syntax::attr;
 use syntax::codemap::Span;
 use syntax::parse::token;
-use syntax::parse::token::{special_idents};
-use syntax::print::pprust::stmt_to_str;
-use syntax::{ast, ast_util, codemap, ast_map};
+use syntax::{ast, ast_util, ast_map};
 use syntax::attr::AttrMetaMethods;
 use syntax::abi::{X86, X86_64, Arm, Mips, Rust, RustIntrinsic, OsWin32};
 use syntax::visit;
@@ -757,7 +755,8 @@ pub fn iter_structural_ty<'r,
           }
       }
       ty::ty_enum(tid, ref substs) => {
-          let ccx = cx.ccx();
+          let fcx = cx.fcx;
+          let ccx = fcx.ccx;
 
           let repr = adt::represent_type(ccx, t);
           let variants = ty::enum_variants(ccx.tcx, tid);
@@ -773,16 +772,16 @@ pub fn iter_structural_ty<'r,
               }
               (_match::switch, Some(lldiscrim_a)) => {
                   cx = f(cx, lldiscrim_a, ty::mk_int());
-                  let unr_cx = sub_block(cx, "enum-iter-unr");
+                  let unr_cx = fcx.new_temp_block("enum-iter-unr");
                   Unreachable(unr_cx);
                   let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb,
                                         n_variants);
-                  let next_cx = sub_block(cx, "enum-iter-next");
+                  let next_cx = fcx.new_temp_block("enum-iter-next");
 
                   for variant in (*variants).iter() {
                       let variant_cx =
-                          sub_block(cx, ~"enum-iter-variant-" +
-                                    variant.disr_val.to_str());
+                          fcx.new_temp_block(~"enum-iter-variant-" +
+                                             variant.disr_val.to_str());
                       let variant_cx =
                           iter_variant(variant_cx, repr, av, *variant,
                                        substs.tps, |x,y,z| f(x,y,z));
@@ -929,23 +928,27 @@ pub fn invoke<'a>(
         return (C_null(Type::i8()), bcx);
     }
 
-    match bcx.node_info {
-        None => debug!("invoke at ???"),
-        Some(node_info) => {
+    match bcx.opt_node_id {
+        None => {
+            debug!("invoke at ???");
+        }
+        Some(id) => {
             debug!("invoke at {}",
-                   bcx.sess().codemap.span_to_str(node_info.span));
+                   ast_map::node_id_to_str(bcx.tcx().items,
+                                           id,
+                                           token::get_ident_interner()));
         }
     }
 
-    if need_invoke(bcx) {
+    if bcx.fcx.needs_invoke() {
         unsafe {
             debug!("invoking {} at {}", llfn, bcx.llbb);
             for &llarg in llargs.iter() {
                 debug!("arg: {}", llarg);
             }
         }
-        let normal_bcx = sub_block(bcx, "normal return");
-        let landing_pad = get_landing_pad(bcx);
+        let normal_bcx = bcx.fcx.new_temp_block("normal-return");
+        let landing_pad = bcx.fcx.get_landing_pad();
 
         match call_info {
             Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span),
@@ -987,146 +990,9 @@ pub fn need_invoke(bcx: &Block) -> bool {
         return false;
     }
 
-    if have_cached_lpad(bcx) {
-        return true;
-    }
-
-    // Walk the scopes to look for cleanups
-    let mut cur = bcx;
-    let mut cur_scope = cur.scope.get();
-    loop {
-        cur_scope = match cur_scope {
-            Some(inf) => {
-                let cleanups = inf.cleanups.borrow();
-                for cleanup in cleanups.get().iter() {
-                    match *cleanup {
-                        Clean(_, cleanup_type) | CleanTemp(_, _, cleanup_type) => {
-                            if cleanup_type == normal_exit_and_unwind {
-                                return true;
-                            }
-                        }
-                    }
-                }
-                inf.parent
-            }
-            None => {
-                cur = match cur.parent {
-                    Some(next) => next,
-                    None => return false
-                };
-                cur.scope.get()
-            }
-        }
-    }
-}
-
-pub fn have_cached_lpad(bcx: &Block) -> bool {
-    let mut res = false;
-    in_lpad_scope_cx(bcx, |inf| {
-        match inf.landing_pad.get() {
-          Some(_) => res = true,
-          None => res = false
-        }
-    });
-    return res;
+    bcx.fcx.needs_invoke()
 }
 
-pub fn in_lpad_scope_cx<'a>(bcx: &'a Block<'a>, f: |si: &'a ScopeInfo<'a>|) {
-    let mut bcx = bcx;
-    let mut cur_scope = bcx.scope.get();
-    loop {
-        cur_scope = match cur_scope {
-            Some(inf) => {
-                if !inf.empty_cleanups() || (inf.parent.is_none() && bcx.parent.is_none()) {
-                    f(inf);
-                    return;
-                }
-                inf.parent
-            }
-            None => {
-                bcx = block_parent(bcx);
-                bcx.scope.get()
-            }
-        }
-    }
-}
-
-pub fn get_landing_pad<'a>(bcx: &'a Block<'a>) -> BasicBlockRef {
-    let _icx = push_ctxt("get_landing_pad");
-
-    let mut cached = None;
-    let mut pad_bcx = bcx; // Guaranteed to be set below
-    in_lpad_scope_cx(bcx, |inf| {
-        // If there is a valid landing pad still around, use it
-        match inf.landing_pad.get() {
-          Some(target) => cached = Some(target),
-          None => {
-            pad_bcx = lpad_block(bcx, "unwind");
-            inf.landing_pad.set(Some(pad_bcx.llbb));
-          }
-        }
-    });
-    // Can't return from block above
-    match cached { Some(b) => return b, None => () }
-    // The landing pad return type (the type being propagated). Not sure what
-    // this represents but it's determined by the personality function and
-    // this is what the EH proposal example uses.
-    let llretty = Type::struct_([Type::i8p(), Type::i32()], false);
-    // The exception handling personality function.
-    let personality = callee::trans_fn_ref(bcx,
-                                           langcall(bcx, None, "", EhPersonalityLangItem),
-                                           0).llfn;
-    // The only landing pad clause will be 'cleanup'
-    let llretval = LandingPad(pad_bcx, llretty, personality, 1u);
-    // The landing pad block is a cleanup
-    SetCleanup(pad_bcx, llretval);
-
-    // We store the retval in a function-central alloca, so that calls to
-    // Resume can find it.
-    match bcx.fcx.personality.get() {
-      Some(addr) => Store(pad_bcx, llretval, addr),
-      None => {
-        let addr = alloca(pad_bcx, val_ty(llretval), "");
-        bcx.fcx.personality.set(Some(addr));
-        Store(pad_bcx, llretval, addr);
-      }
-    }
-
-    // Unwind all parent scopes, and finish with a Resume instr
-    cleanup_and_leave(pad_bcx, None, None);
-    return pad_bcx.llbb;
-}
-
-pub fn find_bcx_for_scope<'a>(bcx: &'a Block<'a>, scope_id: ast::NodeId)
-                          -> &'a Block<'a> {
-    let mut bcx_sid = bcx;
-    let mut cur_scope = bcx_sid.scope.get();
-    loop {
-        cur_scope = match cur_scope {
-            Some(inf) => {
-                match inf.node_info {
-                    Some(NodeInfo { id, .. }) if id == scope_id => {
-                        return bcx_sid
-                    }
-                    // FIXME(#6268, #6248) hacky cleanup for nested method calls
-                    Some(NodeInfo { callee_id: Some(id), .. }) if id == scope_id => {
-                        return bcx_sid
-                    }
-                    _ => inf.parent
-                }
-            }
-            None => {
-                bcx_sid = match bcx_sid.parent {
-                    None => bcx.tcx().sess.bug(format!("no enclosing scope with id {}", scope_id)),
-                    Some(bcx_par) => bcx_par
-                };
-                bcx_sid.scope.get()
-            }
-        }
-    }
-}
-
-
 pub fn do_spill(bcx: &Block, v: ValueRef, t: ty::t) -> ValueRef {
     if ty::type_is_bot(t) {
         return C_null(Type::i8p());
@@ -1181,139 +1047,7 @@ pub fn init_local<'a>(bcx: &'a Block<'a>, local: &ast::Local)
         }
     }
 
-    _match::store_local(bcx, local.pat, local.init)
-}
-
-pub fn trans_stmt<'a>(cx: &'a Block<'a>, s: &ast::Stmt) -> &'a Block<'a> {
-    let _icx = push_ctxt("trans_stmt");
-    debug!("trans_stmt({})", stmt_to_str(s, cx.tcx().sess.intr()));
-
-    if cx.sess().asm_comments() {
-        add_span_comment(cx, s.span, stmt_to_str(s, cx.ccx().sess.intr()));
-    }
-
-    let mut bcx = cx;
-
-    match s.node {
-        ast::StmtExpr(e, _) | ast::StmtSemi(e, _) => {
-            bcx = expr::trans_into(cx, e, expr::Ignore);
-        }
-        ast::StmtDecl(d, _) => {
-            match d.node {
-                ast::DeclLocal(ref local) => {
-                    bcx = init_local(bcx, *local);
-                    if cx.sess().opts.extra_debuginfo {
-                        debuginfo::create_local_var_metadata(bcx, *local);
-                    }
-                }
-                ast::DeclItem(i) => trans_item(cx.fcx.ccx, i)
-            }
-        }
-        ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
-    }
-
-    return bcx;
-}
-
-// You probably don't want to use this one. See the
-// next three functions instead.
-pub fn new_block<'a>(
-                 cx: &'a FunctionContext<'a>,
-                 parent: Option<&'a Block<'a>>,
-                 scope: Option<&'a ScopeInfo<'a>>,
-                 is_lpad: bool,
-                 name: &str,
-                 opt_node_info: Option<NodeInfo>)
-                 -> &'a Block<'a> {
-    unsafe {
-        let llbb = name.with_c_str(|buf| {
-            llvm::LLVMAppendBasicBlockInContext(cx.ccx.llcx, cx.llfn, buf)
-        });
-        let bcx = Block::new(llbb, parent, is_lpad, opt_node_info, cx);
-        bcx.scope.set(scope);
-        for cx in parent.iter() {
-            if cx.unreachable.get() {
-                Unreachable(bcx);
-                break;
-            }
-        }
-        bcx
-    }
-}
-
-pub fn simple_block_scope<'a>(
-                          fcx: &'a FunctionContext<'a>,
-                          parent: Option<&'a ScopeInfo<'a>>,
-                          node_info: Option<NodeInfo>)
-                          -> &'a ScopeInfo<'a> {
-    fcx.scope_info_arena.alloc(ScopeInfo {
-        parent: parent,
-        loop_break: None,
-        loop_label: None,
-        cleanups: RefCell::new(~[]),
-        cleanup_paths: RefCell::new(~[]),
-        landing_pad: Cell::new(None),
-        node_info: node_info,
-    })
-}
-
-// Use this when you're at the top block of a function or the like.
-pub fn top_scope_block<'a>(
-                       fcx: &'a FunctionContext<'a>,
-                       opt_node_info: Option<NodeInfo>)
-                       -> &'a Block<'a> {
-    new_block(fcx,
-              None,
-              Some(simple_block_scope(fcx, None, opt_node_info)),
-              false,
-              "function top level",
-              opt_node_info)
-}
-
-pub fn scope_block<'a>(
-                   bcx: &'a Block<'a>,
-                   opt_node_info: Option<NodeInfo>,
-                   n: &str)
-                   -> &'a Block<'a> {
-    new_block(bcx.fcx,
-              Some(bcx),
-              Some(simple_block_scope(bcx.fcx, None, opt_node_info)),
-              bcx.is_lpad,
-              n,
-              opt_node_info)
-}
-
-pub fn loop_scope_block<'a>(
-                        bcx: &'a Block<'a>,
-                        loop_break: &'a Block<'a>,
-                        loop_label: Option<Name>,
-                        n: &str,
-                        opt_node_info: Option<NodeInfo>)
-                        -> &'a Block<'a> {
-    new_block(bcx.fcx,
-              Some(bcx),
-              Some(bcx.fcx.scope_info_arena.alloc(ScopeInfo {
-                parent: None,
-                loop_break: Some(loop_break),
-                loop_label: loop_label,
-                cleanups: RefCell::new(~[]),
-                cleanup_paths: RefCell::new(~[]),
-                landing_pad: Cell::new(None),
-                node_info: opt_node_info,
-              })),
-              bcx.is_lpad,
-              n,
-              opt_node_info)
-}
-
-// Use this when creating a block for the inside of a landing pad.
-pub fn lpad_block<'a>(bcx: &'a Block<'a>, n: &str) -> &'a Block<'a> {
-    new_block(bcx.fcx, Some(bcx), None, true, n, None)
-}
-
-// Use this when you're making a general CFG BB within a scope.
-pub fn sub_block<'a>(bcx: &'a Block<'a>, n: &str) -> &'a Block<'a> {
-    new_block(bcx.fcx, Some(bcx), None, bcx.is_lpad, n, None)
+    _match::store_local(bcx, local)
 }
 
 pub fn raw_block<'a>(
@@ -1321,236 +1055,7 @@ pub fn raw_block<'a>(
                  is_lpad: bool,
                  llbb: BasicBlockRef)
                  -> &'a Block<'a> {
-    Block::new(llbb, None, is_lpad, None, fcx)
-}
-
-
-// trans_block_cleanups: Go through all the cleanups attached to this
-// block and execute them.
-//
-// When translating a block that introduces new variables during its scope, we
-// need to make sure those variables go out of scope when the block ends.  We
-// do that by running a 'cleanup' function for each variable.
-// trans_block_cleanups runs all the cleanup functions for the block.
-pub fn trans_block_cleanups<'a>(bcx: &'a Block<'a>, cleanups: ~[cleanup])
-                            -> &'a Block<'a> {
-    trans_block_cleanups_(bcx, cleanups, false)
-}
-
-pub fn trans_block_cleanups_<'a>(
-                             bcx: &'a Block<'a>,
-                             cleanups: &[cleanup],
-                             is_lpad: bool)
-                             -> &'a Block<'a> {
-    let _icx = push_ctxt("trans_block_cleanups");
-    // 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 = bcx.ccx().sess.no_landing_pads();
-    if bcx.unreachable.get() && !no_lpads {
-        return bcx
-    }
-    let mut bcx = bcx;
-    for cu in cleanups.rev_iter() {
-        match *cu {
-            Clean(cfn, cleanup_type) | CleanTemp(_, cfn, cleanup_type) => {
-                // Some types don't need to be cleaned up during
-                // landing pads because they can be freed en mass later
-                if cleanup_type == normal_exit_and_unwind || !is_lpad {
-                    bcx = cfn.clean(bcx);
-                }
-            }
-        }
-    }
-    return bcx;
-}
-
-// In the last argument, Some(block) mean jump to this block, and none means
-// this is a landing pad and leaving should be accomplished with a resume
-// instruction.
-pub fn cleanup_and_leave<'a>(
-                         bcx: &'a Block<'a>,
-                         upto: Option<BasicBlockRef>,
-                         leave: Option<BasicBlockRef>) {
-    let _icx = push_ctxt("cleanup_and_leave");
-    let mut cur = bcx;
-    let mut bcx = bcx;
-    let is_lpad = leave == None;
-    loop {
-        debug!("cleanup_and_leave: leaving {}", cur.to_str());
-
-        let mut cur_scope = cur.scope.get();
-        loop {
-            cur_scope = match cur_scope {
-                Some (inf) if !inf.empty_cleanups() => {
-                    let (sub_cx, dest, inf_cleanups) = {
-                        let inf = &*inf;
-                        let mut skip = 0;
-                        let mut dest = None;
-                        {
-                            let cleanup_paths = inf.cleanup_paths.borrow();
-                            let r = cleanup_paths.get()
-                                                 .rev_iter()
-                                                 .find(|cp| {
-                                cp.target == leave
-                            });
-                            for cp in r.iter() {
-                                let cleanups = inf.cleanups.borrow();
-                                if cp.size == cleanups.get().len() {
-                                    Br(bcx, cp.dest);
-                                    return;
-                                }
-
-                                skip = cp.size;
-                                dest = Some(cp.dest);
-                            }
-                        }
-                        let sub_cx = sub_block(bcx, "cleanup");
-                        Br(bcx, sub_cx.llbb);
-                        let cleanups = inf.cleanups.borrow();
-                        let mut cleanup_paths = inf.cleanup_paths
-                                                   .borrow_mut();
-                        cleanup_paths.get().push(cleanup_path {
-                            target: leave,
-                            size: cleanups.get().len(),
-                            dest: sub_cx.llbb
-                        });
-                        (sub_cx, dest, cleanups.get().tailn(skip).to_owned())
-                    };
-                    bcx = trans_block_cleanups_(sub_cx,
-                                                inf_cleanups,
-                                                is_lpad);
-                    for &dest in dest.iter() {
-                        Br(bcx, dest);
-                        return;
-                    }
-                    inf.parent
-                }
-                Some(inf) => inf.parent,
-                None => break
-            }
-        }
-
-        match upto {
-          Some(bb) => { if cur.llbb == bb { break; } }
-          _ => ()
-        }
-        cur = match cur.parent {
-          Some(next) => next,
-          None => { assert!(upto.is_none()); break; }
-        };
-    }
-    match leave {
-      Some(target) => Br(bcx, target),
-      None => {
-          let ll_load = Load(bcx, bcx.fcx.personality.get().unwrap());
-          Resume(bcx, ll_load);
-      }
-    }
-}
-
-pub fn cleanup_block<'a>(bcx: &'a Block<'a>, upto: Option<BasicBlockRef>)
-                     -> &'a Block<'a> {
-    let _icx = push_ctxt("cleanup_block");
-    let mut cur = bcx;
-    let mut bcx = bcx;
-    loop {
-        debug!("cleanup_block: {}", cur.to_str());
-
-        let mut cur_scope = cur.scope.get();
-        loop {
-            cur_scope = match cur_scope {
-                Some(inf) => {
-                    let cleanups = inf.cleanups.borrow();
-                    bcx = trans_block_cleanups_(bcx,
-                                                cleanups.get().to_owned(),
-                                                false);
-                    inf.parent
-                }
-                None => break
-            }
-        }
-
-        match upto {
-          Some(bb) => { if cur.llbb == bb { break; } }
-          _ => ()
-        }
-        cur = match cur.parent {
-          Some(next) => next,
-          None => { assert!(upto.is_none()); break; }
-        };
-    }
-    bcx
-}
-
-pub fn cleanup_and_Br<'a>(
-                      bcx: &'a Block<'a>,
-                      upto: &'a Block<'a>,
-                      target: BasicBlockRef) {
-    let _icx = push_ctxt("cleanup_and_Br");
-    cleanup_and_leave(bcx, Some(upto.llbb), Some(target));
-}
-
-pub fn leave_block<'a>(bcx: &'a Block<'a>, out_of: &'a Block<'a>)
-                   -> &'a Block<'a> {
-    let _icx = push_ctxt("leave_block");
-    let next_cx = sub_block(block_parent(out_of), "next");
-    if bcx.unreachable.get() {
-        Unreachable(next_cx);
-    }
-    cleanup_and_Br(bcx, out_of, next_cx.llbb);
-    next_cx
-}
-
-pub fn with_scope<'a>(
-                  bcx: &'a Block<'a>,
-                  opt_node_info: Option<NodeInfo>,
-                  name: &str,
-                  f: |&'a Block<'a>| -> &'a Block<'a>)
-                  -> &'a Block<'a> {
-    let _icx = push_ctxt("with_scope");
-
-    debug!("with_scope(bcx={}, opt_node_info={:?}, name={})",
-           bcx.to_str(), opt_node_info, name);
-    let _indenter = indenter();
-
-    let scope = simple_block_scope(bcx.fcx, bcx.scope.get(), opt_node_info);
-    bcx.scope.set(Some(scope));
-    let ret = f(bcx);
-    let ret = trans_block_cleanups_(ret, scope.cleanups.get(), false);
-    bcx.scope.set(scope.parent);
-    ret
-}
-
-pub fn with_scope_result<'a>(
-                         bcx: &'a Block<'a>,
-                         opt_node_info: Option<NodeInfo>,
-                         _name: &str,
-                         f: |&'a Block<'a>| -> Result<'a>)
-                         -> Result<'a> {
-    let _icx = push_ctxt("with_scope_result");
-
-    let scope = simple_block_scope(bcx.fcx, bcx.scope.get(), opt_node_info);
-    bcx.scope.set(Some(scope));
-    let Result { bcx: out_bcx, val } = f(bcx);
-    let out_bcx = trans_block_cleanups_(out_bcx, scope.cleanups.get(), false);
-    bcx.scope.set(scope.parent);
-
-    rslt(out_bcx, val)
-}
-
-pub fn with_scope_datumblock<'a>(
-                             bcx: &'a Block<'a>,
-                             opt_node_info: Option<NodeInfo>,
-                             name: &str,
-                             f: |&'a Block| -> datum::DatumBlock<'a>)
-                             -> datum::DatumBlock<'a> {
-    use middle::trans::datum::DatumBlock;
-
-    let _icx = push_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}
+    Block::new(llbb, is_lpad, None, fcx)
 }
 
 pub fn block_locals(b: &ast::Block, it: |@ast::Local|) {
@@ -1573,8 +1078,9 @@ pub fn with_cond<'a>(
                  f: |&'a Block<'a>| -> &'a Block<'a>)
                  -> &'a Block<'a> {
     let _icx = push_ctxt("with_cond");
-    let next_cx = base::sub_block(bcx, "next");
-    let cond_cx = base::sub_block(bcx, "cond");
+    let fcx = bcx.fcx;
+    let next_cx = fcx.new_temp_block("next");
+    let cond_cx = fcx.new_temp_block("cond");
     CondBr(bcx, val, cond_cx.llbb, next_cx.llbb);
     let after_cx = f(cond_cx);
     if !after_cx.terminated.get() {
@@ -1725,24 +1231,25 @@ pub fn make_return_pointer(fcx: &FunctionContext, output_type: ty::t)
 // NB: must keep 4 fns in sync:
 //
 //  - type_of_fn
-//  - create_llargs_for_fn_args.
+//  - create_datums_for_fn_args.
 //  - new_fn_ctxt
 //  - trans_args
 //
 // Be warned! You must call `init_function` before doing anything with the
 // returned function context.
-pub fn new_fn_ctxt_w_id(ccx: @CrateContext,
-                        path: ast_map::Path,
-                        llfndecl: ValueRef,
-                        id: ast::NodeId,
-                        output_type: ty::t,
-                        param_substs: Option<@param_substs>,
-                        sp: Option<Span>)
-                        -> FunctionContext {
+pub fn new_fn_ctxt_detailed(ccx: @CrateContext,
+                            path: ast_map::Path,
+                            llfndecl: ValueRef,
+                            id: ast::NodeId,
+                            output_type: ty::t,
+                            param_substs: Option<@param_substs>,
+                            sp: Option<Span>)
+                            -> FunctionContext {
     for p in param_substs.iter() { p.validate(); }
 
-    debug!("new_fn_ctxt_w_id(path={}, id={:?}, \
-            param_substs={})",
+    debug!("new_fn_ctxt_detailed(path={},
+           id={:?}, \
+           param_substs={})",
            path_str(ccx.sess, path),
            id,
            param_substs.repr(ccx.tcx));
@@ -1776,9 +1283,9 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext,
           span: sp,
           path: path,
           block_arena: TypedArena::new(),
-          scope_info_arena: TypedArena::new(),
           ccx: ccx,
           debug_context: debug_context,
+          scopes: RefCell::new(~[])
     };
     fcx.llenv.set(unsafe {
           llvm::LLVMGetParam(llfndecl, fcx.env_arg_pos() as c_uint)
@@ -1793,10 +1300,9 @@ pub fn init_function<'a>(
                      fcx: &'a FunctionContext<'a>,
                      skip_retptr: bool,
                      output_type: ty::t,
-                     param_substs: Option<@param_substs>,
-                     opt_node_info: Option<NodeInfo>) {
+                     param_substs: Option<@param_substs>) {
     unsafe {
-        let entry_bcx = top_scope_block(fcx, opt_node_info);
+        let entry_bcx = fcx.new_temp_block("entry-block");
         Load(entry_bcx, C_null(Type::i8p()));
 
         fcx.entry_bcx.set(Some(entry_bcx));
@@ -1814,7 +1320,7 @@ pub fn init_function<'a>(
         }
     };
 
-    if !ty::type_is_voidish(fcx.ccx.tcx, substd_output_type) {
+    if !return_type_is_void(fcx.ccx, substd_output_type) {
         // If the function returns nil/bot, there is no real return
         // value, so do not set `llretptr`.
         if !skip_retptr || fcx.caller_expects_out_pointer {
@@ -1835,108 +1341,71 @@ pub fn new_fn_ctxt(ccx: @CrateContext,
                    -> FunctionContext {
     // FIXME(#11385): Do not call `init_function` here; it will typecheck
     // but segfault.
-    new_fn_ctxt_w_id(ccx, path, llfndecl, -1, output_type, None, sp)
+    new_fn_ctxt_detailed(ccx, path, llfndecl, -1, output_type, None, sp)
 }
 
 // NB: must keep 4 fns in sync:
 //
 //  - type_of_fn
-//  - create_llargs_for_fn_args.
+//  - create_datums_for_fn_args.
 //  - new_fn_ctxt
 //  - trans_args
 
-// create_llargs_for_fn_args: Creates a mapping from incoming arguments to
-// allocas created for them.
-//
-// When we translate a function, we need to map its incoming arguments to the
-// spaces that have been created for them (by code in the llallocas field of
-// the function's fn_ctxt).  create_llargs_for_fn_args populates the llargs
-// field of the fn_ctxt with
-fn create_llargs_for_fn_args(cx: &FunctionContext,
+fn arg_kind(cx: &FunctionContext, t: ty::t) -> datum::Rvalue {
+    use middle::trans::datum::{ByRef, ByValue};
+
+    datum::Rvalue {
+        mode: if arg_is_indirect(cx.ccx, t) { ByRef } else { ByValue }
+    }
+}
+
+// work around bizarre resolve errors
+type RvalueDatum = datum::Datum<datum::Rvalue>;
+type LvalueDatum = datum::Datum<datum::Lvalue>;
+
+// create_datums_for_fn_args: creates rvalue datums for `self` and each of the
+// incoming function arguments. These will later be stored into
+// appropriate lvalue datums.
+fn create_datums_for_fn_args(cx: &FunctionContext,
                              self_arg: Option<ty::t>,
                              arg_tys: &[ty::t])
-                             -> ~[datum::Datum] {
-    let _icx = push_ctxt("create_llargs_for_fn_args");
-
-    match self_arg {
-        Some(t) => {
-            cx.llself.set(Some(datum::Datum {
-                val: cx.llenv.get(),
-                ty: t,
-                mode: if arg_is_indirect(cx.ccx, t) {
-                    datum::ByRef(datum::ZeroMem)
-                } else {
-                    datum::ByValue
-                }
-            }));
-        }
-        None => {}
-    }
+                             -> (Option<RvalueDatum>, ~[RvalueDatum]) {
+    let _icx = push_ctxt("create_datums_for_fn_args");
+
+    let self_datum = self_arg.map(
+        |t| datum::Datum(cx.llenv.get(), t, arg_kind(cx, t)));
 
     // Return an array wrapping the ValueRefs that we get from
     // llvm::LLVMGetParam for each argument into datums.
-    arg_tys.iter().enumerate().map(|(i, &arg_ty)| {
-        let llarg = unsafe { llvm::LLVMGetParam(cx.llfn, cx.arg_pos(i) as c_uint) };
-        datum::Datum {
-            val: llarg,
-            ty: arg_ty,
-            mode: if arg_is_indirect(cx.ccx, arg_ty) {
-                datum::ByRef(datum::ZeroMem)
-            } else {
-                datum::ByValue
-            }
-        }
-    }).collect()
+    let arg_datums = arg_tys.iter().enumerate().map(|(i, &arg_ty)| {
+            let llarg = unsafe {
+                llvm::LLVMGetParam(cx.llfn, cx.arg_pos(i) as c_uint)
+            };
+            datum::Datum(llarg, arg_ty, arg_kind(cx, arg_ty))
+        }).collect();
+
+    (self_datum, arg_datums)
 }
 
 fn copy_args_to_allocas<'a>(fcx: &FunctionContext<'a>,
+                            arg_scope: cleanup::CustomScopeIndex,
                             bcx: &'a Block<'a>,
                             args: &[ast::Arg],
-                            method: Option<&ast::Method>,
-                            raw_llargs: &[datum::Datum])
+                            self_datum: Option<RvalueDatum>,
+                            arg_datums: ~[RvalueDatum])
                             -> &'a Block<'a> {
-    debug!("copy_args_to_allocas: args=[{}]",
-           raw_llargs.map(|d| d.to_str(fcx.ccx)).connect(", "));
+    debug!("copy_args_to_allocas");
 
     let _icx = push_ctxt("copy_args_to_allocas");
     let mut bcx = bcx;
 
-    match fcx.llself.get() {
-        Some(slf) => {
-            let needs_indirection = if slf.mode.is_by_value() {
-                // FIXME(eddyb) #11445 Always needs indirection because of cleanup.
-                if true {
-                    true
-                } else {
-                    match method {
-                        Some(method) => {
-                            match method.explicit_self.node {
-                                ast::SelfValue(ast::MutMutable) => true,
-                                _ => false
-                            }
-                        }
-                        None => true
-                    }
-                }
-            } else {
-                false
-            };
-            let slf = if needs_indirection {
-                // HACK(eddyb) this is just slf.to_ref_datum(bcx) with a named alloca.
-                let alloc = alloc_ty(bcx, slf.ty, "__self");
-                Store(bcx, slf.val, alloc);
-                datum::Datum {
-                    val: alloc,
-                    ty: slf.ty,
-                    mode: datum::ByRef(datum::ZeroMem)
-                }
-            } else {
-                slf
-            };
-
+    let arg_scope_id = cleanup::CustomScope(arg_scope);
+    match self_datum {
+        Some(slf_rv) => {
+            let slf = unpack_datum!(
+                bcx, slf_rv.to_lvalue_datum_in_scope(bcx, "__self",
+                                                     arg_scope_id));
             fcx.llself.set(Some(slf));
-            slf.add_clean(bcx);
-
             if fcx.ccx.sess.opts.extra_debuginfo {
                 debuginfo::create_self_argument_metadata(bcx, slf.ty, slf.val);
             }
@@ -1944,24 +1413,7 @@ fn copy_args_to_allocas<'a>(fcx: &FunctionContext<'a>,
         _ => {}
     }
 
-    for (i, &arg) in raw_llargs.iter().enumerate() {
-        let needs_indirection = if arg.mode.is_by_value() {
-            if fcx.ccx.sess.opts.extra_debuginfo {
-                true
-            } else {
-                // FIXME(eddyb) #11445 Always needs indirection because of cleanup.
-                if true {
-                    true
-                } else {
-                    match args[i].pat.node {
-                        ast::PatIdent(ast::BindByValue(ast::MutMutable), _, _) => true,
-                        _ => false
-                    }
-                }
-            }
-        } else {
-            false
-        };
+    for (i, arg_datum) in arg_datums.move_iter().enumerate() {
         // For certain mode/type combinations, the raw llarg values are passed
         // by value.  However, within the fn body itself, we want to always
         // have all locals and arguments be by-ref so that we can cancel the
@@ -1969,25 +1421,8 @@ fn copy_args_to_allocas<'a>(fcx: &FunctionContext<'a>,
         // the argument would be passed by value, we store it into an alloca.
         // This alloca should be optimized away by LLVM's mem-to-reg pass in
         // the event it's not truly needed.
-        let arg = if needs_indirection {
-            // HACK(eddyb) this is just arg.to_ref_datum(bcx) with a named alloca.
-            let alloc = match args[i].pat.node {
-                ast::PatIdent(_, ref path, _) => {
-                    let name = ast_util::path_to_ident(path).name;
-                    alloc_ty(bcx, arg.ty, token::interner_get(name))
-                }
-                _ => alloc_ty(bcx, arg.ty, "__arg")
-            };
-            Store(bcx, arg.val, alloc);
-            datum::Datum {
-                val: alloc,
-                ty: arg.ty,
-                mode: datum::ByRef(datum::ZeroMem)
-            }
-        } else {
-            arg
-        };
-        bcx = _match::store_arg(bcx, args[i].pat, arg);
+
+        bcx = _match::store_arg(bcx, args[i].pat, arg_datum, arg_scope_id);
 
         if fcx.ccx.sess.opts.extra_debuginfo {
             debuginfo::create_argument_metadata(bcx, &args[i]);
@@ -2056,7 +1491,6 @@ pub fn trans_closure(ccx: @CrateContext,
                      self_arg: Option<ty::t>,
                      param_substs: Option<@param_substs>,
                      id: ast::NodeId,
-                     method: Option<&ast::Method>,
                      _attributes: &[ast::Attribute],
                      output_type: ty::t,
                      maybe_load_env: |&FunctionContext|) {
@@ -2068,14 +1502,17 @@ pub fn trans_closure(ccx: @CrateContext,
     debug!("trans_closure(..., param_substs={})",
            param_substs.repr(ccx.tcx));
 
-    let fcx = new_fn_ctxt_w_id(ccx,
-                               path,
-                               llfndecl,
-                               id,
-                               output_type,
-                               param_substs,
-                               Some(body.span));
-    init_function(&fcx, false, output_type, param_substs, body.info());
+    let fcx = new_fn_ctxt_detailed(ccx,
+                                   path,
+                                   llfndecl,
+                                   id,
+                                   output_type,
+                                   param_substs,
+                                   Some(body.span));
+    init_function(&fcx, false, output_type, param_substs);
+
+    // cleanup scope for the incoming arguments
+    let arg_scope = fcx.push_custom_cleanup_scope();
 
     // Create the first basic block in the function and keep a handle on it to
     //  pass to finish_fn later.
@@ -2085,9 +1522,11 @@ pub fn trans_closure(ccx: @CrateContext,
 
     // Set up arguments to the function.
     let arg_tys = ty::ty_fn_args(node_id_type(bcx, id));
-    let raw_llargs = create_llargs_for_fn_args(&fcx, self_arg, arg_tys);
+    let (self_datum, arg_datums) =
+        create_datums_for_fn_args(&fcx, self_arg, arg_tys);
 
-    bcx = copy_args_to_allocas(&fcx, bcx, decl.inputs, method, raw_llargs);
+    bcx = copy_args_to_allocas(&fcx, arg_scope, bcx,
+                               decl.inputs, self_datum, arg_datums);
 
     maybe_load_env(&fcx);
 
@@ -2100,7 +1539,7 @@ pub fn trans_closure(ccx: @CrateContext,
     // translation calls that don't have a return value (trans_crate,
     // trans_mod, trans_item, et cetera) and those that do
     // (trans_block, trans_expr, et cetera).
-    if body.expr.is_none() || ty::type_is_voidish(bcx.tcx(), block_ty) {
+    if body.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
         bcx = controlflow::trans_block(bcx, body, expr::Ignore);
     } else {
         let dest = expr::SaveIn(fcx.llretptr.get().unwrap());
@@ -2108,8 +1547,15 @@ pub fn trans_closure(ccx: @CrateContext,
     }
 
     match fcx.llreturn.get() {
-        Some(llreturn) => cleanup_and_Br(bcx, bcx_top, llreturn),
-        None => bcx = cleanup_block(bcx, Some(bcx_top.llbb))
+        Some(_) => {
+            Br(bcx, fcx.return_exit_block());
+            fcx.pop_custom_cleanup_scope(arg_scope);
+        }
+        None => {
+            // Microoptimization writ large: avoid creating a separate
+            // llreturn basic block
+            bcx = fcx.pop_and_trans_custom_cleanup_scope(bcx, arg_scope);
+        }
     };
 
     // Put return block after all other blocks.
@@ -2135,9 +1581,7 @@ pub fn trans_fn(ccx: @CrateContext,
                 self_arg: Option<ty::t>,
                 param_substs: Option<@param_substs>,
                 id: ast::NodeId,
-                method: Option<&ast::Method>,
                 attrs: &[ast::Attribute]) {
-
     let the_path_str = path_str(ccx.sess, path);
     let _s = StatRecorder::new(ccx, the_path_str);
     debug!("trans_fn(self_arg={:?}, param_substs={})",
@@ -2153,44 +1597,15 @@ pub fn trans_fn(ccx: @CrateContext,
                   self_arg,
                   param_substs,
                   id,
-                  method,
                   attrs,
                   output_type,
                   |_fcx| { });
 }
 
-fn insert_synthetic_type_entries(bcx: &Block,
-                                 fn_args: &[ast::Arg],
-                                 arg_tys: &[ty::t]) {
-    /*!
-     * For tuple-like structs and enum-variants, we generate
-     * synthetic AST nodes for the arguments.  These have no types
-     * in the type table and no entries in the moves table,
-     * so the code in `copy_args_to_allocas` and `bind_irrefutable_pat`
-     * gets upset. This hack of a function bridges the gap by inserting types.
-     *
-     * This feels horrible. I think we should just have a special path
-     * for these functions and not try to use the generic code, but
-     * that's not the problem I'm trying to solve right now. - nmatsakis
-     */
-
-    let tcx = bcx.tcx();
-    for i in range(0u, fn_args.len()) {
-        debug!("setting type of argument {} (pat node {}) to {}",
-               i, fn_args[i].pat.id, bcx.ty_to_str(arg_tys[i]));
-
-        let pat_id = fn_args[i].pat.id;
-        let arg_ty = arg_tys[i];
-
-        let mut node_types = tcx.node_types.borrow_mut();
-        node_types.get().insert(pat_id as uint, arg_ty);
-    }
-}
-
 pub fn trans_enum_variant(ccx: @CrateContext,
                           _enum_id: ast::NodeId,
                           variant: &ast::Variant,
-                          args: &[ast::VariantArg],
+                          _args: &[ast::VariantArg],
                           disr: ty::Disr,
                           param_substs: Option<@param_substs>,
                           llfndecl: ValueRef) {
@@ -2199,14 +1614,13 @@ pub fn trans_enum_variant(ccx: @CrateContext,
     trans_enum_variant_or_tuple_like_struct(
         ccx,
         variant.node.id,
-        args,
         disr,
         param_substs,
         llfndecl);
 }
 
 pub fn trans_tuple_struct(ccx: @CrateContext,
-                          fields: &[ast::StructField],
+                          _fields: &[ast::StructField],
                           ctor_id: ast::NodeId,
                           param_substs: Option<@param_substs>,
                           llfndecl: ValueRef) {
@@ -2215,46 +1629,16 @@ pub fn trans_tuple_struct(ccx: @CrateContext,
     trans_enum_variant_or_tuple_like_struct(
         ccx,
         ctor_id,
-        fields,
         0,
         param_substs,
         llfndecl);
 }
 
-trait IdAndTy {
-    fn id(&self) -> ast::NodeId;
-    fn ty(&self) -> ast::P<ast::Ty>;
-}
-
-impl IdAndTy for ast::VariantArg {
-    fn id(&self) -> ast::NodeId { self.id }
-    fn ty(&self) -> ast::P<ast::Ty> { self.ty }
-}
-
-impl IdAndTy for ast::StructField {
-    fn id(&self) -> ast::NodeId { self.node.id }
-    fn ty(&self) -> ast::P<ast::Ty> { self.node.ty }
-}
-
-fn trans_enum_variant_or_tuple_like_struct<A:IdAndTy>(
-    ccx: @CrateContext,
-    ctor_id: ast::NodeId,
-    args: &[A],
-    disr: ty::Disr,
-    param_substs: Option<@param_substs>,
-    llfndecl: ValueRef) {
-    // Translate variant arguments to function arguments.
-    let fn_args = args.map(|varg| {
-        ast::Arg {
-            ty: varg.ty(),
-            pat: ast_util::ident_to_pat(
-                ccx.tcx.sess.next_node_id(),
-                codemap::DUMMY_SP,
-                special_idents::arg),
-            id: varg.id(),
-        }
-    });
-
+fn trans_enum_variant_or_tuple_like_struct(ccx: @CrateContext,
+                                           ctor_id: ast::NodeId,
+                                           disr: ty::Disr,
+                                           param_substs: Option<@param_substs>,
+                                           llfndecl: ValueRef) {
     let no_substs: &[ty::t] = [];
     let ty_param_substs = match param_substs {
         Some(ref substs) => {
@@ -2280,38 +1664,34 @@ fn trans_enum_variant_or_tuple_like_struct<A:IdAndTy>(
                  ty_to_str(ccx.tcx, ctor_ty)))
     };
 
-    let fcx = new_fn_ctxt_w_id(ccx,
-                               ~[],
-                               llfndecl,
-                               ctor_id,
-                               result_ty,
-                               param_substs,
-                               None);
-    init_function(&fcx, false, result_ty, param_substs, None);
+    let fcx = new_fn_ctxt_detailed(ccx,
+                                   ~[],
+                                   llfndecl,
+                                   ctor_id,
+                                   result_ty,
+                                   param_substs,
+                                   None);
+    init_function(&fcx, false, result_ty, param_substs);
 
     let arg_tys = ty::ty_fn_args(ctor_ty);
 
-    let raw_llargs = create_llargs_for_fn_args(&fcx, None, arg_tys);
+    let (_, arg_datums) = create_datums_for_fn_args(&fcx, None, arg_tys);
 
     let bcx = fcx.entry_bcx.get().unwrap();
 
-    insert_synthetic_type_entries(bcx, fn_args, arg_tys);
-    let bcx = copy_args_to_allocas(&fcx, bcx, fn_args, None, raw_llargs);
-
-    let repr = adt::represent_type(ccx, result_ty);
-    adt::trans_start_init(bcx, repr, fcx.llretptr.get().unwrap(), disr);
-    for (i, fn_arg) in fn_args.iter().enumerate() {
-        let lldestptr = adt::trans_field_ptr(bcx,
-                                             repr,
-                                             fcx.llretptr.get().unwrap(),
-                                             disr,
-                                             i);
-        let llarg = {
-            let llargs = fcx.llargs.borrow();
-            llargs.get().get_copy(&fn_arg.pat.id)
-        };
-        llarg.move_to(bcx, datum::INIT, lldestptr);
+    if !type_is_zero_size(fcx.ccx, result_ty) {
+        let repr = adt::represent_type(ccx, result_ty);
+        adt::trans_start_init(bcx, repr, fcx.llretptr.get().unwrap(), disr);
+        for (i, arg_datum) in arg_datums.move_iter().enumerate() {
+            let lldestptr = adt::trans_field_ptr(bcx,
+                                                 repr,
+                                                 fcx.llretptr.get().unwrap(),
+                                                 disr,
+                                                 i);
+            arg_datum.store_to(bcx, lldestptr);
+        }
     }
+
     finish_fn(&fcx, bcx);
 }
 
@@ -2380,7 +1760,6 @@ pub fn trans_item(ccx: @CrateContext, item: &ast::Item) {
                      None,
                      None,
                      item.id,
-                     None,
                      item.attrs);
         } else {
             // Be sure to travel more than just one layer deep to catch nested
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index 0dd0d1589ff..44410ae24b9 100644
--- a/src/librustc/middle/trans/build.rs
+++ b/src/librustc/middle/trans/build.rs
@@ -24,6 +24,7 @@ use std::cast;
 use std::libc::{c_uint, c_ulonglong, c_char};
 
 pub fn terminate(cx: &Block, _: &str) {
+    debug!("terminate({})", cx.to_str());
     cx.terminated.set(true);
 }
 
@@ -315,12 +316,16 @@ pub fn ArrayMalloc(cx: &Block, Ty: Type, Val: ValueRef) -> ValueRef {
 pub fn Alloca(cx: &Block, Ty: Type, name: &str) -> ValueRef {
     unsafe {
         if cx.unreachable.get() { return llvm::LLVMGetUndef(Ty.ptr_to().to_ref()); }
-        let b = cx.fcx.ccx.builder();
-        b.position_before(cx.fcx.alloca_insert_pt.get().unwrap());
-        b.alloca(Ty, name)
+        AllocaFcx(cx.fcx, Ty, name)
     }
 }
 
+pub fn AllocaFcx(fcx: &FunctionContext, Ty: Type, name: &str) -> ValueRef {
+    let b = fcx.ccx.builder();
+    b.position_before(fcx.alloca_insert_pt.get().unwrap());
+    b.alloca(Ty, name)
+}
+
 pub fn ArrayAlloca(cx: &Block, Ty: Type, Val: ValueRef) -> ValueRef {
     unsafe {
         if cx.unreachable.get() { return llvm::LLVMGetUndef(Ty.ptr_to().to_ref()); }
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index c1451a8fe1e..27903e74e07 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -27,6 +27,8 @@ use middle::trans::base;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common;
 use middle::trans::common::*;
 use middle::trans::datum::*;
@@ -60,11 +62,10 @@ pub struct FnData {
 pub struct MethodData {
     llfn: ValueRef,
     llself: ValueRef,
-    temp_cleanup: Option<ValueRef>
 }
 
 pub enum CalleeData {
-    Closure(Datum),
+    Closure(Datum<Lvalue>),
     Fn(FnData),
     Method(MethodData)
 }
@@ -74,7 +75,7 @@ pub struct Callee<'a> {
     data: CalleeData
 }
 
-pub fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> {
+fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> {
     let _icx = push_ctxt("trans_callee");
     debug!("callee::trans(expr={})", expr.repr(bcx.tcx()));
 
@@ -90,13 +91,15 @@ pub fn trans<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> {
     return datum_callee(bcx, expr);
 
     fn datum_callee<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> Callee<'a> {
-        let DatumBlock {bcx, datum} = expr::trans_to_datum(bcx, expr);
+        let DatumBlock {bcx: mut bcx, datum} = expr::trans(bcx, expr);
         match ty::get(datum.ty).sty {
             ty::ty_bare_fn(..) => {
-                let llval = datum.to_appropriate_llval(bcx);
+                let llval = datum.to_llscalarish(bcx);
                 return Callee {bcx: bcx, data: Fn(FnData {llfn: llval})};
             }
             ty::ty_closure(..) => {
+                let datum = unpack_datum!(
+                    bcx, datum.to_lvalue_datum(bcx, "callee", expr.id));
                 return Callee {bcx: bcx, data: Closure(datum)};
             }
             _ => {
@@ -458,10 +461,10 @@ pub fn trans_call<'a>(
                   -> &'a Block<'a> {
     let _icx = push_ctxt("trans_call");
     trans_call_inner(in_cx,
-                     call_ex.info(),
+                     Some(common::expr_info(call_ex)),
                      expr_ty(in_cx, f),
                      node_id_type(in_cx, id),
-                     |cx| trans(cx, f),
+                     |cx, _| trans(cx, f),
                      args,
                      Some(dest),
                      DontAutorefArg).bcx
@@ -481,10 +484,10 @@ pub fn trans_method_call<'a>(
            rcvr.repr(in_cx.tcx()));
     trans_call_inner(
         in_cx,
-        call_ex.info(),
+        Some(common::expr_info(call_ex)),
         node_id_type(in_cx, callee_id),
         expr_ty(in_cx, call_ex),
-        |cx| {
+        |cx, arg_cleanup_scope| {
             let origin_opt = {
                 let mut method_map = cx.ccx().maps.method_map.borrow_mut();
                 method_map.get().find_copy(&call_ex.id)
@@ -495,7 +498,11 @@ pub fn trans_method_call<'a>(
                            call_ex.repr(in_cx.tcx()),
                            origin.repr(in_cx.tcx()));
 
-                    meth::trans_method_callee(cx, callee_id, rcvr, origin)
+                    meth::trans_method_callee(cx,
+                                              callee_id,
+                                              rcvr,
+                                              origin,
+                                              arg_cleanup_scope)
                 }
                 None => {
                     cx.tcx().sess.span_bug(call_ex.span, "method call expr wasn't in method map")
@@ -523,7 +530,7 @@ pub fn trans_lang_call<'a>(
                              None,
                              fty,
                              rty,
-                             |bcx| {
+                             |bcx, _| {
                                 trans_fn_ref_with_vtables_to_callee(bcx,
                                                                     did,
                                                                     0,
@@ -551,8 +558,11 @@ pub fn trans_lang_call_with_type_params<'a>(
 
     let rty = ty::ty_fn_ret(fty);
     return callee::trans_call_inner(
-        bcx, None, fty, rty,
-        |bcx| {
+        bcx,
+        None,
+        fty,
+        rty,
+        |bcx, _| {
             let callee =
                 trans_fn_ref_with_vtables_to_callee(bcx, did, 0,
                                                     type_params,
@@ -577,11 +587,13 @@ pub fn trans_lang_call_with_type_params<'a>(
 }
 
 pub fn trans_call_inner<'a>(
-                        in_cx: &'a Block<'a>,
+                        bcx: &'a Block<'a>,
                         call_info: Option<NodeInfo>,
                         callee_ty: ty::t,
                         ret_ty: ty::t,
-                        get_callee: |&'a Block<'a>| -> Callee<'a>,
+                        get_callee: |bcx: &'a Block<'a>,
+                                     arg_cleanup_scope: cleanup::ScopeId|
+                                     -> Callee<'a>,
                         args: CallArgs,
                         dest: Option<expr::Dest>,
                         autoref_arg: AutorefArg)
@@ -593,171 +605,180 @@ pub fn trans_call_inner<'a>(
      * this into two functions seems like a good idea).
      *
      * In particular, for lang items, it is invoked with a dest of
-     * None, and
+     * None, and in that case the return value contains the result of
+     * the fn. The lang item must not return a structural type or else
+     * all heck breaks loose.
+     *
+     * For non-lang items, `dest` is always Some, and hence the result
+     * is written into memory somewhere. Nonetheless we return the
+     * actual return value of the function.
      */
 
-
-    base::with_scope_result(in_cx, call_info, "call", |cx| {
-        let callee = get_callee(cx);
-        let mut bcx = callee.bcx;
-        let ccx = cx.ccx();
-
-        let (llfn, llenv) = unsafe {
-            match callee.data {
-                Fn(d) => {
-                    (d.llfn, llvm::LLVMGetUndef(Type::opaque_box(ccx).ptr_to().to_ref()))
-                }
-                Method(d) => {
-                    // Weird but true: we pass self in the *environment* slot!
-                    (d.llfn, d.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)
-                }
+    // Introduce a temporary cleanup scope that will contain cleanups
+    // for the arguments while they are being evaluated. The purpose
+    // this cleanup is to ensure that, should a failure occur while
+    // evaluating argument N, the values for arguments 0...N-1 are all
+    // cleaned up. If no failure occurs, the values are handed off to
+    // the callee, and hence none of the cleanups in this temporary
+    // scope will ever execute.
+    let fcx = bcx.fcx;
+    let ccx = fcx.ccx;
+    let arg_cleanup_scope = fcx.push_custom_cleanup_scope();
+
+    let callee = get_callee(bcx, cleanup::CustomScope(arg_cleanup_scope));
+    let mut bcx = callee.bcx;
+
+    let (llfn, llenv) = unsafe {
+        match callee.data {
+            Fn(d) => {
+                (d.llfn, llvm::LLVMGetUndef(Type::opaque_box(ccx).ptr_to().to_ref()))
             }
-        };
-
-        let abi = match ty::get(callee_ty).sty {
-            ty::ty_bare_fn(ref f) => f.abis,
-            _ => AbiSet::Rust()
-        };
-        let is_rust_fn =
-            abi.is_rust() ||
-            abi.is_intrinsic();
-
-        // Generate a location to store the result. If the user does
-        // not care about the result, just make a stack slot.
-        let opt_llretslot = match dest {
-            None => {
-                assert!(!type_of::return_uses_outptr(in_cx.ccx(), ret_ty));
-                None
+            Method(d) => {
+                // Weird but true: we pass self in the *environment* slot!
+                (d.llfn, d.llself)
             }
-            Some(expr::SaveIn(dst)) => Some(dst),
-            Some(expr::Ignore) => {
-                if !ty::type_is_voidish(in_cx.tcx(), ret_ty) {
-                    Some(alloc_ty(bcx, ret_ty, "__llret"))
-                } else {
-                    unsafe {
-                        Some(llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref()))
-                    }
-                }
+            Closure(d) => {
+                // Closures are represented as (llfn, llclosure) pair:
+                // load the requisite values out.
+                let pair = d.to_llref();
+                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 mut llresult = unsafe {
-            llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())
-        };
+        }
+    };
 
-        // The code below invokes the function, using either the Rust
-        // conventions (if it is a rust fn) or the native conventions
-        // (otherwise).  The important part is that, when all is sad
-        // and done, either the return value of the function will have been
-        // written in opt_llretslot (if it is Some) or `llresult` will be
-        // set appropriately (otherwise).
-        if is_rust_fn {
-            let mut llargs = ~[];
-
-            // Push the out-pointer if we use an out-pointer for this
-            // return type, otherwise push "undef".
-            if type_of::return_uses_outptr(in_cx.ccx(), ret_ty) {
-                llargs.push(opt_llretslot.unwrap());
+    let abi = match ty::get(callee_ty).sty {
+        ty::ty_bare_fn(ref f) => f.abis,
+        _ => AbiSet::Rust()
+    };
+    let is_rust_fn =
+        abi.is_rust() ||
+        abi.is_intrinsic();
+
+    // Generate a location to store the result. If the user does
+    // not care about the result, just make a stack slot.
+    let opt_llretslot = match dest {
+        None => {
+            assert!(!type_of::return_uses_outptr(ccx, ret_ty));
+            None
+        }
+        Some(expr::SaveIn(dst)) => Some(dst),
+        Some(expr::Ignore) => {
+            if !type_is_zero_size(ccx, ret_ty) {
+                Some(alloc_ty(bcx, ret_ty, "__llret"))
+            } else {
+                let llty = type_of::type_of(ccx, ret_ty);
+                Some(C_undef(llty.ptr_to()))
             }
+        }
+    };
+
+    let mut llresult = unsafe {
+        llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())
+    };
 
-            // Push the environment.
-            llargs.push(llenv);
+    // The code below invokes the function, using either the Rust
+    // conventions (if it is a rust fn) or the native conventions
+    // (otherwise).  The important part is that, when all is sad
+    // and done, either the return value of the function will have been
+    // written in opt_llretslot (if it is Some) or `llresult` will be
+    // set appropriately (otherwise).
+    if is_rust_fn {
+        let mut llargs = ~[];
+
+        // Push the out-pointer if we use an out-pointer for this
+        // return type, otherwise push "undef".
+        if type_of::return_uses_outptr(ccx, ret_ty) {
+            llargs.push(opt_llretslot.unwrap());
+        }
 
-            // Push the arguments.
-            bcx = trans_args(bcx, args, callee_ty,
-                             autoref_arg, &mut llargs);
+        // Push the environment.
+        llargs.push(llenv);
 
-            // Now that the arguments have finished evaluating, we
-            // need to revoke the cleanup for the self argument
-            match callee.data {
-                Method(d) => {
-                    for &v in d.temp_cleanup.iter() {
-                        revoke_clean(bcx, v);
-                    }
-                }
-                _ => {}
-            }
+        // Push the arguments.
+        bcx = trans_args(bcx, args, callee_ty,
+                         autoref_arg, &mut llargs,
+                         cleanup::CustomScope(arg_cleanup_scope));
 
-            // A function pointer is called without the declaration available, so we have to apply
-            // any attributes with ABI implications directly to the call instruction. Right now, the
-            // only attribute we need to worry about is `sret`.
-            let mut attrs = ~[];
-            if type_of::return_uses_outptr(in_cx.ccx(), ret_ty) {
-                attrs.push((1, StructRetAttribute));
-            }
+        fcx.pop_custom_cleanup_scope(arg_cleanup_scope);
+
+        // A function pointer is called without the declaration
+        // available, so we have to apply any attributes with ABI
+        // implications directly to the call instruction. Right now,
+        // the only attribute we need to worry about is `sret`.
+        let mut attrs = ~[];
+        if type_of::return_uses_outptr(ccx, ret_ty) {
+            attrs.push((1, StructRetAttribute));
+        }
 
-            // The `noalias` attribute on the return value is useful to a function ptr caller.
-            match ty::get(ret_ty).sty {
-                // `~` pointer return values never alias because ownership is transferred
-                ty::ty_uniq(..) |
+        // The `noalias` attribute on the return value is useful to a
+        // function ptr caller.
+        match ty::get(ret_ty).sty {
+            // `~` pointer return values never alias because ownership
+            // is transferred
+            ty::ty_uniq(..) |
                 ty::ty_vec(_, ty::vstore_uniq) => {
-                    attrs.push((0, NoAliasAttribute));
-                }
-                _ => ()
+                attrs.push((0, NoAliasAttribute));
             }
+            _ => ()
+        }
 
-            // Invoke the actual rust fn and update bcx/llresult.
-            let (llret, b) = base::invoke(bcx, llfn, llargs, attrs, call_info);
-            bcx = b;
-            llresult = llret;
-
-            // If the Rust convention for this type is return via
-            // the return value, copy it into llretslot.
-            match opt_llretslot {
-                Some(llretslot) => {
-                    if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) &&
-                        !ty::type_is_voidish(bcx.tcx(), ret_ty)
-                    {
-                        Store(bcx, llret, llretslot);
-                    }
+        // Invoke the actual rust fn and update bcx/llresult.
+        let (llret, b) = base::invoke(bcx, llfn, llargs, attrs, call_info);
+        bcx = b;
+        llresult = llret;
+
+        // If the Rust convention for this type is return via
+        // the return value, copy it into llretslot.
+        match opt_llretslot {
+            Some(llretslot) => {
+                if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) &&
+                    !type_is_zero_size(bcx.ccx(), ret_ty)
+                {
+                    Store(bcx, llret, llretslot);
                 }
-                None => {}
             }
-        } else {
-            // Lang items are the only case where dest is None, and
-            // they are always Rust fns.
-            assert!(dest.is_some());
-
-            let mut llargs = ~[];
-            bcx = trans_args(bcx, args, callee_ty,
-                             autoref_arg, &mut llargs);
-            let arg_tys = match args {
-                ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(),
-                ArgVals(_) => fail!("expected arg exprs.")
-            };
-            bcx = foreign::trans_native_call(bcx, callee_ty,
-                                             llfn, opt_llretslot.unwrap(), llargs, arg_tys);
+            None => {}
         }
+    } else {
+        // Lang items are the only case where dest is None, and
+        // they are always Rust fns.
+        assert!(dest.is_some());
+
+        let mut llargs = ~[];
+        bcx = trans_args(bcx, args, callee_ty,
+                         autoref_arg, &mut llargs,
+                         cleanup::CustomScope(arg_cleanup_scope));
+        fcx.pop_custom_cleanup_scope(arg_cleanup_scope);
+        let arg_tys = match args {
+            ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(),
+            ArgVals(_) => fail!("expected arg exprs.")
+        };
+        bcx = foreign::trans_native_call(bcx, callee_ty,
+                                         llfn, opt_llretslot.unwrap(), llargs, arg_tys);
+    }
 
-        // If the caller doesn't care about the result of this fn call,
-        // drop the temporary slot we made.
-        match dest {
-            None => {
-                assert!(!type_of::return_uses_outptr(bcx.ccx(), ret_ty));
-            }
-            Some(expr::Ignore) => {
-                // drop the value if it is not being saved.
-                bcx = glue::drop_ty(bcx, opt_llretslot.unwrap(), ret_ty);
-            }
-            Some(expr::SaveIn(_)) => { }
+    // If the caller doesn't care about the result of this fn call,
+    // drop the temporary slot we made.
+    match dest {
+        None => {
+            assert!(!type_of::return_uses_outptr(bcx.ccx(), ret_ty));
         }
-
-        if ty::type_is_bot(ret_ty) {
-            Unreachable(bcx);
+        Some(expr::Ignore) => {
+            // drop the value if it is not being saved.
+            bcx = glue::drop_ty(bcx, opt_llretslot.unwrap(), ret_ty);
         }
+        Some(expr::SaveIn(_)) => { }
+    }
 
-        rslt(bcx, llresult)
-    })
+    if ty::type_is_bot(ret_ty) {
+        Unreachable(bcx);
+    }
+
+    rslt(bcx, llresult)
 }
 
 pub enum CallArgs<'a> {
@@ -770,10 +791,11 @@ pub fn trans_args<'a>(
                   args: CallArgs,
                   fn_ty: ty::t,
                   autoref_arg: AutorefArg,
-                  llargs: &mut ~[ValueRef])
-                  -> &'a Block<'a> {
+                  llargs: &mut ~[ValueRef],
+                  arg_cleanup_scope: cleanup::ScopeId)
+                  -> &'a Block<'a>
+{
     let _icx = push_ctxt("trans_args");
-    let mut temp_cleanups = ~[];
     let arg_tys = ty::ty_fn_args(fn_ty);
     let variadic = ty::fn_is_variadic(fn_ty);
 
@@ -796,7 +818,7 @@ pub fn trans_args<'a>(
                 trans_arg_expr(bcx,
                                arg_ty,
                                *arg_expr,
-                               &mut temp_cleanups,
+                               arg_cleanup_scope,
                                autoref_arg)
             });
             llargs.push(arg_val);
@@ -807,13 +829,6 @@ pub fn trans_args<'a>(
       }
     }
 
-    // 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).
-    for c in temp_cleanups.iter() {
-        revoke_clean(bcx, *c)
-    }
-
     bcx
 }
 
@@ -822,16 +837,15 @@ pub enum AutorefArg {
     DoAutorefArg
 }
 
-// temp_cleanups: cleanups that should run only if failure occurs before the
-// call takes place:
 pub fn trans_arg_expr<'a>(
                       bcx: &'a Block<'a>,
                       formal_arg_ty: ty::t,
                       arg_expr: &ast::Expr,
-                      temp_cleanups: &mut ~[ValueRef],
+                      arg_cleanup_scope: cleanup::ScopeId,
                       autoref_arg: AutorefArg)
                       -> Result<'a> {
     let _icx = push_ctxt("trans_arg_expr");
+    let mut bcx = bcx;
     let ccx = bcx.ccx();
 
     debug!("trans_arg_expr(formal_arg_ty=({}), arg_expr={})",
@@ -839,14 +853,13 @@ pub fn trans_arg_expr<'a>(
            arg_expr.repr(bcx.tcx()));
 
     // translate the arg expr to a datum
-    let arg_datumblock = expr::trans_to_datum(bcx, arg_expr);
-    let arg_datum = arg_datumblock.datum;
-    let bcx = arg_datumblock.bcx;
+    let arg_datum = unpack_datum!(bcx, expr::trans(bcx, arg_expr));
+    let arg_datum_ty = arg_datum.ty;
 
     debug!("   arg datum: {}", arg_datum.to_str(bcx.ccx()));
 
     let mut val;
-    if ty::type_is_bot(arg_datum.ty) {
+    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
@@ -859,34 +872,31 @@ pub fn trans_arg_expr<'a>(
         // FIXME(#3548) use the adjustments table
         match autoref_arg {
             DoAutorefArg => {
-                val = arg_datum.to_ref_llval(bcx);
+                // We will pass argument by reference
+                // We want an lvalue, so that we can pass by reference and
+                let arg_datum = unpack_datum!(
+                    bcx, arg_datum.to_lvalue_datum(bcx, "arg", arg_expr.id));
+                val = arg_datum.val;
             }
             DontAutorefArg => {
-                let need_scratch = ty::type_needs_drop(bcx.tcx(), arg_datum.ty) ||
-                    (bcx.expr_is_lval(arg_expr) &&
-                     arg_datum.appropriate_mode(bcx.ccx()).is_by_ref());
-
-                let arg_datum = if need_scratch {
-                    let scratch = scratch_datum(bcx, arg_datum.ty, "__self", false);
-                    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);
-                    temp_cleanups.push(scratch.val);
-
-                    scratch
-                } else {
-                    arg_datum
-                };
-
-                debug!("by copy arg with type {}", bcx.ty_to_str(arg_datum.ty));
-                val = arg_datum.to_appropriate_llval(bcx);
+                // Make this an rvalue, since we are going to be
+                // passing ownership.
+                let arg_datum = unpack_datum!(
+                    bcx, arg_datum.to_rvalue_datum(bcx, "arg"));
+
+                // Now that arg_datum is owned, get it into the appropriate
+                // mode (ref vs value).
+                let arg_datum = unpack_datum!(
+                    bcx, arg_datum.to_appropriate_datum(bcx));
+
+                // Technically, ownership of val passes to the callee.
+                // However, we must cleanup should we fail before the
+                // callee is actually invoked.
+                val = arg_datum.add_clean(bcx.fcx, arg_cleanup_scope);
             }
         }
 
-        if formal_arg_ty != arg_datum.ty {
+        if formal_arg_ty != arg_datum_ty {
             // this could happen due to e.g. subtyping
             let llformal_arg_ty = type_of::type_of_explicit_arg(ccx, formal_arg_ty);
             debug!("casting actual type ({}) to match formal ({})",
diff --git a/src/librustc/middle/trans/cleanup.rs b/src/librustc/middle/trans/cleanup.rs
new file mode 100644
index 00000000000..6fed396a78c
--- /dev/null
+++ b/src/librustc/middle/trans/cleanup.rs
@@ -0,0 +1,948 @@
+// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+ * Code pertaining to cleanup of temporaries as well as execution of
+ * drop glue. See discussion in `doc.rs` for a high-level summary.
+ */
+
+use lib::llvm::{BasicBlockRef, ValueRef};
+use middle::lang_items::{EhPersonalityLangItem};
+use middle::trans::base;
+use middle::trans::build;
+use middle::trans::callee;
+use middle::trans::common;
+use middle::trans::common::{Block, FunctionContext};
+use middle::trans::glue;
+use middle::trans::type_::Type;
+use middle::ty;
+use syntax::ast;
+use syntax::ast_map;
+use syntax::parse::token;
+use syntax::opt_vec;
+use syntax::opt_vec::OptVec;
+use util::ppaux::Repr;
+
+pub struct CleanupScope<'a> {
+    // The id of this cleanup scope. If the id is None,
+    // this is a *temporary scope* that is pushed during trans to
+    // cleanup miscellaneous garbage that trans may generate whose
+    // lifetime is a subset of some expression.  See module doc for
+    // more details.
+    kind: CleanupScopeKind<'a>,
+
+    // Cleanups to run upon scope exit.
+    cleanups: OptVec<~Cleanup>,
+
+    cached_early_exits: OptVec<CachedEarlyExit>,
+    cached_landing_pad: Option<BasicBlockRef>,
+}
+
+pub struct CustomScopeIndex {
+    priv index: uint
+}
+
+pub static EXIT_BREAK: uint = 0;
+pub static EXIT_LOOP: uint = 1;
+pub static EXIT_MAX: uint = 2;
+
+enum CleanupScopeKind<'a> {
+    CustomScopeKind,
+    AstScopeKind(ast::NodeId),
+    LoopScopeKind(ast::NodeId, [&'a Block<'a>, ..EXIT_MAX])
+}
+
+#[deriving(Eq)]
+enum EarlyExitLabel {
+    UnwindExit,
+    ReturnExit,
+    LoopExit(ast::NodeId, uint)
+}
+
+struct CachedEarlyExit {
+    label: EarlyExitLabel,
+    cleanup_block: BasicBlockRef,
+}
+
+pub trait Cleanup {
+    fn clean_on_unwind(&self) -> bool;
+    fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a>;
+}
+
+pub enum ScopeId {
+    AstScope(ast::NodeId),
+    CustomScope(CustomScopeIndex)
+}
+
+impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
+    fn push_ast_cleanup_scope(&self, id: ast::NodeId) {
+        /*!
+         * Invoked when we start to trans the code contained
+         * within a new cleanup scope.
+         */
+
+        debug!("push_ast_cleanup_scope({})",
+               ast_map::node_id_to_str(self.ccx.tcx.items, id,
+                                       token::get_ident_interner()));
+
+        // FIXME(#2202) -- currently closure bodies have a parent
+        // region, which messes up the assertion below, since there
+        // are no cleanup scopes on the stack at the start of
+        // trans'ing a closure body.  I think though that this should
+        // eventually be fixed by closure bodies not having a parent
+        // region, though that's a touch unclear, and it might also be
+        // better just to narrow this assertion more (i.e., by
+        // excluding id's that correspond to closure bodies only). For
+        // now we just say that if there is already an AST scope on the stack,
+        // this new AST scope had better be its immediate child.
+        let top_scope = self.top_ast_scope();
+        if top_scope.is_some() {
+            assert_eq!(self.ccx.tcx.region_maps.opt_encl_scope(id), top_scope);
+        }
+
+        self.push_scope(CleanupScope::new(AstScopeKind(id)));
+    }
+
+    fn push_loop_cleanup_scope(&self,
+                               id: ast::NodeId,
+                               exits: [&'a Block<'a>, ..EXIT_MAX]) {
+        debug!("push_loop_cleanup_scope({})",
+               ast_map::node_id_to_str(self.ccx.tcx.items, id,
+                                       token::get_ident_interner()));
+        assert_eq!(Some(id), self.top_ast_scope());
+
+        self.push_scope(CleanupScope::new(LoopScopeKind(id, exits)));
+    }
+
+    fn push_custom_cleanup_scope(&self) -> CustomScopeIndex {
+        let index = self.scopes_len();
+        debug!("push_custom_cleanup_scope(): {}", index);
+        self.push_scope(CleanupScope::new(CustomScopeKind));
+        CustomScopeIndex { index: index }
+    }
+
+    fn pop_and_trans_ast_cleanup_scope(&self,
+                                       bcx: &'a Block<'a>,
+                                       cleanup_scope: ast::NodeId)
+                                       -> &'a Block<'a> {
+        /*!
+         * Removes the cleanup scope for id `cleanup_scope`, which
+         * must be at the top of the cleanup stack, and generates the
+         * code to do its cleanups for normal exit.
+         */
+
+        debug!("pop_and_trans_ast_cleanup_scope({})",
+               ast_map::node_id_to_str(self.ccx.tcx.items, cleanup_scope,
+                                       token::get_ident_interner()));
+
+        assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope)));
+
+        let scope = self.pop_scope();
+        self.trans_scope_cleanups(bcx, &scope)
+
+    }
+
+    fn pop_loop_cleanup_scope(&self,
+                              cleanup_scope: ast::NodeId) {
+        /*!
+         * Removes the loop cleanup scope for id `cleanup_scope`, which
+         * must be at the top of the cleanup stack. Does not generate
+         * any cleanup code, since loop scopes should exit by
+         * branching to a block generated by `normal_exit_block`.
+         */
+
+        debug!("pop_loop_cleanup_scope({})",
+               ast_map::node_id_to_str(self.ccx.tcx.items, cleanup_scope,
+                                       token::get_ident_interner()));
+
+        assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope)));
+
+        let _ = self.pop_scope();
+    }
+
+    fn pop_custom_cleanup_scope(&self,
+                                custom_scope: CustomScopeIndex) {
+        /*!
+         * Removes the top cleanup scope from the stack without
+         * executing its cleanups. The top cleanup scope must
+         * be the temporary scope `custom_scope`.
+         */
+
+        debug!("pop_custom_cleanup_scope({})", custom_scope.index);
+        assert!(self.is_valid_to_pop_custom_scope(custom_scope));
+        let _ = self.pop_scope();
+    }
+
+    fn pop_and_trans_custom_cleanup_scope(&self,
+                                        bcx: &'a Block<'a>,
+                                        custom_scope: CustomScopeIndex)
+                                        -> &'a Block<'a> {
+        /*!
+         * Removes the top cleanup scope from the stack, which must be
+         * a temporary scope, and generates the code to do its
+         * cleanups for normal exit.
+         */
+
+        debug!("pop_and_trans_custom_cleanup_scope({:?})", custom_scope);
+        assert!(self.is_valid_to_pop_custom_scope(custom_scope));
+
+        let scope = self.pop_scope();
+        self.trans_scope_cleanups(bcx, &scope)
+    }
+
+    fn top_loop_scope(&self) -> ast::NodeId {
+        /*!
+         * Returns the id of the top-most loop scope
+         */
+
+        let scopes = self.scopes.borrow();
+        for scope in scopes.get().iter().invert() {
+            match scope.kind {
+                LoopScopeKind(id, _) => {
+                    return id;
+                }
+                _ => {}
+            }
+        }
+        self.ccx.tcx.sess.bug("No loop scope found");
+    }
+
+    fn normal_exit_block(&self,
+                         cleanup_scope: ast::NodeId,
+                         exit: uint) -> BasicBlockRef {
+        /*!
+         * Returns a block to branch to which will perform all pending
+         * cleanups and then break/continue (depending on `exit`) out
+         * of the loop with id `cleanup_scope`
+         */
+
+        self.trans_cleanups_to_exit_scope(LoopExit(cleanup_scope, exit))
+    }
+
+    fn return_exit_block(&self) -> BasicBlockRef {
+        /*!
+         * Returns a block to branch to which will perform all pending
+         * cleanups and then return from this function
+         */
+
+        self.trans_cleanups_to_exit_scope(ReturnExit)
+    }
+
+    fn schedule_drop_mem(&self,
+                         cleanup_scope: ScopeId,
+                         val: ValueRef,
+                         ty: ty::t) {
+        /*!
+         * Schedules a (deep) drop of `val`, which is a pointer to an
+         * instance of `ty`
+         */
+
+        if !ty::type_needs_drop(self.ccx.tcx, ty) { return; }
+        let drop = ~DropValue {
+            is_immediate: false,
+            on_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx, ty),
+            val: val,
+            ty: ty
+        };
+
+        debug!("schedule_drop_mem({:?}, val={}, ty={})",
+               cleanup_scope,
+               self.ccx.tn.val_to_str(val),
+               ty.repr(self.ccx.tcx));
+
+        self.schedule_clean(cleanup_scope, drop as ~Cleanup);
+    }
+
+    fn schedule_drop_immediate(&self,
+                               cleanup_scope: ScopeId,
+                               val: ValueRef,
+                               ty: ty::t) {
+        /*!
+         * Schedules a (deep) drop of `val`, which is an instance of `ty`
+         */
+
+        if !ty::type_needs_drop(self.ccx.tcx, ty) { return; }
+        let drop = ~DropValue {
+            is_immediate: true,
+            on_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx, ty),
+            val: val,
+            ty: ty
+        };
+
+        debug!("schedule_drop_immediate({:?}, val={}, ty={})",
+               cleanup_scope,
+               self.ccx.tn.val_to_str(val),
+               ty.repr(self.ccx.tcx));
+
+        self.schedule_clean(cleanup_scope, drop as ~Cleanup);
+    }
+
+    fn schedule_free_value(&self,
+                           cleanup_scope: ScopeId,
+                           val: ValueRef,
+                           heap: common::heap) {
+        /*!
+         * Schedules a call to `free(val)`. Note that this is a shallow
+         * operation.
+         */
+
+        let drop = ~FreeValue { ptr: val, heap: heap };
+
+        debug!("schedule_free_value({:?}, val={}, heap={:?})",
+               cleanup_scope,
+               self.ccx.tn.val_to_str(val),
+               heap);
+
+        self.schedule_clean(cleanup_scope, drop as ~Cleanup);
+    }
+
+    fn schedule_clean(&self,
+                      cleanup_scope: ScopeId,
+                      cleanup: ~Cleanup) {
+        match cleanup_scope {
+            AstScope(id) => self.schedule_clean_in_ast_scope(id, cleanup),
+            CustomScope(id) => self.schedule_clean_in_custom_scope(id, cleanup),
+        }
+    }
+
+    fn schedule_clean_in_ast_scope(&self,
+                                   cleanup_scope: ast::NodeId,
+                                   cleanup: ~Cleanup) {
+        /*!
+         * Schedules a cleanup to occur upon exit from `cleanup_scope`.
+         * If `cleanup_scope` is not provided, then the cleanup is scheduled
+         * in the topmost scope, which must be a temporary scope.
+         */
+
+        debug!("schedule_clean_in_ast_scope(cleanup_scope={:?})",
+               cleanup_scope);
+
+        let mut scopes = self.scopes.borrow_mut();
+        for scope in scopes.get().mut_iter().invert() {
+            if scope.kind.is_ast_with_id(cleanup_scope) {
+                scope.cleanups.push(cleanup);
+                scope.clear_cached_exits();
+                return;
+            } else {
+                // will be adding a cleanup to some enclosing scope
+                scope.clear_cached_exits();
+            }
+        }
+
+        self.ccx.tcx.sess.bug(
+            format!("No cleanup scope {} found",
+                    ast_map::node_id_to_str(self.ccx.tcx.items, cleanup_scope,
+                                            token::get_ident_interner())));
+    }
+
+    fn schedule_clean_in_custom_scope(&self,
+                                      custom_scope: CustomScopeIndex,
+                                      cleanup: ~Cleanup) {
+        /*!
+         * Schedules a cleanup to occur in the top-most scope,
+         * which must be a temporary scope.
+         */
+
+        debug!("schedule_clean_in_custom_scope(custom_scope={})",
+               custom_scope.index);
+
+        assert!(self.is_valid_custom_scope(custom_scope));
+
+        let mut scopes = self.scopes.borrow_mut();
+        let scope = &mut scopes.get()[custom_scope.index];
+        scope.cleanups.push(cleanup);
+        scope.clear_cached_exits();
+    }
+
+    fn needs_invoke(&self) -> bool {
+        /*!
+         * Returns true if there are pending cleanups that should
+         * execute on failure.
+         */
+
+        let scopes = self.scopes.borrow();
+        scopes.get().iter().invert().any(|s| s.needs_invoke())
+    }
+
+    fn get_landing_pad(&self) -> BasicBlockRef {
+        /*!
+         * Returns a basic block to branch to in the event of a failure.
+         * This block will run the failure cleanups and eventually
+         * invoke the LLVM `Resume` instruction.
+         */
+
+        let _icx = base::push_ctxt("get_landing_pad");
+
+        debug!("get_landing_pad");
+
+        let orig_scopes_len = self.scopes_len();
+        assert!(orig_scopes_len > 0);
+
+        // Remove any scopes that do not have cleanups on failure:
+        let mut popped_scopes = opt_vec::Empty;
+        while !self.top_scope(|s| s.needs_invoke()) {
+            debug!("top scope does not need invoke");
+            popped_scopes.push(self.pop_scope());
+        }
+
+        // Check for an existing landing pad in the new topmost scope:
+        let llbb = self.get_or_create_landing_pad();
+
+        // Push the scopes we removed back on:
+        while !popped_scopes.is_empty() {
+            self.push_scope(popped_scopes.pop());
+        }
+
+        assert_eq!(self.scopes_len(), orig_scopes_len);
+
+        return llbb;
+    }
+}
+
+impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> {
+    fn top_ast_scope(&self) -> Option<ast::NodeId> {
+        /*!
+         * Returns the id of the current top-most AST scope, if any.
+         */
+        let scopes = self.scopes.borrow();
+        for scope in scopes.get().iter().invert() {
+            match scope.kind {
+                CustomScopeKind | LoopScopeKind(..) => {}
+                AstScopeKind(i) => {
+                    return Some(i);
+                }
+            }
+        }
+        None
+    }
+
+    fn top_nonempty_cleanup_scope(&self) -> Option<uint> {
+        let scopes = self.scopes.borrow();
+        scopes.get().iter().invert().position(|s| !s.cleanups.is_empty())
+    }
+
+    fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool {
+        let scopes = self.scopes.borrow();
+        self.is_valid_custom_scope(custom_scope) &&
+            custom_scope.index == scopes.get().len() - 1
+    }
+
+    fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool {
+        let scopes = self.scopes.borrow();
+        custom_scope.index < scopes.get().len() &&
+            scopes.get()[custom_scope.index].kind.is_temp()
+    }
+
+    fn trans_scope_cleanups(&self, // cannot borrow self, will recurse
+                            bcx: &'a Block<'a>,
+                            scope: &CleanupScope) -> &'a Block<'a> {
+        /*! Generates the cleanups for `scope` into `bcx` */
+
+        let mut bcx = bcx;
+        if !bcx.unreachable.get() {
+            for cleanup in scope.cleanups.iter().invert() {
+                bcx = cleanup.trans(bcx);
+            }
+        }
+        bcx
+    }
+
+    fn scopes_len(&self) -> uint {
+        let scopes = self.scopes.borrow();
+        scopes.get().len()
+    }
+
+    fn push_scope(&self, scope: CleanupScope<'a>) {
+        let mut scopes = self.scopes.borrow_mut();
+        scopes.get().push(scope);
+    }
+
+    fn pop_scope(&self) -> CleanupScope<'a> {
+        debug!("popping cleanup scope {}, {} scopes remaining",
+               self.top_scope(|s| s.block_name("")),
+               self.scopes_len() - 1);
+
+        let mut scopes = self.scopes.borrow_mut();
+        scopes.get().pop()
+    }
+
+    fn top_scope<R>(&self, f: |&CleanupScope<'a>| -> R) -> R {
+        let scopes = self.scopes.borrow();
+        f(scopes.get().last())
+    }
+
+    fn trans_cleanups_to_exit_scope(&self,
+                                    label: EarlyExitLabel)
+                                    -> BasicBlockRef {
+        /*!
+         * Used when the caller wishes to jump to an early exit, such
+         * as a return, break, continue, or unwind. This function will
+         * generate all cleanups between the top of the stack and the
+         * exit `label` and return a basic block that the caller can
+         * branch to.
+         *
+         * For example, if the current stack of cleanups were as follows:
+         *
+         *      AST 22
+         *      Custom 1
+         *      AST 23
+         *      Loop 23
+         *      Custom 2
+         *      AST 24
+         *
+         * and the `label` specifies a break from `Loop 23`, then this
+         * function would generate a series of basic blocks as follows:
+         *
+         *      Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk
+         *
+         * where `break_blk` is the block specified in `Loop 23` as
+         * the target for breaks. The return value would be the first
+         * basic block in that sequence (`Cleanup(AST 24)`). The
+         * caller could then branch to `Cleanup(AST 24)` and it will
+         * perform all cleanups and finally branch to the `break_blk`.
+         */
+
+        debug!("trans_cleanups_to_exit_scope label={:?} scopes={}",
+               label, self.scopes_len());
+
+        let orig_scopes_len = self.scopes_len();
+        let mut prev_llbb;
+        let mut popped_scopes = opt_vec::Empty;
+
+        // First we pop off all the cleanup stacks that are
+        // traversed until the exit is reached, pushing them
+        // onto the side vector `popped_scopes`. No code is
+        // generated at this time.
+        //
+        // So, continuing the example from above, we would wind up
+        // with a `popped_scopes` vector of `[AST 24, Custom 2]`.
+        // (Presuming that there are no cached exits)
+        loop {
+            if self.scopes_len() == 0 {
+                match label {
+                    UnwindExit => {
+                        // Generate a block that will `Resume`.
+                        let prev_bcx = self.new_block(true, "resume", None);
+                        let personality = self.personality.get().expect(
+                            "create_landing_pad() should have set this");
+                        build::Resume(prev_bcx,
+                                      build::Load(prev_bcx, personality));
+                        prev_llbb = prev_bcx.llbb;
+                        break;
+                    }
+
+                    ReturnExit => {
+                        prev_llbb = self.get_llreturn();
+                        break;
+                    }
+
+                    LoopExit(id, _) => {
+                        self.ccx.tcx.sess.bug(format!(
+                                "Cannot exit from scope {:?}, \
+                                not in scope", id));
+                    }
+                }
+            }
+
+            // Check if we have already cached the unwinding of this
+            // scope for this label. If so, we can stop popping scopes
+            // and branch to the cached label, since it contains the
+            // cleanups for any subsequent scopes.
+            match self.top_scope(|s| s.cached_early_exit(label)) {
+                Some(cleanup_block) => {
+                    prev_llbb = cleanup_block;
+                    break;
+                }
+                None => { }
+            }
+
+            // Pop off the scope, since we will be generating
+            // unwinding code for it. If we are searching for a loop exit,
+            // and this scope is that loop, then stop popping and set
+            // `prev_llbb` to the appropriate exit block from the loop.
+            popped_scopes.push(self.pop_scope());
+            let scope = popped_scopes.last();
+            match label {
+                UnwindExit | ReturnExit => { }
+                LoopExit(id, exit) => {
+                    match scope.kind.early_exit_block(id, exit) {
+                        Some(exitllbb) => {
+                            prev_llbb = exitllbb;
+                            break;
+                        }
+
+                        None => { }
+                    }
+                }
+            }
+        }
+
+        debug!("trans_cleanups_to_exit_scope: popped {} scopes",
+               popped_scopes.len());
+
+        // Now push the popped scopes back on. As we go,
+        // we track in `prev_llbb` the exit to which this scope
+        // should branch when it's done.
+        //
+        // So, continuing with our example, we will start out with
+        // `prev_llbb` being set to `break_blk` (or possibly a cached
+        // early exit). We will then pop the scopes from `popped_scopes`
+        // and generate a basic block for each one, prepending it in the
+        // series and updating `prev_llbb`. So we begin by popping `Custom 2`
+        // and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)`
+        // branch to `prev_llbb == break_blk`, giving us a sequence like:
+        //
+        //     Cleanup(Custom 2) -> prev_llbb
+        //
+        // We then pop `AST 24` and repeat the process, giving us the sequence:
+        //
+        //     Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb
+        //
+        // At this point, `popped_scopes` is empty, and so the final block
+        // that we return to the user is `Cleanup(AST 24)`.
+        while !popped_scopes.is_empty() {
+            let mut scope = popped_scopes.pop();
+
+            if scope.cleanups.iter().any(|c| cleanup_is_suitable_for(*c, label))
+            {
+                let name = scope.block_name("clean");
+                debug!("generating cleanups for {}", name);
+                let bcx_in = self.new_block(label.is_unwind(), name, None);
+                let mut bcx_out = bcx_in;
+                for cleanup in scope.cleanups.iter().invert() {
+                    if cleanup_is_suitable_for(*cleanup, label) {
+                        bcx_out = cleanup.trans(bcx_out);
+                    }
+                }
+                build::Br(bcx_out, prev_llbb);
+                prev_llbb = bcx_in.llbb;
+            } else {
+                debug!("no suitable cleanups in {}",
+                       scope.block_name("clean"));
+            }
+
+            scope.add_cached_early_exit(label, prev_llbb);
+            self.push_scope(scope);
+        }
+
+        debug!("trans_cleanups_to_exit_scope: prev_llbb={}", prev_llbb);
+
+        assert_eq!(self.scopes_len(), orig_scopes_len);
+        prev_llbb
+    }
+
+    fn get_or_create_landing_pad(&self) -> BasicBlockRef {
+        /*!
+         * Creates a landing pad for the top scope, if one does not
+         * exist.  The landing pad will perform all cleanups necessary
+         * for an unwind and then `resume` to continue error
+         * propagation:
+         *
+         *     landing_pad -> ... cleanups ... -> [resume]
+         *
+         * (The cleanups and resume instruction are created by
+         * `trans_cleanups_to_exit_scope()`, not in this function
+         * itself.)
+         */
+
+        let pad_bcx;
+
+        debug!("get_or_create_landing_pad");
+
+        // Check if a landing pad block exists; if not, create one.
+        {
+            let mut scopes = self.scopes.borrow_mut();
+            let last_scope = scopes.get().mut_last();
+            match last_scope.cached_landing_pad {
+                Some(llbb) => { return llbb; }
+                None => {
+                    let name = last_scope.block_name("unwind");
+                    pad_bcx = self.new_block(true, name, None);
+                    last_scope.cached_landing_pad = Some(pad_bcx.llbb);
+                }
+            }
+        }
+
+        // The landing pad return type (the type being propagated). Not sure what
+        // this represents but it's determined by the personality function and
+        // this is what the EH proposal example uses.
+        let llretty = Type::struct_([Type::i8p(), Type::i32()], false);
+
+        // The exception handling personality function.
+        let def_id = common::langcall(pad_bcx, None, "", EhPersonalityLangItem);
+        let llpersonality = callee::trans_fn_ref(pad_bcx, def_id, 0).llfn;
+
+        // The only landing pad clause will be 'cleanup'
+        let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u);
+
+        // The landing pad block is a cleanup
+        build::SetCleanup(pad_bcx, llretval);
+
+        // We store the retval in a function-central alloca, so that calls to
+        // Resume can find it.
+        match self.personality.get() {
+            Some(addr) => {
+                build::Store(pad_bcx, llretval, addr);
+            }
+            None => {
+                let addr = base::alloca(pad_bcx, common::val_ty(llretval), "");
+                self.personality.set(Some(addr));
+                build::Store(pad_bcx, llretval, addr);
+            }
+        }
+
+        // Generate the cleanup block and branch to it.
+        let cleanup_llbb = self.trans_cleanups_to_exit_scope(UnwindExit);
+        build::Br(pad_bcx, cleanup_llbb);
+
+        return pad_bcx.llbb;
+    }
+}
+
+impl<'a> CleanupScope<'a> {
+    fn new(kind: CleanupScopeKind<'a>) -> CleanupScope<'a> {
+        CleanupScope {
+            kind: kind,
+            cleanups: opt_vec::Empty,
+            cached_early_exits: opt_vec::Empty,
+            cached_landing_pad: None,
+        }
+    }
+
+    fn clear_cached_exits(&mut self) {
+        self.cached_early_exits = opt_vec::Empty;
+        self.cached_landing_pad = None;
+    }
+
+    fn cached_early_exit(&self,
+                         label: EarlyExitLabel)
+                         -> Option<BasicBlockRef> {
+        self.cached_early_exits.iter().
+            find(|e| e.label == label).
+            map(|e| e.cleanup_block)
+    }
+
+    fn add_cached_early_exit(&mut self,
+                             label: EarlyExitLabel,
+                             blk: BasicBlockRef) {
+        self.cached_early_exits.push(
+            CachedEarlyExit { label: label,
+                              cleanup_block: blk });
+    }
+
+    fn needs_invoke(&self) -> bool {
+        /*! True if this scope has cleanups for use during unwinding */
+
+        self.cached_landing_pad.is_some() ||
+            self.cleanups.iter().any(|c| c.clean_on_unwind())
+    }
+
+    fn block_name(&self, prefix: &str) -> ~str {
+        /*!
+         * Returns a suitable name to use for the basic block that
+         * handles this cleanup scope
+         */
+
+        match self.kind {
+            CustomScopeKind => format!("{}_custom_", prefix),
+            AstScopeKind(id) => format!("{}_ast_{}_", prefix, id),
+            LoopScopeKind(id, _) => format!("{}_loop_{}_", prefix, id),
+        }
+    }
+}
+
+impl<'a> CleanupScopeKind<'a> {
+    fn is_temp(&self) -> bool {
+        match *self {
+            CustomScopeKind => true,
+            LoopScopeKind(..) | AstScopeKind(..) => false,
+        }
+    }
+
+    fn is_ast_with_id(&self, id: ast::NodeId) -> bool {
+        match *self {
+            CustomScopeKind | LoopScopeKind(..) => false,
+            AstScopeKind(i) => i == id
+        }
+    }
+
+    fn is_loop_with_id(&self, id: ast::NodeId) -> bool {
+        match *self {
+            CustomScopeKind | AstScopeKind(..) => false,
+            LoopScopeKind(i, _) => i == id
+        }
+    }
+
+    fn early_exit_block(&self,
+                        id: ast::NodeId,
+                        exit: uint) -> Option<BasicBlockRef> {
+        /*!
+         * If this is a loop scope with id `id`, return the early
+         * exit block `exit`, else `None`
+         */
+
+        match *self {
+            LoopScopeKind(i, ref exits) if id == i => Some(exits[exit].llbb),
+            _ => None,
+        }
+    }
+}
+
+impl EarlyExitLabel {
+    fn is_unwind(&self) -> bool {
+        match *self {
+            UnwindExit => true,
+            _ => false
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Cleanup types
+
+pub struct DropValue {
+    is_immediate: bool,
+    on_unwind: bool,
+    val: ValueRef,
+    ty: ty::t,
+}
+
+impl Cleanup for DropValue {
+    fn clean_on_unwind(&self) -> bool {
+        self.on_unwind
+    }
+
+    fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> {
+        if self.is_immediate {
+            glue::drop_ty_immediate(bcx, self.val, self.ty)
+        } else {
+            glue::drop_ty(bcx, self.val, self.ty)
+        }
+    }
+}
+
+pub struct FreeValue {
+    ptr: ValueRef,
+    heap: common::heap,
+}
+
+impl Cleanup for FreeValue {
+    fn clean_on_unwind(&self) -> bool {
+        true
+    }
+
+    fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> {
+        match self.heap {
+            common::heap_managed => {
+                glue::trans_free(bcx, self.ptr)
+            }
+            common::heap_exchange | common::heap_exchange_closure => {
+                glue::trans_exchange_free(bcx, self.ptr)
+            }
+        }
+    }
+}
+
+pub fn temporary_scope(tcx: ty::ctxt,
+                       id: ast::NodeId)
+                       -> ScopeId {
+    match tcx.region_maps.temporary_scope(id) {
+        Some(scope) => {
+            let r = AstScope(scope);
+            debug!("temporary_scope({}) = {:?}", id, r);
+            r
+        }
+        None => {
+            tcx.sess.bug(format!("no temporary scope available for expr {}", id))
+        }
+    }
+}
+
+pub fn var_scope(tcx: ty::ctxt,
+                 id: ast::NodeId)
+                 -> ScopeId {
+    let r = AstScope(tcx.region_maps.var_scope(id));
+    debug!("var_scope({}) = {:?}", id, r);
+    r
+}
+
+fn cleanup_is_suitable_for(c: &Cleanup,
+                           label: EarlyExitLabel) -> bool {
+    !label.is_unwind() || c.clean_on_unwind()
+}
+
+///////////////////////////////////////////////////////////////////////////
+// These traits just exist to put the methods into this file.
+
+pub trait CleanupMethods<'a> {
+    fn push_ast_cleanup_scope(&self, id: ast::NodeId);
+    fn push_loop_cleanup_scope(&self,
+                                   id: ast::NodeId,
+                                   exits: [&'a Block<'a>, ..EXIT_MAX]);
+    fn push_custom_cleanup_scope(&self) -> CustomScopeIndex;
+    fn pop_and_trans_ast_cleanup_scope(&self,
+                                              bcx: &'a Block<'a>,
+                                              cleanup_scope: ast::NodeId)
+                                              -> &'a Block<'a>;
+    fn pop_loop_cleanup_scope(&self,
+                              cleanup_scope: ast::NodeId);
+    fn pop_custom_cleanup_scope(&self,
+                                custom_scope: CustomScopeIndex);
+    fn pop_and_trans_custom_cleanup_scope(&self,
+                                          bcx: &'a Block<'a>,
+                                          custom_scope: CustomScopeIndex)
+                                          -> &'a Block<'a>;
+    fn top_loop_scope(&self) -> ast::NodeId;
+    fn normal_exit_block(&self,
+                         cleanup_scope: ast::NodeId,
+                         exit: uint) -> BasicBlockRef;
+    fn return_exit_block(&self) -> BasicBlockRef;
+    fn schedule_drop_mem(&self,
+                         cleanup_scope: ScopeId,
+                         val: ValueRef,
+                         ty: ty::t);
+    fn schedule_drop_immediate(&self,
+                               cleanup_scope: ScopeId,
+                               val: ValueRef,
+                               ty: ty::t);
+    fn schedule_free_value(&self,
+                           cleanup_scope: ScopeId,
+                           val: ValueRef,
+                           heap: common::heap);
+    fn schedule_clean(&self,
+                      cleanup_scope: ScopeId,
+                      cleanup: ~Cleanup);
+    fn schedule_clean_in_ast_scope(&self,
+                                   cleanup_scope: ast::NodeId,
+                                   cleanup: ~Cleanup);
+    fn schedule_clean_in_custom_scope(&self,
+                                    custom_scope: CustomScopeIndex,
+                                    cleanup: ~Cleanup);
+    fn needs_invoke(&self) -> bool;
+    fn get_landing_pad(&self) -> BasicBlockRef;
+}
+
+trait CleanupHelperMethods<'a> {
+    fn top_ast_scope(&self) -> Option<ast::NodeId>;
+    fn top_nonempty_cleanup_scope(&self) -> Option<uint>;
+    fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool;
+    fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool;
+    fn trans_scope_cleanups(&self,
+                            bcx: &'a Block<'a>,
+                            scope: &CleanupScope<'a>) -> &'a Block<'a>;
+    fn trans_cleanups_to_exit_scope(&self,
+                                    label: EarlyExitLabel)
+                                    -> BasicBlockRef;
+    fn get_or_create_landing_pad(&self) -> BasicBlockRef;
+    fn scopes_len(&self) -> uint;
+    fn push_scope(&self, scope: CleanupScope<'a>);
+    fn pop_scope(&self) -> CleanupScope<'a>;
+    fn top_scope<R>(&self, f: |&CleanupScope<'a>| -> R) -> R;
+}
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index f3d061f70b1..58f7171452e 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -16,7 +16,7 @@ use middle::moves;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
-use middle::trans::datum::{Datum, INIT};
+use middle::trans::datum::{Datum, Lvalue};
 use middle::trans::debuginfo;
 use middle::trans::expr;
 use middle::trans::glue;
@@ -112,7 +112,7 @@ pub enum EnvAction {
 
 pub struct EnvValue {
     action: EnvAction,
-    datum: Datum
+    datum: Datum<Lvalue>
 }
 
 impl EnvAction {
@@ -219,7 +219,7 @@ pub fn store_environment<'a>(
 
     // Copy expr values into boxed bindings.
     let mut bcx = bcx;
-    for (i, bv) in bound_values.iter().enumerate() {
+    for (i, bv) in bound_values.move_iter().enumerate() {
         debug!("Copy {} into closure", bv.to_str(ccx));
 
         if ccx.sess.asm_comments() {
@@ -230,17 +230,13 @@ pub fn store_environment<'a>(
         let bound_data = GEPi(bcx, llbox, [0u, abi::box_field_body, i]);
 
         match bv.action {
-            EnvCopy => {
-                bcx = bv.datum.copy_to(bcx, INIT, bound_data);
-            }
-            EnvMove => {
-                bcx = bv.datum.move_to(bcx, INIT, bound_data);
+            EnvCopy | EnvMove => {
+                bcx = bv.datum.store_to(bcx, bound_data);
             }
             EnvRef => {
-                Store(bcx, bv.datum.to_ref_llval(bcx), bound_data);
+                Store(bcx, bv.datum.to_llref(), bound_data);
             }
         }
-
     }
 
     ClosureResult { llbox: llbox, cdata_ty: cdata_ty, bcx: bcx }
@@ -413,7 +409,6 @@ pub fn trans_expr_fn<'a>(
                           None,
                           bcx.fcx.param_substs,
                           user_id,
-                          None,
                           [],
                           ty::ty_fn_ret(fty),
                           |fcx| load_environment(fcx, cdata_ty, cap_vars, sigil));
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index f98fec7cbef..e466e4da38d 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -20,8 +20,9 @@ use lib;
 use middle::lang_items::LangItem;
 use middle::trans::base;
 use middle::trans::build;
+use middle::trans::cleanup;
 use middle::trans::datum;
-use middle::trans::glue;
+use middle::trans::datum::{Datum, Lvalue};
 use middle::trans::debuginfo;
 use middle::trans::type_::Type;
 use middle::ty::substs;
@@ -37,8 +38,7 @@ use std::cast;
 use std::cell::{Cell, RefCell};
 use std::hashmap::HashMap;
 use std::libc::{c_uint, c_longlong, c_ulonglong, c_char};
-use std::vec;
-use syntax::ast::{Name, Ident};
+use syntax::ast::{Ident};
 use syntax::ast_map::{Path, PathElem, PathPrettyName};
 use syntax::codemap::Span;
 use syntax::parse::token;
@@ -64,7 +64,7 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool {
     let tcx = ccx.tcx;
     let simple = ty::type_is_scalar(ty) || ty::type_is_boxed(ty) ||
         ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) ||
-        type_is_newtype_immediate(ccx, ty) ||
+        type_is_newtype_immediate(ccx, ty) || ty::type_is_bot(ty) ||
         ty::type_is_simd(tcx, ty);
     if simple {
         return true;
@@ -75,10 +75,33 @@ pub fn type_is_immediate(ccx: &CrateContext, ty: ty::t) -> bool {
             let llty = sizing_type_of(ccx, ty);
             llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type)
         }
-        _ => false
+        _ => type_is_zero_size(ccx, ty)
     }
 }
 
+pub fn type_is_zero_size(ccx: &CrateContext, ty: ty::t) -> bool {
+    /*!
+     * Identify types which have size zero at runtime.
+     */
+
+    use middle::trans::machine::llsize_of_alloc;
+    use middle::trans::type_of::sizing_type_of;
+    let llty = sizing_type_of(ccx, ty);
+    llsize_of_alloc(ccx, llty) == 0
+}
+
+pub fn return_type_is_void(ccx: &CrateContext, ty: ty::t) -> bool {
+    /*!
+     * Identifies types which we declare to be equivalent to `void`
+     * in C for the purpose of function return types. These are
+     * `()`, bot, and uninhabited enums. Note that all such types
+     * are also zero-size, but not all zero-size types use a `void`
+     * return type (in order to aid with C ABI compatibility).
+     */
+
+    ty::type_is_nil(ty) || ty::type_is_bot(ty) || ty::type_is_empty(ccx.tcx, ty)
+}
+
 pub fn gensym_name(name: &str) -> (Ident, PathElem) {
     let name = token::gensym(name);
     let ident = Ident::new(name);
@@ -122,6 +145,15 @@ pub struct tydesc_info {
  *
  */
 
+pub struct NodeInfo {
+    id: ast::NodeId,
+    span: Span,
+}
+
+pub fn expr_info(expr: &ast::Expr) -> NodeInfo {
+    NodeInfo { id: expr.id, span: expr.span }
+}
+
 pub struct Stats {
     n_static_tydescs: Cell<uint>,
     n_glues_created: Cell<uint>,
@@ -185,6 +217,10 @@ impl Repr for param_substs {
     }
 }
 
+// work around bizarre resolve errors
+type RvalueDatum = datum::Datum<datum::Rvalue>;
+type LvalueDatum = datum::Datum<datum::Lvalue>;
+
 // Function context.  Every LLVM function we create will have one of
 // these.
 pub struct FunctionContext<'a> {
@@ -213,13 +249,15 @@ pub struct FunctionContext<'a> {
     // allocas, so that LLVM will coalesce them into a single alloca call.
     alloca_insert_pt: Cell<Option<ValueRef>>,
     llreturn: Cell<Option<BasicBlockRef>>,
+
     // The 'self' value currently in use in this function, if there
     // is one.
     //
     // NB: This is the type of the self *variable*, not the self *type*. The
     // self type is set only for default methods, while the self variable is
     // set for all methods.
-    llself: Cell<Option<datum::Datum>>,
+    llself: Cell<Option<LvalueDatum>>,
+
     // The a value alloca'd for calls to upcalls.rust_personality. Used when
     // outputting the resume instruction.
     personality: Cell<Option<ValueRef>>,
@@ -230,10 +268,12 @@ pub struct FunctionContext<'a> {
     caller_expects_out_pointer: bool,
 
     // Maps arguments to allocas created for them in llallocas.
-    llargs: RefCell<HashMap<ast::NodeId, datum::Datum>>,
+    llargs: RefCell<HashMap<ast::NodeId, LvalueDatum>>,
+
     // Maps the def_ids for local variables to the allocas created for
     // them in llallocas.
-    lllocals: RefCell<HashMap<ast::NodeId, datum::Datum>>,
+    lllocals: RefCell<HashMap<ast::NodeId, LvalueDatum>>,
+
     // Same as above, but for closure upvars
     llupvars: RefCell<HashMap<ast::NodeId, ValueRef>>,
 
@@ -253,14 +293,14 @@ pub struct FunctionContext<'a> {
     // The arena that blocks are allocated from.
     block_arena: TypedArena<Block<'a>>,
 
-    // The arena that scope info is allocated from.
-    scope_info_arena: TypedArena<ScopeInfo<'a>>,
-
     // This function's enclosing crate context.
     ccx: @CrateContext,
 
     // Used and maintained by the debuginfo module.
     debug_context: debuginfo::FunctionDebugContext,
+
+    // Cleanup scopes.
+    scopes: RefCell<~[cleanup::CleanupScope<'a>]>,
 }
 
 impl<'a> FunctionContext<'a> {
@@ -302,315 +342,67 @@ impl<'a> FunctionContext<'a> {
 
         self.llreturn.get().unwrap()
     }
-}
 
-pub fn warn_not_to_commit(ccx: &CrateContext, msg: &str) {
-    if !ccx.do_not_commit_warning_issued.get() {
-        ccx.do_not_commit_warning_issued.set(true);
-        ccx.sess.warn(msg.to_str() + " -- do not commit like this!");
-    }
-}
-
-// Heap selectors. Indicate which heap something should go on.
-#[deriving(Eq)]
-pub enum heap {
-    heap_managed,
-    heap_exchange,
-    heap_exchange_closure
-}
-
-#[deriving(Clone, Eq)]
-pub enum cleantype {
-    normal_exit_only,
-    normal_exit_and_unwind
-}
-
-// Cleanup functions
-
-/// A cleanup function: a built-in destructor.
-pub trait CleanupFunction {
-    fn clean<'a>(&self, block: &'a Block<'a>) -> &'a Block<'a>;
-}
-
-/// A cleanup function that calls the "drop glue" (destructor function) on
-/// a datum.
-struct DatumDroppingCleanupFunction {
-    datum: datum::Datum
-}
-
-impl CleanupFunction for DatumDroppingCleanupFunction {
-    fn clean<'a>(&self, block: &'a Block<'a>) -> &'a Block<'a> {
-        self.datum.drop_val(block)
-    }
-}
-
-/// A cleanup function that frees some memory in the garbage-collected heap.
-pub struct GCHeapFreeingCleanupFunction {
-    ptr: ValueRef,
-}
-
-impl CleanupFunction for GCHeapFreeingCleanupFunction {
-    fn clean<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> {
-        glue::trans_free(bcx, self.ptr)
-    }
-}
-
-/// A cleanup function that frees some memory in the exchange heap.
-pub struct ExchangeHeapFreeingCleanupFunction {
-    ptr: ValueRef,
-}
-
-impl CleanupFunction for ExchangeHeapFreeingCleanupFunction {
-    fn clean<'a>(&self, bcx: &'a Block) -> &'a Block<'a> {
-        glue::trans_exchange_free(bcx, self.ptr)
-    }
-}
-
-pub enum cleanup {
-    Clean(@CleanupFunction, cleantype),
-    CleanTemp(ValueRef, @CleanupFunction, cleantype),
-}
-
-// Can't use deriving(Clone) because of the managed closure.
-impl Clone for cleanup {
-    fn clone(&self) -> cleanup {
-        match *self {
-            Clean(f, ct) => Clean(f, ct),
-            CleanTemp(v, f, ct) => CleanTemp(v, f, ct),
+    pub fn new_block(&'a self,
+                     is_lpad: bool,
+                     name: &str,
+                     opt_node_id: Option<ast::NodeId>)
+                     -> &'a Block<'a> {
+        unsafe {
+            let llbb = name.with_c_str(|buf| {
+                    llvm::LLVMAppendBasicBlockInContext(self.ccx.llcx,
+                                                        self.llfn,
+                                                        buf)
+                });
+            Block::new(llbb, is_lpad, opt_node_id, self)
         }
     }
-}
 
-// Used to remember and reuse existing cleanup paths
-// target: none means the path ends in an resume instruction
-#[deriving(Clone)]
-pub struct cleanup_path {
-    target: Option<BasicBlockRef>,
-    size: uint,
-    dest: BasicBlockRef
-}
-
-pub fn shrink_scope_clean(scope_info: &ScopeInfo, size: uint) {
-    scope_info.landing_pad.set(None);
-    let new_cleanup_paths = {
-        let cleanup_paths = scope_info.cleanup_paths.borrow();
-        cleanup_paths.get()
-                     .iter()
-                     .take_while(|&cu| cu.size <= size)
-                     .map(|&x| x)
-                     .collect()
-    };
-    scope_info.cleanup_paths.set(new_cleanup_paths)
-}
-
-pub fn grow_scope_clean(scope_info: &ScopeInfo) {
-    scope_info.landing_pad.set(None);
-}
-
-pub fn cleanup_type(cx: ty::ctxt, ty: ty::t) -> cleantype {
-    if ty::type_needs_unwind_cleanup(cx, ty) {
-        normal_exit_and_unwind
-    } else {
-        normal_exit_only
+    pub fn new_id_block(&'a self,
+                        name: &str,
+                        node_id: ast::NodeId)
+                        -> &'a Block<'a> {
+        self.new_block(false, name, Some(node_id))
     }
-}
-
-pub fn add_clean(bcx: &Block, val: ValueRef, ty: ty::t) {
-    if !ty::type_needs_drop(bcx.tcx(), ty) { return; }
-
-    debug!("add_clean({}, {}, {})", bcx.to_str(), bcx.val_to_str(val), ty.repr(bcx.tcx()));
 
-    let cleanup_type = cleanup_type(bcx.tcx(), ty);
-    in_scope_cx(bcx, None, |scope_info| {
-        {
-            let mut cleanups = scope_info.cleanups.borrow_mut();
-            cleanups.get().push(Clean(@DatumDroppingCleanupFunction {
-                datum: datum::Datum {
-                    val: val,
-                    ty: ty,
-                    mode: datum::ByRef(datum::ZeroMem)
-                }
-            } as @CleanupFunction,
-            cleanup_type));
-        }
-        grow_scope_clean(scope_info);
-    })
-}
-
-pub fn add_clean_temp_immediate(bcx: &Block, val: ValueRef, ty: ty::t) {
-    if !ty::type_needs_drop(bcx.tcx(), ty) { return; }
-
-    debug!("add_clean_temp_immediate({}, {}, {})",
-           bcx.to_str(), bcx.val_to_str(val),
-           ty.repr(bcx.tcx()));
-    let cleanup_type = cleanup_type(bcx.tcx(), ty);
-    in_scope_cx(bcx, None, |scope_info| {
-        {
-            let mut cleanups = scope_info.cleanups.borrow_mut();
-            cleanups.get().push(CleanTemp(val, @DatumDroppingCleanupFunction {
-                datum: datum::Datum {
-                    val: val,
-                    ty: ty,
-                    mode: datum::ByValue
-                }
-            } as @CleanupFunction,
-            cleanup_type));
-        }
-        grow_scope_clean(scope_info);
-    })
-}
-
-pub fn add_clean_temp_mem(bcx: &Block, val: ValueRef, t: ty::t) {
-    add_clean_temp_mem_in_scope_(bcx, None, val, t);
-}
-
-pub fn add_clean_temp_mem_in_scope(bcx: &Block,
-                                   scope_id: ast::NodeId,
-                                   val: ValueRef,
-                                   t: ty::t) {
-    add_clean_temp_mem_in_scope_(bcx, Some(scope_id), val, t);
-}
-
-pub fn add_clean_temp_mem_in_scope_(bcx: &Block, scope_id: Option<ast::NodeId>,
-                                    val: ValueRef, t: ty::t) {
-    if !ty::type_needs_drop(bcx.tcx(), t) { return; }
-    debug!("add_clean_temp_mem({}, {}, {})",
-           bcx.to_str(), bcx.val_to_str(val),
-           t.repr(bcx.tcx()));
-    let cleanup_type = cleanup_type(bcx.tcx(), t);
-    in_scope_cx(bcx, scope_id, |scope_info| {
-        {
-            let mut cleanups = scope_info.cleanups.borrow_mut();
-            cleanups.get().push(CleanTemp(val, @DatumDroppingCleanupFunction {
-                datum: datum::Datum {
-                    val: val,
-                    ty: t,
-                    mode: datum::ByRef(datum::RevokeClean)
-                }
-            } as @CleanupFunction,
-            cleanup_type));
-        }
-        grow_scope_clean(scope_info);
-    })
-}
+    pub fn new_temp_block(&'a self,
+                          name: &str)
+                          -> &'a Block<'a> {
+        self.new_block(false, name, None)
+    }
 
-pub fn add_clean_free(cx: &Block, ptr: ValueRef, heap: heap) {
-    let free_fn = match heap {
-        heap_managed => {
-            @GCHeapFreeingCleanupFunction {
-                ptr: ptr,
-            } as @CleanupFunction
+    pub fn join_blocks(&'a self,
+                       id: ast::NodeId,
+                       in_cxs: &[&'a Block<'a>])
+                       -> &'a Block<'a> {
+        let out = self.new_id_block("join", id);
+        let mut reachable = false;
+        for bcx in in_cxs.iter() {
+            if !bcx.unreachable.get() {
+                build::Br(*bcx, out.llbb);
+                reachable = true;
+            }
         }
-        heap_exchange | heap_exchange_closure => {
-            @ExchangeHeapFreeingCleanupFunction {
-                ptr: ptr,
-            } as @CleanupFunction
+        if !reachable {
+            build::Unreachable(out);
         }
-    };
-    in_scope_cx(cx, None, |scope_info| {
-        {
-            let mut cleanups = scope_info.cleanups.borrow_mut();
-            cleanups.get().push(CleanTemp(ptr,
-                                           free_fn,
-                                           normal_exit_and_unwind));
-        }
-        grow_scope_clean(scope_info);
-    })
-}
-
-// Note that this only works for temporaries. We should, at some point, move
-// to a system where we can also cancel the cleanup on local variables, but
-// this will be more involved. For now, we simply zero out the local, and the
-// drop glue checks whether it is zero.
-pub fn revoke_clean(cx: &Block, val: ValueRef) {
-    in_scope_cx(cx, None, |scope_info| {
-        let cleanup_pos = {
-            let mut cleanups = scope_info.cleanups.borrow_mut();
-            debug!("revoke_clean({}, {}) revoking {:?} from {:?}",
-                   cx.to_str(), cx.val_to_str(val), val, cleanups.get());
-            cleanups.get().iter().position(|cu| {
-                match *cu {
-                    CleanTemp(v, _, _) if v == val => true,
-                    _ => false
-                }
-            })
-        };
-        debug!("revoke_clean({}, {}) revoking {:?}",
-               cx.to_str(), cx.val_to_str(val), cleanup_pos);
-        for &i in cleanup_pos.iter() {
-            let new_cleanups = {
-                let cleanups = scope_info.cleanups.borrow();
-                vec::append(cleanups.get().slice(0u, i).to_owned(),
-                            cleanups.get().slice(i + 1u, cleanups.get()
-                                                                 .len()))
-            };
-            scope_info.cleanups.set(new_cleanups);
-            shrink_scope_clean(scope_info, i);
-        }
-    })
-}
-
-pub fn block_cleanups(bcx: &Block) -> ~[cleanup] {
-    match bcx.scope.get() {
-       None  => ~[],
-       Some(inf) => inf.cleanups.get(),
-    }
-}
-
-pub struct ScopeInfo<'a> {
-    parent: Option<&'a ScopeInfo<'a>>,
-    loop_break: Option<&'a Block<'a>>,
-    loop_label: Option<Name>,
-    // A list of functions that must be run at when leaving this
-    // block, cleaning up any variables that were introduced in the
-    // block.
-    cleanups: RefCell<~[cleanup]>,
-    // Existing cleanup paths that may be reused, indexed by destination and
-    // cleared when the set of cleanups changes.
-    cleanup_paths: RefCell<~[cleanup_path]>,
-    // Unwinding landing pad. Also cleared when cleanups change.
-    landing_pad: Cell<Option<BasicBlockRef>>,
-    // info about the AST node this scope originated from, if any
-    node_info: Option<NodeInfo>,
-}
-
-impl<'a> ScopeInfo<'a> {
-    pub fn empty_cleanups(&self) -> bool {
-        let cleanups = self.cleanups.borrow();
-        cleanups.get().is_empty()
-    }
-}
-
-pub trait get_node_info {
-    fn info(&self) -> Option<NodeInfo>;
-}
-
-impl get_node_info for ast::Expr {
-    fn info(&self) -> Option<NodeInfo> {
-        Some(NodeInfo {id: self.id,
-                       callee_id: self.get_callee_id(),
-                       span: self.span})
+        return out;
     }
 }
 
-impl get_node_info for ast::Block {
-    fn info(&self) -> Option<NodeInfo> {
-        Some(NodeInfo {id: self.id,
-                       callee_id: None,
-                       span: self.span})
-    }
-}
-
-impl get_node_info for Option<@ast::Expr> {
-    fn info(&self) -> Option<NodeInfo> {
-        self.as_ref().and_then(|s| s.info())
+pub fn warn_not_to_commit(ccx: &mut CrateContext, msg: &str) {
+    if !ccx.do_not_commit_warning_issued.get() {
+        ccx.do_not_commit_warning_issued.set(true);
+        ccx.sess.warn(msg.to_str() + " -- do not commit like this!");
     }
 }
 
-pub struct NodeInfo {
-    id: ast::NodeId,
-    callee_id: Option<ast::NodeId>,
-    span: Span
+// Heap selectors. Indicate which heap something should go on.
+#[deriving(Eq)]
+pub enum heap {
+    heap_managed,
+    heap_exchange,
+    heap_exchange_closure
 }
 
 // Basic block context.  We create a block context for each basic block
@@ -627,13 +419,14 @@ pub struct Block<'a> {
     llbb: BasicBlockRef,
     terminated: Cell<bool>,
     unreachable: Cell<bool>,
-    parent: Option<&'a Block<'a>>,
-    // The current scope within this basic block
-    scope: RefCell<Option<&'a ScopeInfo<'a>>>,
+
     // Is this block part of a landing pad?
     is_lpad: bool,
-    // info about the AST node this block originated from, if any
-    node_info: Option<NodeInfo>,
+
+    // AST node-id associated with this block, if any. Used for
+    // debugging purposes only.
+    opt_node_id: Option<ast::NodeId>,
+
     // The function context for the function to which this block is
     // attached.
     fcx: &'a FunctionContext<'a>,
@@ -642,20 +435,17 @@ pub struct Block<'a> {
 impl<'a> Block<'a> {
     pub fn new<'a>(
                llbb: BasicBlockRef,
-               parent: Option<&'a Block<'a>>,
                is_lpad: bool,
-               node_info: Option<NodeInfo>,
+               opt_node_id: Option<ast::NodeId>,
                fcx: &'a FunctionContext<'a>)
                -> &'a Block<'a> {
         fcx.block_arena.alloc(Block {
             llbb: llbb,
             terminated: Cell::new(false),
             unreachable: Cell::new(false),
-            parent: parent,
-            scope: RefCell::new(None),
             is_lpad: is_lpad,
-            node_info: node_info,
-            fcx: fcx,
+            opt_node_id: opt_node_id,
+            fcx: fcx
         })
     }
 
@@ -709,12 +499,8 @@ impl<'a> Block<'a> {
     }
 
     pub fn to_str(&self) -> ~str {
-        unsafe {
-            match self.node_info {
-                Some(node_info) => format!("[block {}]", node_info.id),
-                None => format!("[block {}]", transmute::<&Block, *Block>(self)),
-            }
-        }
+        let blk: *Block = self;
+        format!("[block {}]", blk)
     }
 }
 
@@ -743,48 +529,6 @@ pub fn val_ty(v: ValueRef) -> Type {
     }
 }
 
-pub fn in_scope_cx<'a>(
-                   cx: &'a Block<'a>,
-                   scope_id: Option<ast::NodeId>,
-                   f: |si: &'a ScopeInfo<'a>|) {
-    let mut cur = cx;
-    let mut cur_scope = cur.scope.get();
-    loop {
-        cur_scope = match cur_scope {
-            Some(inf) => match scope_id {
-                Some(wanted) => match inf.node_info {
-                    Some(NodeInfo { id: actual, .. }) if wanted == actual => {
-                        debug!("in_scope_cx: selected cur={} (cx={}) info={:?}",
-                               cur.to_str(), cx.to_str(), inf.node_info);
-                        f(inf);
-                        return;
-                    },
-                    _ => inf.parent,
-                },
-                None => {
-                    debug!("in_scope_cx: selected cur={} (cx={}) info={:?}",
-                           cur.to_str(), cx.to_str(), inf.node_info);
-                    f(inf);
-                    return;
-                }
-            },
-            None => {
-                cur = block_parent(cur);
-                cur.scope.get()
-            }
-        }
-    }
-}
-
-pub fn block_parent<'a>(cx: &'a Block<'a>) -> &'a Block<'a> {
-    match cx.parent {
-      Some(b) => b,
-      None    => cx.sess().bug(format!("block_parent called on root block {:?}",
-                                   cx))
-    }
-}
-
-
 // Let T be the content of a box @T.  tuplify_box_ty(t) returns the
 // representation of @T as a tuple (i.e., the ty::t version of what T_box()
 // returns).
@@ -1012,7 +756,7 @@ pub enum mono_param_id {
     mono_repr(uint /* size */,
               uint /* align */,
               MonoDataClass,
-              datum::DatumMode),
+              datum::RvalueMode),
 }
 
 #[deriving(Eq,IterBytes)]
diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs
index 97e338eab85..ec47dbacb39 100644
--- a/src/librustc/middle/trans/controlflow.rs
+++ b/src/librustc/middle/trans/controlflow.rs
@@ -15,10 +15,12 @@ use middle::trans::build::*;
 use middle::trans::callee;
 use middle::trans::common::*;
 use middle::trans::debuginfo;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::expr;
 use middle::ty;
-use util::common::indenter;
 use util::ppaux;
+use util::ppaux::Repr;
 
 use middle::trans::type_::Type;
 
@@ -28,10 +30,56 @@ use syntax::ast_util;
 use syntax::codemap::Span;
 use syntax::visit::Visitor;
 
-pub fn trans_block<'a>(bcx: &'a Block<'a>, b: &ast::Block, dest: expr::Dest)
-                   -> &'a Block<'a> {
+pub fn trans_stmt<'a>(cx: &'a Block<'a>,
+                      s: &ast::Stmt)
+                      -> &'a Block<'a> {
+    let _icx = push_ctxt("trans_stmt");
+    let fcx = cx.fcx;
+    debug!("trans_stmt({})", s.repr(cx.tcx()));
+
+    if cx.sess().asm_comments() {
+        add_span_comment(cx, s.span, s.repr(cx.tcx()));
+    }
+
+    let mut bcx = cx;
+
+    let id = ast_util::stmt_id(s);
+    fcx.push_ast_cleanup_scope(id);
+
+    match s.node {
+        ast::StmtExpr(e, _) | ast::StmtSemi(e, _) => {
+            bcx = expr::trans_into(cx, e, expr::Ignore);
+        }
+        ast::StmtDecl(d, _) => {
+            match d.node {
+                ast::DeclLocal(ref local) => {
+                    bcx = init_local(bcx, *local);
+                    if cx.sess().opts.extra_debuginfo {
+                        debuginfo::create_local_var_metadata(bcx, *local);
+                    }
+                }
+                ast::DeclItem(i) => trans_item(cx.fcx.ccx, i)
+            }
+        }
+        ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
+    }
+
+    bcx = fcx.pop_and_trans_ast_cleanup_scope(
+        bcx, ast_util::stmt_id(s));
+
+    return bcx;
+}
+
+pub fn trans_block<'a>(bcx: &'a Block<'a>,
+                       b: &ast::Block,
+                       dest: expr::Dest)
+                       -> &'a Block<'a> {
     let _icx = push_ctxt("trans_block");
+    let fcx = bcx.fcx;
     let mut bcx = bcx;
+
+    fcx.push_ast_cleanup_scope(b.id);
+
     for s in b.stmts.iter() {
         bcx = trans_stmt(bcx, *s);
     }
@@ -43,27 +91,26 @@ pub fn trans_block<'a>(bcx: &'a Block<'a>, b: &ast::Block, dest: expr::Dest)
             assert!(dest == expr::Ignore || bcx.unreachable.get());
         }
     }
+
+    bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
+
     return bcx;
 }
 
-pub fn trans_if<'a>(
-                bcx: &'a Block<'a>,
-                cond: &ast::Expr,
-                thn: ast::P<ast::Block>,
-                els: Option<@ast::Expr>,
-                dest: expr::Dest)
-                -> &'a Block<'a> {
-    debug!("trans_if(bcx={}, cond={}, thn={:?}, dest={})",
-           bcx.to_str(), bcx.expr_to_str(cond), thn.id,
+pub fn trans_if<'a>(bcx: &'a Block<'a>,
+                    if_id: ast::NodeId,
+                    cond: &ast::Expr,
+                    thn: ast::P<ast::Block>,
+                    els: Option<@ast::Expr>,
+                    dest: expr::Dest)
+                    -> &'a Block<'a> {
+    debug!("trans_if(bcx={}, if_id={}, cond={}, thn={:?}, dest={})",
+           bcx.to_str(), if_id, bcx.expr_to_str(cond), thn.id,
            dest.to_str(bcx.ccx()));
-    let _indenter = indenter();
-
     let _icx = push_ctxt("trans_if");
+    let mut bcx = bcx;
 
-    let Result {bcx, val: cond_val} =
-        expr::trans_to_datum(bcx, cond).to_result();
-
-    let cond_val = bool_to_i1(bcx, cond_val);
+    let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
 
     // Drop branches that are known to be impossible
     if is_const(cond_val) && !is_undef(cond_val) {
@@ -76,11 +123,8 @@ pub fn trans_if<'a>(
                 None => {}
             }
             // if true { .. } [else { .. }]
-            return with_scope(bcx, thn.info(), "if_true_then", |bcx| {
-                let bcx_out = trans_block(bcx, thn, dest);
-                debuginfo::clear_source_location(bcx.fcx);
-                bcx_out
-            })
+            bcx = trans_block(bcx, thn, dest);
+            debuginfo::clear_source_location(bcx.fcx);
         } else {
             let mut trans = TransItemVisitor { ccx: bcx.fcx.ccx } ;
             trans.visit_block(thn, ());
@@ -88,229 +132,174 @@ pub fn trans_if<'a>(
             match els {
                 // if false { .. } else { .. }
                 Some(elexpr) => {
-                    return with_scope(bcx,
-                                      elexpr.info(),
-                                      "if_false_then",
-                                      |bcx| {
-                        let bcx_out = trans_if_else(bcx, elexpr, dest, false);
-                        debuginfo::clear_source_location(bcx.fcx);
-                        bcx_out
-                    })
+                    bcx = expr::trans_into(bcx, elexpr, dest);
+                    debuginfo::clear_source_location(bcx.fcx);
                 }
+
                 // if false { .. }
-                None => return bcx,
+                None => { }
             }
         }
-    }
 
-    let then_bcx_in = scope_block(bcx, thn.info(), "then");
+        return bcx;
+    }
 
+    let name = format!("then-block-{}-", thn.id);
+    let then_bcx_in = bcx.fcx.new_id_block(name, thn.id);
     let then_bcx_out = trans_block(then_bcx_in, thn, dest);
-
     debuginfo::clear_source_location(bcx.fcx);
-    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_in, next_bcx) = match els {
-      Some(elexpr) => {
-          let else_bcx_in = scope_block(bcx, elexpr.info(), "else");
-          let else_bcx_out = trans_if_else(else_bcx_in, elexpr, dest, true);
-          (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out]))
-      }
-      _ => {
-          let next_bcx = sub_block(bcx, "next");
-          Br(then_bcx_out, next_bcx.llbb);
 
-          (next_bcx, next_bcx)
-      }
-    };
+    let next_bcx;
+    match els {
+        Some(elexpr) => {
+            let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
+            let else_bcx_out = expr::trans_into(else_bcx_in, elexpr, dest);
+            next_bcx = bcx.fcx.join_blocks(if_id,
+                                           [then_bcx_out, else_bcx_out]);
+            CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
+        }
 
-    debug!("then_bcx_in={}, else_bcx_in={}",
-           then_bcx_in.to_str(), else_bcx_in.to_str());
+        None => {
+            next_bcx = bcx.fcx.new_id_block("next-block", if_id);
+            Br(then_bcx_out, next_bcx.llbb);
+            CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb);
+        }
+    }
 
     // Clear the source location because it is still set to whatever has been translated
     // right before.
-    debuginfo::clear_source_location(else_bcx_in.fcx);
-    CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
-    return next_bcx;
-
-    // trans `else [ if { .. } ... | { .. } ]`
-    fn trans_if_else<'a>(
-                     else_bcx_in: &'a Block<'a>,
-                     elexpr: @ast::Expr,
-                     dest: expr::Dest,
-                     cleanup: bool)
-                     -> &'a Block<'a> {
-        let else_bcx_out = match elexpr.node {
-            ast::ExprIf(_, _, _) => {
-                let elseif_blk = ast_util::block_from_expr(elexpr);
-                trans_block(else_bcx_in, elseif_blk, dest)
-            }
-            ast::ExprBlock(blk) => {
-                trans_block(else_bcx_in, blk, dest)
-            }
-            // would be nice to have a constraint on ifs
-            _ => else_bcx_in.tcx().sess.bug("strange alternative in if")
-        };
-        if cleanup {
-            debuginfo::clear_source_location(else_bcx_in.fcx);
-            trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in))
-        } else {
-            else_bcx_out
-        }
-    }
-}
+    debuginfo::clear_source_location(next_bcx.fcx);
 
-pub fn join_blocks<'a>(
-                   parent_bcx: &'a Block<'a>,
-                   in_cxs: &[&'a Block<'a>])
-                   -> &'a Block<'a> {
-    let out = sub_block(parent_bcx, "join");
-    let mut reachable = false;
-    for bcx in in_cxs.iter() {
-        if !bcx.unreachable.get() {
-            Br(*bcx, out.llbb);
-            reachable = true;
-        }
-    }
-    if !reachable {
-        Unreachable(out);
-    }
-    return out;
+    next_bcx
 }
 
-pub fn trans_while<'a>(
-                   bcx: &'a Block<'a>,
-                   cond: &ast::Expr,
-                   body: &ast::Block)
-                   -> &'a Block<'a> {
+pub fn trans_while<'a>(bcx: &'a Block<'a>,
+                       loop_id: ast::NodeId,
+                       cond: &ast::Expr,
+                       body: &ast::Block)
+                       -> &'a Block<'a> {
     let _icx = push_ctxt("trans_while");
-    let next_bcx = sub_block(bcx, "while next");
+    let fcx = bcx.fcx;
 
     //            bcx
     //             |
-    //          loop_bcx
-    //             |
     //         cond_bcx_in  <--------+
     //             |                 |
     //         cond_bcx_out          |
     //           |      |            |
     //           |    body_bcx_in    |
-    //    +------+      |            |
+    // cleanup_blk      |            |
     //    |           body_bcx_out --+
-    // next_bcx
+    // next_bcx_in
+
+    let next_bcx_in = fcx.new_id_block("while_exit", loop_id);
+    let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
+    let body_bcx_in = fcx.new_id_block("while_body", body.id);
+
+    fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, cond_bcx_in]);
 
-    let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`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);
+    Br(bcx, cond_bcx_in.llbb);
+
+    // compile the block where we will handle loop cleanups
+    let cleanup_llbb = fcx.normal_exit_block(loop_id, cleanup::EXIT_BREAK);
 
     // compile the condition
     let Result {bcx: cond_bcx_out, val: cond_val} =
-        expr::trans_to_datum(cond_bcx_in, cond).to_result();
-    let cond_val = bool_to_i1(cond_bcx_out, cond_val);
-    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);
+        expr::trans(cond_bcx_in, cond).to_llbool();
+    CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_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);
+    Br(body_bcx_out, cond_bcx_in.llbb);
 
-    return next_bcx;
+    fcx.pop_loop_cleanup_scope(loop_id);
+    return next_bcx_in;
 }
 
-pub fn trans_loop<'a>(
-                  bcx: &'a Block<'a>,
-                  body: &ast::Block,
-                  opt_label: Option<Name>)
-                  -> &'a Block<'a> {
+pub fn trans_loop<'a>(bcx:&'a Block<'a>,
+                      loop_id: ast::NodeId,
+                      body: &ast::Block)
+                      -> &'a Block<'a> {
     let _icx = push_ctxt("trans_loop");
-    let next_bcx = sub_block(bcx, "next");
-    let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`",
-                                       body.info());
+    let fcx = bcx.fcx;
+
+    //            bcx
+    //             |
+    //         body_bcx_in
+    //             |
+    //         body_bcx_out
+    //
+    // next_bcx
+    //
+    // Links between body_bcx_in and next_bcx are created by
+    // break statements.
+
+    let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_id);
+    let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
+
+    fcx.push_loop_cleanup_scope(loop_id, [next_bcx_in, body_bcx_in]);
+
     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;
+    Br(body_bcx_out, body_bcx_in.llbb);
+
+    fcx.pop_loop_cleanup_scope(loop_id);
+
+    return next_bcx_in;
 }
 
-pub fn trans_break_cont<'a>(
-                        bcx: &'a Block<'a>,
-                        opt_label: Option<Name>,
-                        to_end: bool)
-                        -> &'a Block<'a> {
+pub fn trans_break_cont<'a>(bcx: &'a Block<'a>,
+                            expr_id: ast::NodeId,
+                            opt_label: Option<Name>,
+                            exit: uint)
+                            -> &'a Block<'a> {
     let _icx = push_ctxt("trans_break_cont");
-    // Locate closest loop block, outputting cleanup as we go.
-    let mut unwind = bcx;
-    let mut cur_scope = unwind.scope.get();
-    let mut target;
-    loop {
-        cur_scope = match cur_scope {
-            Some(&ScopeInfo {
-                loop_break: Some(brk),
-                loop_label: l,
-                parent,
-                ..
-            }) => {
-                // If we're looking for a labeled loop, check the label...
-                target = if to_end {
-                    brk
-                } else {
-                    unwind
-                };
-                match opt_label {
-                    Some(desired) => match l {
-                        Some(actual) if actual == desired => break,
-                        // If it doesn't match the one we want,
-                        // don't break
-                        _ => parent,
-                    },
-                    None => break,
+    let fcx = bcx.fcx;
+
+    if bcx.unreachable.get() {
+        return bcx;
+    }
+
+    // Locate loop that we will break to
+    let loop_id = match opt_label {
+        None => fcx.top_loop_scope(),
+        Some(_) => {
+            let def_map = bcx.tcx().def_map.borrow();
+            match def_map.get().find(&expr_id) {
+                Some(&ast::DefLabel(loop_id)) => loop_id,
+                ref r => {
+                    bcx.tcx().sess.bug(format!("{:?} in def-map for label", r))
                 }
             }
-            Some(inf) => inf.parent,
-            None => {
-                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.get().unwrap());
-                            cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
-                            Unreachable(bcx);
-                            return bcx;
-                        }
-                };
-                unwind.scope.get()
-            }
         }
-    }
-    cleanup_and_Br(bcx, unwind, target.llbb);
-    Unreachable(bcx);
+    };
+
+    // Generate appropriate cleanup code and branch
+    let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
+    Br(bcx, cleanup_llbb);
+    Unreachable(bcx); // anything afterwards should be ignored
     return bcx;
 }
 
-pub fn trans_break<'a>(bcx: &'a Block<'a>, label_opt: Option<Name>)
-                   -> &'a Block<'a> {
-    return trans_break_cont(bcx, label_opt, true);
+pub fn trans_break<'a>(bcx: &'a Block<'a>,
+                       expr_id: ast::NodeId,
+                       label_opt: Option<Name>)
+                       -> &'a Block<'a> {
+    return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_BREAK);
 }
 
-pub fn trans_cont<'a>(bcx: &'a Block<'a>, label_opt: Option<Name>)
-                  -> &'a Block<'a> {
-    return trans_break_cont(bcx, label_opt, false);
+pub fn trans_cont<'a>(bcx: &'a Block<'a>,
+                      expr_id: ast::NodeId,
+                      label_opt: Option<Name>)
+                      -> &'a Block<'a> {
+    return trans_break_cont(bcx, expr_id, label_opt, cleanup::EXIT_LOOP);
 }
 
-pub fn trans_ret<'a>(bcx: &'a Block<'a>, e: Option<@ast::Expr>)
-                 -> &'a Block<'a> {
+pub fn trans_ret<'a>(bcx: &'a Block<'a>,
+                     e: Option<@ast::Expr>)
+                     -> &'a Block<'a> {
     let _icx = push_ctxt("trans_ret");
+    let fcx = bcx.fcx;
     let mut bcx = bcx;
     let dest = match bcx.fcx.llretptr.get() {
         None => expr::Ignore,
@@ -322,7 +311,8 @@ pub fn trans_ret<'a>(bcx: &'a Block<'a>, e: Option<@ast::Expr>)
         }
         _ => ()
     }
-    cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
+    let cleanup_llbb = fcx.return_exit_block();
+    Br(bcx, cleanup_llbb);
     Unreachable(bcx);
     return bcx;
 }
@@ -338,8 +328,8 @@ pub fn trans_fail_expr<'a>(
         Some(arg_expr) => {
             let ccx = bcx.ccx();
             let tcx = ccx.tcx;
-            let arg_datum = unpack_datum!(
-                bcx, expr::trans_to_datum(bcx, arg_expr));
+            let arg_datum =
+                unpack_datum!(bcx, expr::trans_to_lvalue(bcx, arg_expr, "fail"));
 
             if ty::type_is_str(arg_datum.ty) {
                 let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx);
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index c2591beac4a..467501449b8 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -9,171 +9,142 @@
 // except according to those terms.
 
 /*!
- *
- * 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.  Instead 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 cleanup styles
- *
- * Each datum carries with it an idea of how its value will be cleaned
- * up.  This is primarily determined by the mode: a `ByValue` datum
- * will always be cleaned up by revoking cleanup using
- * `revoke_clean()`, because there is no other option. By ref datums
- * can sometimes be cleaned up via `revoke_clean` (in particular,
- * by-ref datums that originated from rvalues), but sometimes they
- * must be zeroed. This is indicated by the `DatumCleanup`
- * parameter. Note that zeroing a by-ref datum *always works* to
- * cancel the cleanup, but using `revoke_clean` is preferable since
- * there is no runtime cost. Some older parts of the code (notably
- * `match_`, at least at the time of this writing) rely on this and
- * only use zeroing.
- *
- * # Copying, moving, and storing
- *
- * There are three methods for moving the value into a new
- * location:
- *
- * - `copy_to()` will copy the value into a new location, meaning that
- *    the value is first mem-copied and then the new location is "taken"
- *    via the take glue, in effect creating a deep clone.
- *
- * - `move_to()` will copy the value, meaning that the value is mem-copied
- *   into its new home and then the cleanup on the this datum is revoked.
- *   This is a "shallow" clone.  After `move_to()`, the current datum
- *   is invalid and should no longer be used.
- *
- * - `store_to()` either performs a copy or a move depending on the
- *   Rust type of the datum.
- *
- * # 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. */
-
+ * See the section on datums in `doc.rs` for an overview of what
+ * Datums are and how they are intended to be used.
+ */
 
 use lib;
 use lib::llvm::ValueRef;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
-use middle::trans::common;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::expr;
 use middle::trans::glue;
 use middle::trans::tvec;
 use middle::trans::type_of;
 use middle::trans::write_guard;
 use middle::ty;
-use util::common::indenter;
-use util::ppaux::ty_to_str;
+use util::ppaux::{ty_to_str};
 
-use std::uint;
 use syntax::ast;
 use syntax::codemap::Span;
 
-#[deriving(Eq)]
-pub enum CopyAction {
-    INIT,
-    DROP_EXISTING
-}
-
+/**
+ * A `Datum` encapsulates the result of evaluating an expression.  It
+ * describes where the value is stored, what Rust type the value has,
+ * whether it is addressed by reference, and so forth. Please refer
+ * the section on datums in `doc.rs` for more details.
+ */
 #[deriving(Clone)]
-pub struct Datum {
+pub struct Datum<K> {
     /// The llvm value.  This is either a pointer to the Rust value or
-    /// the value itself, depending on `mode` below.
+    /// the value itself, depending on `kind` below.
     val: ValueRef,
 
     /// The rust type of the value.
     ty: ty::t,
 
     /// Indicates whether this is by-ref or by-value.
-    mode: DatumMode,
+    kind: K,
 }
 
-pub struct DatumBlock<'a> {
+pub struct DatumBlock<'a, K> {
     bcx: &'a Block<'a>,
-    datum: Datum,
+    datum: Datum<K>,
+}
+
+pub enum Expr {
+    /// a fresh value that was produced and which has no cleanup yet
+    /// because it has not yet "landed" into its permanent home
+    RvalueExpr(Rvalue),
+
+    /// `val` is a pointer into memory for which a cleanup is scheduled
+    /// (and thus has type *T). If you move out of an Lvalue, you must
+    /// zero out the memory (FIXME #5016).
+    LvalueExpr,
+}
+
+#[deriving(Clone)]
+pub struct Lvalue;
+
+pub struct Rvalue {
+    mode: RvalueMode
+}
+
+pub fn Rvalue(m: RvalueMode) -> Rvalue {
+    Rvalue { mode: m }
 }
 
-#[deriving(Clone, Eq, IterBytes)]
-pub enum DatumMode {
-    /// `val` is a pointer to the actual value (and thus has type *T).
-    /// The argument indicates how to cancel cleanup of this datum if
-    /// the value is moved elsewhere, which can either be by zeroing
-    /// the memory or by canceling a registered cleanup.
-    ByRef(DatumCleanup),
+// Make Datum linear for more type safety.
+impl Drop for Rvalue {
+    fn drop(&mut self) { }
+}
+
+#[deriving(Eq, IterBytes)]
+pub enum RvalueMode {
+    /// `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 {
-    pub fn is_by_ref(&self) -> bool {
-        match *self { ByRef(_) => true, ByValue => false }
-    }
+pub fn Datum<K:KindOps>(val: ValueRef, ty: ty::t, kind: K) -> Datum<K> {
+    Datum { val: val, ty: ty, kind: kind }
+}
 
-    pub fn is_by_value(&self) -> bool {
-        match *self { ByRef(_) => false, ByValue => true }
-    }
+pub fn DatumBlock<'a, K>(bcx: &'a Block<'a>,
+                         datum: Datum<K>)
+                         -> DatumBlock<'a, K> {
+    DatumBlock { bcx: bcx, datum: datum }
 }
 
-/// See `Datum cleanup styles` section at the head of this module.
-#[deriving(Clone, Eq, IterBytes)]
-pub enum DatumCleanup {
-    RevokeClean,
-    ZeroMem
+pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum<Rvalue> {
+    return Datum(val, ty, Rvalue(ByValue));
 }
 
-pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum {
-    return Datum {val: val, ty: ty, mode: ByValue};
+pub fn immediate_rvalue_bcx<'a>(bcx: &'a Block<'a>,
+                                val: ValueRef,
+                                ty: ty::t)
+                                -> DatumBlock<'a, Rvalue> {
+    return DatumBlock(bcx, immediate_rvalue(val, ty))
 }
 
-pub fn immediate_rvalue_bcx<'a>(bcx: &'a Block<'a>, val: ValueRef, ty: ty::t)
-                            -> DatumBlock<'a> {
-    DatumBlock {
-        bcx: bcx,
-        datum: immediate_rvalue(val, ty),
-    }
+
+pub fn lvalue_scratch_datum<'a, A>(bcx: &'a Block<'a>,
+                                   ty: ty::t,
+                                   name: &str,
+                                   zero: bool,
+                                   scope: cleanup::ScopeId,
+                                   arg: A,
+                                   populate: |A, &'a Block<'a>, ValueRef|
+                                             -> &'a Block<'a>)
+                                   -> DatumBlock<'a, Lvalue> {
+    /*!
+     * Allocates temporary space on the stack using alloca() and
+     * returns a by-ref Datum pointing to it. The memory will be
+     * dropped upon exit from `scope`. The callback `populate` should
+     * initialize the memory. If `zero` is true, the space will be
+     * zeroed when it is allocated; this is not necessary unless `bcx`
+     * does not dominate the end of `scope`.
+     */
+
+    let llty = type_of::type_of(bcx.ccx(), ty);
+    let scratch = alloca_maybe_zeroed(bcx, llty, name, zero);
+
+    // Subtle. Populate the scratch memory *before* scheduling cleanup.
+    let bcx = populate(arg, bcx, scratch);
+    bcx.fcx.schedule_drop_mem(scope, scratch, ty);
+
+    DatumBlock(bcx, Datum(scratch, ty, Lvalue))
 }
 
-pub fn scratch_datum(bcx: &Block, ty: ty::t, name: &str, zero: bool)
-                     -> Datum {
+pub fn rvalue_scratch_datum(bcx: &Block,
+                            ty: ty::t,
+                            name: &str)
+                            -> Datum<Rvalue> {
     /*!
      * Allocates temporary space on the stack using alloca() and
      * returns a by-ref Datum pointing to it.  If `zero` is true, the
@@ -185,521 +156,376 @@ pub fn scratch_datum(bcx: &Block, ty: ty::t, name: &str, zero: bool)
      */
 
     let llty = type_of::type_of(bcx.ccx(), ty);
-    let scratch = alloca_maybe_zeroed(bcx, llty, name, zero);
-    Datum { val: scratch, ty: ty, mode: ByRef(RevokeClean) }
+    let scratch = alloca_maybe_zeroed(bcx, llty, name, false);
+    Datum(scratch, ty, Rvalue(ByRef))
+}
+
+pub fn is_by_value_type(ccx: &CrateContext, ty: ty::t) -> bool {
+    appropriate_rvalue_mode(ccx, ty) == ByValue
 }
 
-pub fn appropriate_mode(ccx: &CrateContext, ty: ty::t) -> DatumMode {
+pub fn appropriate_rvalue_mode(ccx: &CrateContext, ty: ty::t) -> RvalueMode {
     /*!
      * Indicates the "appropriate" mode for this value,
      * which is either by ref or by value, depending
      * on whether type is immediate or not.
      */
 
-    if ty::type_is_voidish(ccx.tcx, ty) {
+    if type_is_zero_size(ccx, ty) {
         ByValue
     } else if type_is_immediate(ccx, ty) {
         ByValue
     } else {
-        ByRef(RevokeClean)
+        ByRef
     }
 }
 
-impl Datum {
-    pub fn store_to<'a>(
-                    &self,
-                    bcx: &'a Block<'a>,
-                    action: CopyAction,
-                    dst: ValueRef)
-                    -> &'a Block<'a> {
-        /*!
-         *
-         * Stores this value into its final home.  This moves if
-         * `id` is located in the move table, but copies otherwise.
-         */
-
-        if ty::type_moves_by_default(bcx.tcx(), self.ty) {
-            self.move_to(bcx, action, dst)
-        } else {
-            self.copy_to(bcx, action, dst)
-        }
+fn add_rvalue_clean(mode: RvalueMode,
+                    fcx: &FunctionContext,
+                    scope: cleanup::ScopeId,
+                    val: ValueRef,
+                    ty: ty::t) {
+    match mode {
+        ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
+        ByRef => { fcx.schedule_drop_mem(scope, val, ty); }
     }
+}
 
-    pub fn store_to_dest<'a>(&self, bcx: &'a Block<'a>, dest: expr::Dest)
-                         -> &'a Block<'a> {
-        match dest {
-            expr::Ignore => {
-                return bcx;
-            }
-            expr::SaveIn(addr) => {
-                return self.store_to(bcx, INIT, addr);
-            }
-        }
-    }
+pub trait KindOps {
 
-    pub fn store_to_datum<'a>(
-                          &self,
-                          bcx: &'a Block<'a>,
-                          action: CopyAction,
-                          datum: Datum)
-                          -> &'a Block<'a> {
-        debug!("store_to_datum(self={}, action={:?}, datum={})",
-               self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
-        assert!(datum.mode.is_by_ref());
-        self.store_to(bcx, action, datum.val)
-    }
-
-    pub fn move_to_datum<'a>(
-                         &self,
-                         bcx: &'a Block<'a>,
-                         action: CopyAction,
-                         datum: Datum)
-                         -> &'a Block<'a> {
-        assert!(datum.mode.is_by_ref());
-        self.move_to(bcx, action, datum.val)
-    }
-
-    pub fn copy_to_datum<'a>(
-                         &self,
-                         bcx: &'a Block<'a>,
-                         action: CopyAction,
-                         datum: Datum)
-                         -> &'a Block<'a> {
-        assert!(datum.mode.is_by_ref());
-        self.copy_to(bcx, action, datum.val)
-    }
-
-    pub fn copy_to<'a>(
-                   &self,
-                   bcx: &'a Block<'a>,
-                   action: CopyAction,
-                   dst: ValueRef)
-                   -> &'a Block<'a> {
-        /*!
-         *
-         * 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. */
+    /**
+     * Take appropriate action after the value in `datum` has been
+     * stored to a new location.
+     */
+    fn post_store<'a>(&self,
+                      bcx: &'a Block<'a>,
+                      val: ValueRef,
+                      ty: ty::t)
+                      -> &'a Block<'a>;
+
+    /**
+     * True if this mode is a reference mode, meaning that the datum's
+     * val field is a pointer to the actual value
+     */
+    fn is_by_ref(&self) -> bool;
 
-        let _icx = push_ctxt("copy_to");
+    /**
+     * Converts to an Expr kind
+     */
+    fn to_expr_kind(self) -> Expr;
 
-        if ty::type_is_voidish(bcx.tcx(), self.ty) {
-            return bcx;
-        }
+}
 
-        debug!("copy_to(self={}, action={:?}, dst={})",
-               self.to_str(bcx.ccx()), action, bcx.val_to_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);
-                    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)
-        }
+impl KindOps for Rvalue {
+    fn post_store<'a>(&self,
+                      bcx: &'a Block<'a>,
+                      _val: ValueRef,
+                      _ty: ty::t)
+                      -> &'a Block<'a> {
+        // No cleanup is scheduled for an rvalue, so we don't have
+        // to do anything after a move to cancel or duplicate it.
+        bcx
     }
 
-    pub fn copy_to_no_check<'a>(
-                            &self,
-                            bcx: &'a Block<'a>,
-                            action: CopyAction,
-                            dst: ValueRef)
-                            -> &'a Block<'a> {
-        /*!
-         *
-         * A helper for `copy_to()` which does not check to see if we
-         * are copying to/from the same value. */
+    fn is_by_ref(&self) -> bool {
+        self.mode == ByRef
+    }
 
-        let _icx = push_ctxt("copy_to_no_check");
-        let mut bcx = bcx;
+    fn to_expr_kind(self) -> Expr {
+        RvalueExpr(self)
+    }
+}
 
-        if action == DROP_EXISTING {
-            bcx = glue::drop_ty(bcx, dst, self.ty);
-        }
+impl KindOps for Lvalue {
+    fn post_store<'a>(&self,
+                      bcx: &'a Block<'a>,
+                      val: ValueRef,
+                      ty: ty::t)
+                      -> &'a Block<'a> {
+        /*!
+         * If an lvalue is moved, we must zero out the memory in which
+         * it resides so as to cancel cleanup. If an @T lvalue is
+         * copied, we must increment the reference count.
+         */
 
-        match self.mode {
-            ByValue => {
-                Store(bcx, self.val, dst);
-            }
-            ByRef(_) => {
-                memcpy_ty(bcx, dst, self.val, self.ty);
+        if ty::type_needs_drop(bcx.tcx(), ty) {
+            if ty::type_moves_by_default(bcx.tcx(), ty) {
+                // cancel cleanup of affine values by zeroing out
+                let () = zero_mem(bcx, val, ty);
+                bcx
+            } else {
+                // incr. refcount for @T or newtype'd @T
+                glue::take_ty(bcx, val, ty)
             }
+        } else {
+            bcx
         }
-
-        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.
-    //
-    pub fn move_to<'a>(
-                   &self,
-                   bcx: &'a Block<'a>,
-                   action: CopyAction,
-                   dst: ValueRef)
-                   -> &'a Block<'a> {
-        let _icx = push_ctxt("move_to");
-        let mut bcx = bcx;
+    fn is_by_ref(&self) -> bool {
+        true
+    }
 
-        debug!("move_to(self={}, action={:?}, dst={})",
-               self.to_str(bcx.ccx()), action, bcx.val_to_str(dst));
+    fn to_expr_kind(self) -> Expr {
+        LvalueExpr
+    }
+}
 
-        if ty::type_is_voidish(bcx.tcx(), self.ty) {
-            return bcx;
+impl KindOps for Expr {
+    fn post_store<'a>(&self,
+                      bcx: &'a Block<'a>,
+                      val: ValueRef,
+                      ty: ty::t)
+                      -> &'a Block<'a> {
+        match *self {
+            LvalueExpr => Lvalue.post_store(bcx, val, ty),
+            RvalueExpr(ref r) => r.post_store(bcx, val, ty),
         }
+    }
 
-        if action == DROP_EXISTING {
-            bcx = glue::drop_ty(bcx, dst, self.ty);
+    fn is_by_ref(&self) -> bool {
+        match *self {
+            LvalueExpr => Lvalue.is_by_ref(),
+            RvalueExpr(ref r) => r.is_by_ref()
         }
+    }
 
-        match self.mode {
-            ByRef(_) => {
-                memcpy_ty(bcx, dst, self.val, self.ty);
-            }
-            ByValue => {
-                Store(bcx, self.val, dst);
-            }
-        }
+    fn to_expr_kind(self) -> Expr {
+        self
+    }
+}
 
-        self.cancel_clean(bcx);
+impl Datum<Rvalue> {
+    pub fn add_clean(self,
+                     fcx: &FunctionContext,
+                     scope: cleanup::ScopeId)
+                     -> ValueRef {
+        /*!
+         * Schedules a cleanup for this datum in the given scope.
+         * That means that this datum is no longer an rvalue datum;
+         * hence, this function consumes the datum and returns the
+         * contained ValueRef.
+         */
 
-        return bcx;
+        add_rvalue_clean(self.kind.mode, fcx, scope, self.val, self.ty);
+        self.val
     }
 
-    pub fn add_clean(&self, bcx: &Block) {
+    pub fn to_lvalue_datum_in_scope<'a>(self,
+                                        bcx: &'a Block<'a>,
+                                        name: &str,
+                                        scope: cleanup::ScopeId)
+                                        -> DatumBlock<'a, Lvalue> {
         /*!
-         * Schedules this datum for cleanup in `bcx`.  The datum
-         * must be an rvalue.
+         * Returns an lvalue datum (that is, a by ref datum with
+         * cleanup scheduled). If `self` is not already an lvalue,
+         * cleanup will be scheduled in the temporary scope for `expr_id`.
          */
+        let fcx = bcx.fcx;
 
-        match self.mode {
-            ByValue => {
-                add_clean_temp_immediate(bcx, self.val, self.ty);
+        match self.kind.mode {
+            ByRef => {
+                add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty);
+                DatumBlock(bcx, Datum(self.val, self.ty, Lvalue))
             }
-            ByRef(RevokeClean) => {
-                add_clean_temp_mem(bcx, self.val, self.ty);
+
+            ByValue => {
+                lvalue_scratch_datum(
+                    bcx, self.ty, name, false, scope, self,
+                    |this, bcx, llval| this.store_to(bcx, llval))
             }
-            ByRef(ZeroMem) => {
-                add_clean(bcx, self.val, self.ty)
+        }
+    }
+
+    pub fn to_ref_datum<'a>(self, bcx: &'a Block<'a>) -> DatumBlock<'a, Rvalue> {
+        let mut bcx = bcx;
+        match self.kind.mode {
+            ByRef => DatumBlock(bcx, self),
+            ByValue => {
+                let scratch = rvalue_scratch_datum(bcx, self.ty, "to_ref");
+                bcx = self.store_to(bcx, scratch.val);
+                DatumBlock(bcx, scratch)
             }
         }
     }
 
-    pub fn cancel_clean(&self, bcx: &Block) {
-        if ty::type_needs_drop(bcx.tcx(), self.ty) {
-            match self.mode {
-                ByValue |
-                ByRef(RevokeClean) => {
-                    revoke_clean(bcx, self.val);
-                }
-                ByRef(ZeroMem) => {
-                    // Lvalues which potentially need to be dropped
-                    // must be passed by ref, so that we can zero them
-                    // out.
-                    zero_mem(bcx, self.val, self.ty);
+    pub fn to_appropriate_datum<'a>(self,
+                                    bcx: &'a Block<'a>)
+                                    -> DatumBlock<'a, Rvalue> {
+        match self.appropriate_rvalue_mode(bcx.ccx()) {
+            ByRef => {
+                self.to_ref_datum(bcx)
+            }
+            ByValue => {
+                match self.kind.mode {
+                    ByValue => DatumBlock(bcx, self),
+                    ByRef => {
+                        let llval = load(bcx, self.val, self.ty);
+                        DatumBlock(bcx, Datum(llval, self.ty, Rvalue(ByValue)))
+                    }
                 }
             }
         }
     }
+}
 
-    pub fn to_str(&self, ccx: &CrateContext) -> ~str {
-        format!("Datum \\{ val={}, ty={}, mode={:?} \\}",
-             ccx.tn.val_to_str(self.val),
-             ty_to_str(ccx.tcx, self.ty),
-             self.mode)
+/**
+ * Methods suitable for "expr" datums that could be either lvalues or
+ * rvalues. These include coercions into lvalues/rvalues but also a number
+ * of more general operations. (Some of those operations could be moved to
+ * the more general `impl<K> Datum<K>`, but it's convenient to have them
+ * here since we can `match self.kind` rather than having to implement
+ * generic methods in `KindOps`.)
+ */
+impl Datum<Expr> {
+    fn match_kind<R>(self,
+                     if_lvalue: |Datum<Lvalue>| -> R,
+                     if_rvalue: |Datum<Rvalue>| -> R)
+                     -> R {
+        let Datum { val, ty, kind } = self;
+        match kind {
+            LvalueExpr => if_lvalue(Datum(val, ty, Lvalue)),
+            RvalueExpr(r) => if_rvalue(Datum(val, ty, r)),
+        }
     }
 
-    pub fn to_value_datum(&self, bcx: &Block) -> Datum {
-        /*!
-         *
-         * Yields a by-value 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 {
-            ByValue => *self,
-            ByRef(_) => {
-                Datum {val: self.to_value_llval(bcx), mode: ByValue,
-                       ty: self.ty}
-            }
-        }
+    pub fn is_by_ref(&self) -> bool {
+        self.kind.is_by_ref()
     }
 
-    pub fn to_value_llval(&self, bcx: &Block) -> ValueRef {
+    pub fn assert_lvalue(self, bcx: &Block) -> Datum<Lvalue> {
         /*!
-         *
-         * Yields the value itself. */
+         * Asserts that this datum *is* an lvalue and returns it.
+         */
 
-        if ty::type_is_voidish(bcx.tcx(), self.ty) {
-            C_nil()
-        } else {
-            match self.mode {
-                ByValue => self.val,
-                ByRef(_) => {
-                    if ty::type_is_bool(self.ty) {
-                        LoadRangeAssert(bcx, self.val, 0, 2, lib::llvm::True)
-                    } else {
-                        Load(bcx, self.val)
-                    }
-                }
-            }
-        }
+        self.match_kind(
+            |d| d,
+            |_| bcx.sess().bug("assert_lvalue given rvalue"))
     }
 
-    pub fn to_ref_datum(&self, bcx: &Block) -> Datum {
+    pub fn assert_rvalue(self, bcx: &Block) -> Datum<Rvalue> {
         /*!
-         * 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.
+         * Asserts that this datum *is* an lvalue and returns it.
          */
 
-        match self.mode {
-            ByRef(_) => *self,
-            ByValue => {
-                Datum {val: self.to_ref_llval(bcx), mode: ByRef(RevokeClean),
-                       ty: self.ty}
-            }
-        }
+        self.match_kind(
+            |_| bcx.sess().bug("assert_rvalue given lvalue"),
+            |r| r)
     }
 
-    pub fn to_ref_llval(&self, bcx: &Block) -> ValueRef {
-        match self.mode {
-            ByRef(_) => self.val,
-            ByValue => {
-                if ty::type_is_voidish(bcx.tcx(), self.ty) {
-                    C_null(type_of::type_of(bcx.ccx(), self.ty).ptr_to())
-                } else {
-                    let slot = alloc_ty(bcx, self.ty, "");
-                    // The store created here can be modified through a reference, for example:
-                    //
-                    //     // free the old allocation, and change the pointer to a new allocation
-                    //     fn foo(x: &mut ~u8) {
-                    //         *x = ~5;
-                    //     }
-                    //
-                    //     foo(&mut ~5);
-                    Store(bcx, self.val, slot);
-                    // The old cleanup needs to be cancelled, in order for the destructor to observe
-                    // any changes made through the reference.
-                    self.cancel_clean(bcx);
-                    add_clean_temp_mem(bcx, slot, self.ty);
-                    slot
-                }
+    pub fn store_to_dest<'a>(self,
+                             bcx: &'a Block<'a>,
+                             dest: expr::Dest,
+                             expr_id: ast::NodeId)
+                             -> &'a Block<'a> {
+        match dest {
+            expr::Ignore => {
+                self.add_clean_if_rvalue(bcx, expr_id);
+                bcx
+            }
+            expr::SaveIn(addr) => {
+                self.store_to(bcx, addr)
             }
         }
     }
 
-    pub fn appropriate_mode(&self, ccx: &CrateContext) -> DatumMode {
-        /*! See the `appropriate_mode()` function */
-
-        appropriate_mode(ccx, self.ty)
-    }
-
-    pub fn to_appropriate_llval(&self, bcx: &Block) -> ValueRef {
+    pub fn add_clean_if_rvalue<'a>(self,
+                                   bcx: &'a Block<'a>,
+                                   expr_id: ast::NodeId) {
         /*!
-         *
-         * Yields an llvalue with the `appropriate_mode()`. */
+         * Arranges cleanup for `self` if it is an rvalue. Use when
+         * you are done working with a value that may need drop.
+         */
 
-        match self.appropriate_mode(bcx.ccx()) {
-            ByValue => self.to_value_llval(bcx),
-            ByRef(_) => self.to_ref_llval(bcx)
-        }
+        self.match_kind(
+            |_| { /* Nothing to do, cleanup already arranged */ },
+            |r| {
+                let scope = cleanup::temporary_scope(bcx.tcx(), expr_id);
+                r.add_clean(bcx.fcx, scope);
+            })
     }
 
-    pub fn to_appropriate_datum(&self, bcx: &Block) -> Datum {
+    pub fn clean<'a>(self,
+                     bcx: &'a Block<'a>,
+                     name: &'static str,
+                     expr_id: ast::NodeId)
+                     -> &'a Block<'a> {
         /*!
-         *
-         * Yields a datum with the `appropriate_mode()`. */
-
-        match self.appropriate_mode(bcx.ccx()) {
-            ByValue => self.to_value_datum(bcx),
-            ByRef(_) => self.to_ref_datum(bcx)
-        }
-    }
+         * Ensures that `self` will get cleaned up, if it is not an lvalue
+         * already.
+         */
 
-    pub fn get_element(&self,
-                       bcx: &Block,
-                       ty: ty::t,
-                       source: DatumCleanup,
-                       gep: |ValueRef| -> ValueRef)
-                       -> Datum {
-        let base_val = self.to_ref_llval(bcx);
-        Datum {
-            val: gep(base_val),
-            mode: ByRef(source),
-            ty: ty,
-        }
+        self.to_lvalue_datum(bcx, name, expr_id).bcx
     }
 
-    pub fn drop_val<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> {
-        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)
-        };
+    pub fn to_lvalue_datum<'a>(self,
+                               bcx: &'a Block<'a>,
+                               name: &str,
+                               expr_id: ast::NodeId)
+                               -> DatumBlock<'a, Lvalue> {
+        self.match_kind(
+            |l| DatumBlock(bcx, l),
+            |r| {
+                let scope = cleanup::temporary_scope(bcx.tcx(), expr_id);
+                r.to_lvalue_datum_in_scope(bcx, name, scope)
+            })
     }
 
-    pub fn box_body(&self, bcx: &Block) -> Datum {
+    pub fn to_rvalue_datum<'a>(self,
+                               bcx: &'a Block<'a>,
+                               name: &'static str)
+                               -> DatumBlock<'a, Rvalue> {
         /*!
-         *
-         * 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, header) = match ty::get(self.ty).sty {
-            ty::ty_box(typ) => (typ, true),
-            ty::ty_uniq(typ) => (typ, false),
-            _ => {
-                bcx.tcx().sess.bug(format!(
-                    "box_body() invoked on non-box type {}",
-                    ty_to_str(bcx.tcx(), self.ty)));
-            }
-        };
-
-        if !header {
-            let ptr = self.to_value_llval(bcx);
-            let ty = type_of::type_of(bcx.ccx(), content_ty);
-            let body = PointerCast(bcx, ptr, ty.ptr_to());
-            Datum {val: body, ty: content_ty, mode: ByRef(ZeroMem)}
-        } else { // has a header
-            let ptr = self.to_value_llval(bcx);
-            let body = opaque_box_body(bcx, content_ty, ptr);
-            Datum {val: body, ty: content_ty, mode: ByRef(ZeroMem)}
-        }
-    }
+         * Ensures that we have an rvalue datum (that is, a datum with
+         * no cleanup scheduled).
+         */
 
-    pub fn to_rptr(&self, 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::ReStatic,
-                                      self.ty);
-        Datum {val: llval, ty: rptr_ty, mode: ByValue}
-    }
-
-    /// bcx: Block wherein to generate insns.
-    /// span: Location where deref occurs.
-    /// expr_id: ID of deref expr.
-    /// derefs: Number of times deref'd already.
-    /// is_auto: If true, only deref if auto-derefable.
-    pub fn try_deref<'a>(
-                     &self,
-                     bcx: &'a Block<'a>,
-                     span: Span,
-                     expr_id: ast::NodeId,
-                     derefs: uint,
-                     is_auto: bool)
-                     -> (Option<Datum>, &'a Block<'a>) {
-        debug!("try_deref(expr_id={:?}, derefs={:?}, is_auto={}, self={:?})",
-               expr_id, derefs, is_auto, self.to_str(bcx.ccx()));
-
-        let bcx =
-            write_guard::root_and_write_guard(
-                self, bcx, span, expr_id, derefs);
-
-        match ty::get(self.ty).sty {
-            ty::ty_box(_) | ty::ty_uniq(_) => {
-                return (Some(self.box_body(bcx)), bcx);
-            }
-            ty::ty_ptr(mt) => {
-                if is_auto { // unsafe ptrs are not AUTO-derefable
-                    return (None, bcx);
-                } else {
-                    return (Some(deref_ptr(bcx, self, mt.ty)), bcx);
+        let mut bcx = bcx;
+        self.match_kind(
+            |l| {
+                match l.appropriate_rvalue_mode(bcx.ccx()) {
+                    ByRef => {
+                        let scratch = rvalue_scratch_datum(bcx, l.ty, name);
+                        bcx = l.store_to(bcx, scratch.val);
+                        DatumBlock(bcx, scratch)
+                    }
+                    ByValue => {
+                        let v = load(bcx, l.val, l.ty);
+                        l.kind.post_store(bcx, l.val, l.ty);
+                        DatumBlock(bcx, Datum(v, l.ty, Rvalue(ByValue)))
+                    }
                 }
-            }
-            ty::ty_rptr(_, mt) => {
-                return (Some(deref_ptr(bcx, self, mt.ty)), bcx);
-            }
-            _ => { // not derefable.
-                return (None, bcx);
-            }
-        }
-
-        fn deref_ptr(bcx: &Block, lv: &Datum, ty: ty::t) -> Datum {
-            Datum {
-                val: lv.to_value_llval(bcx),
-                ty: ty,
-                mode: ByRef(ZeroMem)
-            }
-        }
+            },
+            |r| DatumBlock(bcx, r))
     }
 
-    /// expr: The deref expression.
-    pub fn deref<'a>(
-                 &self,
-                 bcx: &'a Block<'a>,
-                 expr: &ast::Expr,
-                 derefs: uint)
-                 -> DatumBlock<'a> {
-        match self.try_deref(bcx, expr.span, expr.id, derefs, false) {
-            (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres },
-            (None, _) => {
-                bcx.ccx().sess.span_bug(expr.span,
-                                        "Cannot deref this expression");
-            }
-        }
+}
+
+/**
+ * Methods suitable only for lvalues. These include the various
+ * operations to extract components out of compound data structures,
+ * such as extracting the field from a struct or a particular element
+ * from an array.
+ */
+impl Datum<Lvalue> {
+    pub fn to_llref(self) -> ValueRef {
+        /*!
+         * Converts a datum into a by-ref value. The datum type must
+         * be one which is always passed by reference.
+         */
+
+        self.val
     }
 
-    pub fn autoderef<'a>(
-                     &self,
-                     bcx: &'a Block<'a>,
-                     span: Span,
-                     expr_id: ast::NodeId,
-                     max: uint)
-                     -> DatumBlock<'a> {
-        let _icx = push_ctxt("autoderef");
-
-        debug!("autoderef(expr_id={}, max={:?}, self={:?})",
-               expr_id, max, self.to_str(bcx.ccx()));
-        let _indenter = indenter();
-
-        let mut datum = *self;
-        let mut derefs = 0u;
-        let mut bcx = bcx;
-        while derefs < max {
-            derefs += 1u;
-            match datum.try_deref(bcx, span, expr_id, derefs, true) {
-                (None, new_bcx) => { bcx = new_bcx; break }
-                (Some(datum_deref), new_bcx) => {
-                    datum = datum_deref;
-                    bcx = new_bcx;
-                }
-            }
+    pub fn get_element(&self,
+                       ty: ty::t,
+                       gep: |ValueRef| -> ValueRef)
+                       -> Datum<Lvalue> {
+        Datum {
+            val: gep(self.val),
+            kind: Lvalue,
+            ty: ty,
         }
-
-        // 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);
-        DatumBlock { bcx: bcx, datum: datum }
     }
 
     pub fn get_vec_base_and_byte_len<'a>(
@@ -723,17 +549,15 @@ impl Datum {
         //! Converts a vector into the slice pair. Des not root
         //! nor perform write guard checks.
 
-        let llval = self.to_appropriate_llval(bcx);
-        tvec::get_base_and_byte_len(bcx, llval, self.ty)
+        tvec::get_base_and_byte_len(bcx, self.val, self.ty)
     }
 
-    pub fn get_vec_base_and_len<'a>(
-                                &self,
-                                mut bcx: &'a Block<'a>,
-                                span: Span,
-                                expr_id: ast::NodeId,
-                                derefs: uint)
-                                -> (&'a Block<'a>, ValueRef, ValueRef) {
+    pub fn get_vec_base_and_len<'a>(&self,
+                                    mut bcx: &'a Block<'a>,
+                                    span: Span,
+                                    expr_id: ast::NodeId,
+                                    derefs: uint)
+                                    -> (&'a Block<'a>, ValueRef, ValueRef) {
         //! Converts a vector into the slice pair. Performs rooting
         //! and write guards checks.
 
@@ -744,12 +568,118 @@ impl Datum {
     }
 
     pub fn get_vec_base_and_len_no_root<'a>(&self, bcx: &'a Block<'a>)
-                                        -> (ValueRef, ValueRef) {
+                                            -> (ValueRef, ValueRef) {
         //! Converts a vector into the slice pair. Des not root
         //! nor perform write guard checks.
 
-        let llval = self.to_appropriate_llval(bcx);
-        tvec::get_base_and_len(bcx, llval, self.ty)
+        tvec::get_base_and_len(bcx, self.val, self.ty)
+    }
+}
+
+fn load<'a>(bcx: &'a Block<'a>, llptr: ValueRef, ty: ty::t) -> ValueRef {
+    /*!
+     * Private helper for loading from a by-ref datum. Handles various
+     * special cases where the type gives us better information about
+     * what we are loading.
+     */
+
+    if type_is_zero_size(bcx.ccx(), ty) {
+        C_undef(type_of::type_of(bcx.ccx(), ty))
+    } else if ty::type_is_bool(ty) {
+        LoadRangeAssert(bcx, llptr, 0, 2, lib::llvm::True)
+    } else {
+        Load(bcx, llptr)
+    }
+}
+
+/**
+ * Generic methods applicable to any sort of datum.
+ */
+impl<K:KindOps> Datum<K> {
+    pub fn to_expr_datum(self) -> Datum<Expr> {
+        let Datum { val, ty, kind } = self;
+        Datum { val: val, ty: ty, kind: kind.to_expr_kind() }
+    }
+
+    pub fn store_to<'a>(self,
+                        bcx: &'a Block<'a>,
+                        dst: ValueRef)
+                        -> &'a Block<'a> {
+        /*!
+         * Moves or copies this value into a new home, as appropriate
+         * depending on the type of the datum. This method consumes
+         * the datum, since it would be incorrect to go on using the
+         * datum if the value represented is affine (and hence the value
+         * is moved).
+         */
+
+        self.shallow_copy(bcx, dst);
+
+        self.kind.post_store(bcx, self.val, self.ty)
+    }
+
+    fn shallow_copy<'a>(&self,
+                        bcx: &'a Block<'a>,
+                        dst: ValueRef)
+                        -> &'a Block<'a> {
+        /*!
+         * Helper function that performs a shallow copy of this value
+         * into `dst`, which should be a pointer to a memory location
+         * suitable for `self.ty`. `dst` should contain uninitialized
+         * memory (either newly allocated, zeroed, or dropped).
+         *
+         * This function is private to datums because it leaves memory
+         * in an unstable state, where the source value has been
+         * copied but not zeroed. Public methods are `store_to` (if
+         * you no longer need the source value) or
+         * `shallow_copy_and_take` (if you wish the source value to
+         * remain valid).
+         */
+
+        let _icx = push_ctxt("copy_to_no_check");
+
+        if type_is_zero_size(bcx.ccx(), self.ty) {
+            return bcx;
+        }
+
+        if self.kind.is_by_ref() {
+            memcpy_ty(bcx, dst, self.val, self.ty);
+        } else {
+            Store(bcx, self.val, dst);
+        }
+
+        return bcx;
+    }
+
+    pub fn shallow_copy_and_take<'a>(&self,
+                                     bcx: &'a Block<'a>,
+                                     dst: ValueRef)
+                                     -> &'a Block<'a> {
+        /*!
+         * Copies the value into a new location and runs any necessary
+         * take glue on the new location. This function always
+         * preserves the existing datum as a valid value. Therefore,
+         * it does not consume `self` and, also, cannot be applied to
+         * affine values (since they must never be duplicated).
+         */
+
+        assert!(!ty::type_moves_by_default(bcx.tcx(), self.ty));
+        let mut bcx = bcx;
+        bcx = self.shallow_copy(bcx, dst);
+        glue::take_ty(bcx, dst, self.ty)
+    }
+
+    pub fn to_str(&self, ccx: &CrateContext) -> ~str {
+        format!("Datum({}, {}, {:?})",
+             ccx.tn.val_to_str(self.val),
+             ty_to_str(ccx.tcx, self.ty),
+             self.kind)
+    }
+
+    pub fn appropriate_rvalue_mode(&self, ccx: &CrateContext) -> RvalueMode {
+        /*! See the `appropriate_rvalue_mode()` function */
+
+        appropriate_rvalue_mode(ccx, self.ty)
     }
 
     pub fn root_and_write_guard<'a>(
@@ -762,47 +692,58 @@ impl Datum {
         write_guard::root_and_write_guard(self, bcx, span, expr_id, derefs)
     }
 
-    pub fn to_result<'a>(&self, bcx: &'a Block<'a>) -> common::Result<'a> {
-        rslt(bcx, self.to_appropriate_llval(bcx))
-    }
-}
-
-impl<'a> DatumBlock<'a> {
-    pub fn unpack(&self, bcx: &mut &'a Block<'a>) -> Datum {
-        *bcx = self.bcx;
-        return self.datum;
-    }
+    pub fn to_llscalarish<'a>(self, bcx: &'a Block<'a>) -> ValueRef {
+        /*!
+         * Converts `self` into a by-value `ValueRef`. Consumes this
+         * datum (i.e., absolves you of responsibility to cleanup the
+         * value). For this to work, the value must be something
+         * scalar-ish (like an int or a pointer) which (1) does not
+         * require drop glue and (2) is naturally passed around by
+         * value, and not by reference.
+         */
 
-    pub fn assert_by_ref(&self) -> DatumBlock<'a> {
-        assert!(self.datum.mode.is_by_ref());
-        *self
+        assert!(!ty::type_needs_drop(bcx.tcx(), self.ty));
+        assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue);
+        if self.kind.is_by_ref() {
+            load(bcx, self.val, self.ty)
+        } else {
+            self.val
+        }
     }
 
-    pub fn drop_val(&self) -> &'a Block<'a> {
-        self.datum.drop_val(self.bcx)
+    pub fn to_llbool<'a>(self, bcx: &'a Block<'a>) -> ValueRef {
+        assert!(ty::type_is_bool(self.ty) || ty::type_is_bot(self.ty))
+        let cond_val = self.to_llscalarish(bcx);
+        bool_to_i1(bcx, cond_val)
     }
+}
 
-    pub fn store_to(&self, action: CopyAction, dst: ValueRef)
-                    -> &'a Block<'a> {
-        self.datum.store_to(self.bcx, action, dst)
+impl<'a, K:KindOps> DatumBlock<'a, K> {
+    pub fn to_expr_datumblock(self) -> DatumBlock<'a, Expr> {
+        DatumBlock(self.bcx, self.datum.to_expr_datum())
     }
+}
 
-    pub fn copy_to(&self, action: CopyAction, dst: ValueRef)
-                   -> &'a Block<'a> {
-        self.datum.copy_to(self.bcx, action, dst)
+impl<'a> DatumBlock<'a, Expr> {
+    pub fn assert_by_ref(self) -> DatumBlock<'a, Expr> {
+        assert!(self.datum.kind.is_by_ref());
+        self
     }
 
-    pub fn move_to(&self, action: CopyAction, dst: ValueRef)
-                   -> &'a Block<'a> {
-        self.datum.move_to(self.bcx, action, dst)
+    pub fn store_to(self, dst: ValueRef) -> &'a Block<'a> {
+        let DatumBlock { bcx, datum } = self;
+        datum.store_to(bcx, dst)
     }
 
-    pub fn to_value_llval(&self) -> ValueRef {
-        self.datum.to_value_llval(self.bcx)
+    pub fn store_to_dest(self,
+                         dest: expr::Dest,
+                         expr_id: ast::NodeId) -> &'a Block<'a> {
+        let DatumBlock { bcx, datum } = self;
+        datum.store_to_dest(bcx, dest, expr_id)
     }
 
-    pub fn to_result(&self) -> common::Result<'a> {
-        rslt(self.bcx, self.datum.to_appropriate_llval(self.bcx))
+    pub fn shallow_copy(self, dst: ValueRef) -> &'a Block<'a> {
+        self.datum.shallow_copy(self.bcx, dst)
     }
 
     pub fn ccx(&self) -> @CrateContext {
@@ -816,4 +757,9 @@ impl<'a> DatumBlock<'a> {
     pub fn to_str(&self) -> ~str {
         self.datum.to_str(self.ccx())
     }
+
+    pub fn to_llbool(self) -> Result<'a> {
+        let DatumBlock { datum, bcx } = self;
+        rslt(bcx, datum.to_llbool(bcx))
+    }
 }
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 4e8b3c78dc8..88f38d9f4fd 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -133,7 +133,7 @@ use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::build;
 use middle::trans::common::*;
-use middle::trans::datum;
+use middle::trans::datum::{Datum, Lvalue};
 use middle::trans::machine;
 use middle::trans::type_of;
 use middle::trans::type_::Type;
@@ -396,7 +396,7 @@ pub fn create_match_binding_metadata(bcx: &Block,
                                      variable_ident: ast::Ident,
                                      node_id: ast::NodeId,
                                      span: Span,
-                                     datum: datum::Datum) {
+                                     datum: Datum<Lvalue>) {
     if fn_should_be_ignored(bcx.fcx) {
         return;
     }
diff --git a/src/librustc/middle/trans/doc.rs b/src/librustc/middle/trans/doc.rs
new file mode 100644
index 00000000000..b44f2ba2008
--- /dev/null
+++ b/src/librustc/middle/trans/doc.rs
@@ -0,0 +1,237 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+
+# Documentation for the trans module
+
+This module contains high-level summaries of how the various modules
+in trans work. It is a work in progress. For detailed comments,
+naturally, you can refer to the individual modules themselves.
+
+## The Expr module
+
+The expr module handles translation of expressions. The most general
+translation routine is `trans()`, which will translate an expression
+into a datum. `trans_into()` is also available, which will translate
+an expression and write the result directly into memory, sometimes
+avoiding the need for a temporary stack slot. Finally,
+`trans_to_lvalue()` is available if you'd like to ensure that the
+result has cleanup scheduled.
+
+Internally, each of these functions dispatches to various other
+expression functions depending on the kind of expression. We divide
+up expressions into:
+
+- **Datum expressions:** Those that most naturally yield values.
+  Examples would be `22`, `~x`, or `a + b` (when not overloaded).
+- **DPS expressions:** Those that most naturally write into a location
+  in memory. Examples would be `foo()` or `Point { x: 3, y: 4 }`.
+- **Statement expressions:** That that do not generate a meaningful
+  result. Examples would be `while { ... }` or `return 44`.
+
+## The Datum module
+
+A `Datum` encapsulates the result of evaluating a Rust expression.  It
+contains a `ValueRef` indicating the result, a `ty::t` describing the
+the Rust type, but also a *kind*. The kind indicates whether the datum
+has cleanup scheduled (lvalue) or not (rvalue) and -- in the case of
+rvalues -- whether or not the value is "by ref" or "by value".
+
+The datum API is designed to try and help you avoid memory errors like
+forgetting to arrange cleanup or duplicating a value. The type of the
+datum incorporates the kind, and thus reflects whether it has cleanup
+scheduled:
+
+- `Datum<Lvalue>` -- by ref, cleanup scheduled
+- `Datum<Rvalue>` -- by value or by ref, no cleanup scheduled
+- `Datum<Expr>` -- either `Datum<Lvalue>` or `Datum<Rvalue>`
+
+Rvalue and expr datums are noncopyable, and most of the methods on
+datums consume the datum itself (with some notable exceptions). This
+reflects the fact that datums may represent affine values which ought
+to be consumed exactly once, and if you were to try to (for example)
+store an affine value multiple times, you would be duplicating it,
+which would certainly be a bug.
+
+Some of the datum methods, however, are designed to work only on
+copyable values such as ints or pointers. Those methods may borrow the
+datum (`&self`) rather than consume it, but they always include
+assertions on the type of the value represented to check that this
+makes sense. An example is `shallow_copy_and_take()`, which duplicates
+a datum value.
+
+Translating an expression always yields a `Datum<Expr>` result, but
+the methods `to_[lr]value_datum()` can be used to coerce a
+`Datum<Expr>` into a `Datum<Lvalue>` or `Datum<Rvalue>` as
+needed. Coercing to an lvalue is fairly common, and generally occurs
+whenever it is necessary to inspect a value and pull out its
+subcomponents (for example, a match, or indexing expression). Coercing
+to an rvalue is more unusual; it occurs when moving values from place
+to place, such as in an assignment expression or parameter passing.
+
+### Lvalues in detail
+
+An lvalue datum is one for which cleanup has been scheduled. Lvalue
+datums are always located in memory, and thus the `ValueRef` for an
+LLVM value is always a pointer to the actual Rust value. This means
+that if the Datum has a Rust type of `int`, then the LLVM type of the
+`ValueRef` will be `int*` (pointer to int).
+
+Because lvalues already have cleanups scheduled, the memory must be
+zeroed to prevent the cleanup from taking place (presuming that the
+Rust type needs drop in the first place, otherwise it doesn't
+matter). The Datum code automatically performs this zeroing when the
+value is stored to a new location, for example.
+
+Lvalues usually result from evaluating lvalue expressions. For
+example, evaluating a local variable `x` yields an lvalue, as does a
+reference to a field like `x.f` or an index `x[i]`.
+
+Lvalue datums can also arise by *converting* an rvalue into an lvalue.
+This is done with the `to_lvalue_datum` method defined on
+`Datum<Expr>`. Basically this method just schedules cleanup if the
+datum is an rvalue, possibly storing the value into a stack slot first
+if needed. Converting rvalues into lvalues occurs in constructs like
+`&foo()` or `match foo() { ref x => ... }`, where the user is
+implicitly requesting a temporary.
+
+Somewhat surprisingly, not all lvalue expressions yield lvalue datums
+when trans'd. Ultimately the reason for this is to micro-optimize
+the resulting LLVM. For example, consider the following code:
+
+    fn foo() -> ~int { ... }
+    let x = *foo();
+
+The expression `*foo()` is an lvalue, but if you invoke `expr::trans`,
+it will return an rvalue datum. See `deref_once` in expr.rs for
+more details.
+
+### Rvalues in detail
+
+Rvalues datums are values with no cleanup scheduled. One must be
+careful with rvalue datums to ensure that cleanup is properly
+arranged, usually by converting to an lvalue datum or by invoking the
+`add_clean` method.
+
+### Scratch datums
+
+Sometimes you need some temporary scratch space.  The functions
+`[lr]value_scratch_datum()` can be used to get temporary stack
+space. As their name suggests, they yield lvalues and rvalues
+respectively. That is, the slot from `lvalue_scratch_datum` will have
+cleanup arranged, and the slot from `rvalue_scratch_datum` does not.
+
+## The Cleanup module
+
+The cleanup module tracks what values need to be cleaned up as scopes
+are exited, either via failure or just normal control flow. The basic
+idea is that the function context maintains a stack of cleanup scopes
+that are pushed/popped as we traverse the AST tree. There is typically
+at least one cleanup scope per AST node; some AST nodes may introduce
+additional temporary scopes.
+
+Cleanup items can be scheduled into any of the scopes on the stack.
+Typically, when a scope is popped, we will also generate the code for
+each of its cleanups at that time. This corresponds to a normal exit
+from a block (for example, an expression completing evaluation
+successfully without failure). However, it is also possible to pop a
+block *without* executing its cleanups; this is typically used to
+guard intermediate values that must be cleaned up on failure, but not
+if everything goes right. See the section on custom scopes below for
+more details.
+
+Cleanup scopes come in three kinds:
+- **AST scopes:** each AST node in a function body has a corresponding
+  AST scope. We push the AST scope when we start generate code for an AST
+  node and pop it once the AST node has been fully generated.
+- **Loop scopes:** loops have an additional cleanup scope. Cleanups are
+  never scheduled into loop scopes; instead, they are used to record the
+  basic blocks that we should branch to when a `continue` or `break` statement
+  is encountered.
+- **Custom scopes:** custom scopes are typically used to ensure cleanup
+  of intermediate values.
+
+### When to schedule cleanup
+
+Although the cleanup system is intended to *feel* fairly declarative,
+it's still important to time calls to `schedule_clean()` correctly.
+Basically, you should not schedule cleanup for memory until it has
+been initialized, because if an unwind should occur before the memory
+is fully initialized, then the cleanup will run and try to free or
+drop uninitialized memory. If the initialization itself produces
+byproducts that need to be freed, then you should use temporary custom
+scopes to ensure that those byproducts will get freed on unwind.  For
+example, an expression like `~foo()` will first allocate a box in the
+heap and then call `foo()` -- if `foo()` should fail, this box needs
+to be *shallowly* freed.
+
+### Long-distance jumps
+
+In addition to popping a scope, which corresponds to normal control
+flow exiting the scope, we may also *jump out* of a scope into some
+earlier scope on the stack. This can occur in response to a `return`,
+`break`, or `continue` statement, but also in response to failure. In
+any of these cases, we will generate a series of cleanup blocks for
+each of the scopes that is exited. So, if the stack contains scopes A
+... Z, and we break out of a loop whose corresponding cleanup scope is
+X, we would generate cleanup blocks for the cleanups in X, Y, and Z.
+After cleanup is done we would branch to the exit point for scope X.
+But if failure should occur, we would generate cleanups for all the
+scopes from A to Z and then resume the unwind process afterwards.
+
+To avoid generating tons of code, we cache the cleanup blocks that we
+create for breaks, returns, unwinds, and other jumps. Whenever a new
+cleanup is scheduled, though, we must clear these cached blocks. A
+possible improvement would be to keep the cached blocks but simply
+generate a new block which performs the additional cleanup and then
+branches to the existing cached blocks.
+
+### AST and loop cleanup scopes
+
+AST cleanup scopes are pushed when we begin and end processing an AST
+node. They are used to house cleanups related to rvalue temporary that
+get referenced (e.g., due to an expression like `&Foo()`). Whenever an
+AST scope is popped, we always trans all the cleanups, adding the cleanup
+code after the postdominator of the AST node.
+
+AST nodes that represent breakable loops also push a loop scope; the
+loop scope never has any actual cleanups, it's just used to point to
+the basic blocks where control should flow after a "continue" or
+"break" statement. Popping a loop scope never generates code.
+
+### Custom cleanup scopes
+
+Custom cleanup scopes are used for a variety of purposes. The most
+common though is to handle temporary byproducts, where cleanup only
+needs to occur on failure. The general strategy is to push a custom
+cleanup scope, schedule *shallow* cleanups into the custom scope, and
+then pop the custom scope (without transing the cleanups) when
+execution succeeds normally. This way the cleanups are only trans'd on
+unwind, and only up until the point where execution succeeded, at
+which time the complete value should be stored in an lvalue or some
+other place where normal cleanup applies.
+
+To spell it out, here is an example. Imagine an expression `~expr`.
+We would basically:
+
+1. Push a custom cleanup scope C.
+2. Allocate the `~` box.
+3. Schedule a shallow free in the scope C.
+4. Trans `expr` into the box.
+5. Pop the scope C.
+6. Return the box as an rvalue.
+
+This way, if a failure occurs while transing `expr`, the custom
+cleanup scope C is pushed and hence the box will be freed. The trans
+code for `expr` itself is responsible for freeing any other byproducts
+that may be in play.
+
+*/
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 72fc3e80228..36fc927b64c 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -9,110 +9,27 @@
 // except according to those terms.
 
 /*!
-
-# Translation of expressions.
-
-## Recommended entry point
-
-If you wish to translate an expression, the preferred way to do
-so is to use:
-
-    expr::trans_into(block, expr, Dest) -> block
-
-This will generate code that evaluates `expr`, storing the 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.  It returns the resulting basic block.  This form will
-handle all automatic adjustments for you. The value will be moved if
-its type is linear and copied otherwise.
-
-## Translation to a datum
-
-In some cases, `trans_into()` is too narrow of an interface.
-Generally this occurs either when you know that the result value is
-going to be a scalar, or when you need to evaluate the expression into
-some memory location so you can go and inspect it (e.g., assignments,
-`match` expressions, the `&` operator).
-
-In such cases, you want the following function:
-
-    trans_to_datum(block, expr) -> DatumBlock
-
-This function generates code to 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.  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).
-
-NB: The `trans_to_datum()` function does perform adjustments, but
-since it returns a pointer to the value "in place" it does not handle
-moves.  If you wish to copy/move the value returned into a new
-location, you should use the Datum method `store_to()` (move or copy
-depending on type). You can also use `move_to()` (force move) or
-`copy_to()` (force copy) for special situations.
-
-## Translating local variables
-
-`trans_local_var()` can be used to trans a ref to a local variable
-that is not an expression.  This is needed for captures.
-
-## 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()` 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`).
-
-*/
-
+ * # Translation of Expressions
+ *
+ * Public entry points:
+ *
+ * - `trans_into(bcx, expr, dest) -> bcx`: evaluates an expression,
+ *   storing the result into `dest`. This is the preferred form, if you
+ *   can manage it.
+ *
+ * - `trans(bcx, expr) -> DatumBlock`: evaluates an expression, yielding
+ *   `Datum` with the result. You can then store the datum, inspect
+ *   the value, etc. This may introduce temporaries if the datum is a
+ *   structural type.
+ *
+ * - `trans_to_lvalue(bcx, expr, "...") -> DatumBlock`: evaluates an
+ *   expression and ensures that the result has a cleanup associated with it,
+ *   creating a temporary stack slot if necessary.
+ *
+ * - `trans_local_var -> Datum`: looks up a local variable or upvar.
+ *
+ * See doc.rs for more comments.
+ */
 
 use back::abi;
 use back::link;
@@ -127,17 +44,21 @@ use middle::trans::base;
 use middle::trans::build::*;
 use middle::trans::callee::DoAutorefArg;
 use middle::trans::callee;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::closure;
 use middle::trans::common::*;
 use middle::trans::consts;
 use middle::trans::controlflow;
 use middle::trans::datum::*;
 use middle::trans::debuginfo;
+use middle::trans::glue;
 use middle::trans::machine;
 use middle::trans::meth;
 use middle::trans::inline;
 use middle::trans::tvec;
 use middle::trans::type_of;
+use middle::trans::write_guard;
 use middle::ty::struct_fields;
 use middle::ty::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe};
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn};
@@ -175,31 +96,102 @@ impl Dest {
     }
 }
 
-pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
-                      -> DatumBlock<'a> {
-    debug!("trans_to_datum(expr={})", bcx.expr_to_str(expr));
+pub fn trans_into<'a>(bcx: &'a Block<'a>,
+                      expr: &ast::Expr,
+                      dest: Dest)
+                      -> &'a Block<'a> {
+    /*!
+     * This function is equivalent to `trans(bcx, expr).store_to_dest(dest)`
+     * but it may generate better optimized LLVM code.
+     */
+
+    let mut bcx = bcx;
+
+    let is_adjusted = {
+        let adjustments = bcx.tcx().adjustments.borrow();
+        adjustments.get().contains_key(&expr.id)
+    };
+
+    if is_adjusted {
+        // use trans, which may be less efficient but
+        // which will perform the adjustments:
+        let datum = unpack_datum!(bcx, trans(bcx, expr));
+        return datum.store_to_dest(bcx, dest, expr.id)
+    }
+
+    debug!("trans_into() expr={}", expr.repr(bcx.tcx()));
+    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
+
+    bcx.fcx.push_ast_cleanup_scope(expr.id);
+
+    let kind = ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr);
+    bcx = match kind {
+        ty::LvalueExpr | ty::RvalueDatumExpr => {
+            trans_unadjusted(bcx, expr).store_to_dest(dest, expr.id)
+        }
+        ty::RvalueDpsExpr => {
+            trans_rvalue_dps_unadjusted(bcx, expr, dest)
+        }
+        ty::RvalueStmtExpr => {
+            trans_rvalue_stmt_unadjusted(bcx, expr)
+        }
+    };
+
+    bcx.fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id)
+}
+
+pub fn trans<'a>(bcx: &'a Block<'a>,
+                 expr: &ast::Expr)
+                 -> DatumBlock<'a, Expr> {
+    /*!
+     * Translates an expression, returning a datum (and new block)
+     * encapsulating the result. When possible, it is preferred to
+     * use `trans_into`, as that may avoid creating a temporary on
+     * the stack.
+     */
+
+    debug!("trans(expr={})", bcx.expr_to_str(expr));
 
     let mut bcx = bcx;
-    let mut datum = unpack_datum!(bcx, trans_to_datum_unadjusted(bcx, expr));
+    let fcx = bcx.fcx;
+
+    fcx.push_ast_cleanup_scope(expr.id);
+    let datum = unpack_datum!(bcx, trans_unadjusted(bcx, expr));
+    let datum = unpack_datum!(bcx, apply_adjustments(bcx, expr, datum));
+    bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id);
+    return DatumBlock(bcx, datum);
+}
+
+fn apply_adjustments<'a>(bcx: &'a Block<'a>,
+                         expr: &ast::Expr,
+                         datum: Datum<Expr>)
+                         -> DatumBlock<'a, Expr> {
+    /*!
+     * Helper for trans that apply adjustments from `expr` to `datum`,
+     * which should be the unadjusted translation of `expr`.
+     */
+
+    let mut bcx = bcx;
+    let mut datum = datum;
     let adjustment = {
         let adjustments = bcx.tcx().adjustments.borrow();
         match adjustments.get().find_copy(&expr.id) {
-            None => { return DatumBlock {bcx: bcx, datum: datum}; }
+            None => {
+                return DatumBlock(bcx, datum);
+            }
             Some(adj) => { adj }
         }
     };
-    debug!("unadjusted datum: {}", datum.to_str(bcx.ccx()));
+    debug!("unadjusted datum for expr {}: {}",
+           expr.id, datum.to_str(bcx.ccx()));
     match *adjustment {
         AutoAddEnv(..) => {
             datum = unpack_datum!(bcx, add_env(bcx, expr, datum));
         }
         AutoDerefRef(ref adj) => {
             if adj.autoderefs > 0 {
-                datum =
-                    unpack_datum!(
-                        bcx,
-                        datum.autoderef(bcx, expr.span,
-                                        expr.id, adj.autoderefs));
+                datum = unpack_datum!(
+                    bcx, deref_multiple(bcx, expr, datum, adj.autoderefs));
             }
 
             datum = match adj.autoref {
@@ -208,7 +200,7 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
                 }
                 Some(AutoUnsafe(..)) | // region + unsafe ptrs have same repr
                 Some(AutoPtr(..)) => {
-                    unpack_datum!(bcx, auto_ref(bcx, datum))
+                    unpack_datum!(bcx, auto_ref(bcx, datum, expr))
                 }
                 Some(AutoBorrowVec(..)) => {
                     unpack_datum!(bcx, auto_slice(bcx, adj.autoderefs,
@@ -230,51 +222,75 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
             };
         }
         AutoObject(..) => {
-
             let adjusted_ty = ty::expr_ty_adjusted(bcx.tcx(), expr);
-            let scratch = scratch_datum(bcx, adjusted_ty, "__adjust", false);
-
-            bcx = meth::trans_trait_cast(bcx, expr, expr.id, SaveIn(scratch.val), Some(datum));
-
-            datum = scratch.to_appropriate_datum(bcx);
-            datum.add_clean(bcx);
+            let scratch = rvalue_scratch_datum(bcx, adjusted_ty, "__adjust");
+            bcx = meth::trans_trait_cast(
+                bcx, datum, expr.id, SaveIn(scratch.val));
+            datum = scratch.to_expr_datum();
         }
     }
     debug!("after adjustments, datum={}", datum.to_str(bcx.ccx()));
     return DatumBlock {bcx: bcx, datum: datum};
 
-    fn auto_ref<'a>(bcx: &'a Block<'a>, datum: Datum) -> DatumBlock<'a> {
-        DatumBlock {bcx: bcx, datum: datum.to_rptr(bcx)}
+    fn auto_ref<'a>(bcx: &'a Block<'a>,
+                    datum: Datum<Expr>,
+                    expr: &ast::Expr)
+                    -> DatumBlock<'a, Expr> {
+        let mut bcx = bcx;
+
+        // Ensure cleanup of `datum` if not already scheduled and obtain
+        // a "by ref" pointer.
+        let lv_datum = unpack_datum!(bcx, datum.to_lvalue_datum(bcx, "autoref", expr.id));
+
+        // Compute final type. Note that we are loose with the region and
+        // mutability, since those things don't matter in trans.
+        let referent_ty = lv_datum.ty;
+        let ptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::ReStatic, referent_ty);
+
+        // Get the pointer.
+        let llref = lv_datum.to_llref();
+
+        // Construct the resulting datum, using what was the "by ref"
+        // ValueRef of type `referent_ty` to be the "by value" ValueRef
+        // of type `&referent_ty`.
+        DatumBlock(bcx, Datum(llref, ptr_ty, RvalueExpr(Rvalue(ByValue))))
     }
 
     fn auto_borrow_fn<'a>(
                       bcx: &'a Block<'a>,
                       adjusted_ty: ty::t,
-                      datum: Datum)
-                      -> DatumBlock<'a> {
+                      datum: Datum<Expr>)
+                      -> DatumBlock<'a, Expr> {
         // Currently, all closure types are represented precisely the
         // same, so no runtime adjustment is required, but we still
         // must patchup the type.
         DatumBlock {bcx: bcx,
-                    datum: Datum {val: datum.val, ty: adjusted_ty,
-                                  mode: datum.mode}}
+                    datum: Datum {val: datum.val,
+                                  ty: adjusted_ty,
+                                  kind: datum.kind}}
     }
 
     fn auto_slice<'a>(
                   bcx: &'a Block<'a>,
                   autoderefs: uint,
                   expr: &ast::Expr,
-                  datum: Datum)
-                  -> DatumBlock<'a> {
+                  datum: Datum<Expr>)
+                  -> DatumBlock<'a, Expr> {
         // This is not the most efficient thing possible; since slices
         // are two words it'd be better if this were compiled in
         // 'dest' mode, but I can't find a nice way to structure the
         // code and keep it DRY that accommodates that use case at the
         // moment.
 
+        let mut bcx = bcx;
         let tcx = bcx.tcx();
         let unit_ty = ty::sequence_element_type(tcx, datum.ty);
 
+        // Arrange cleanup, if not already done. This is needed in
+        // case we are auto-slicing an owned vector or some such.
+        let datum = unpack_datum!(
+            bcx, datum.to_lvalue_datum(bcx, "auto_slice", expr.id));
+
         let (bcx, base, len) =
             datum.get_vec_base_and_len(bcx, expr.span, expr.id, autoderefs+1);
 
@@ -284,15 +300,16 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
                                   ty::mt { ty: unit_ty, mutbl: ast::MutImmutable },
                                   ty::vstore_slice(ty::ReStatic));
 
-        let scratch = scratch_datum(bcx, slice_ty, "__adjust", false);
-
+        let scratch = rvalue_scratch_datum(bcx, slice_ty, "__adjust");
         Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
         Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
-        DatumBlock {bcx: bcx, datum: scratch}
+        DatumBlock(bcx, scratch.to_expr_datum())
     }
 
-    fn add_env<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, datum: Datum)
-               -> DatumBlock<'a> {
+    fn add_env<'a>(bcx: &'a Block<'a>,
+                   expr: &ast::Expr,
+                   datum: Datum<Expr>)
+                   -> DatumBlock<'a, Expr> {
         // This is not the most efficient thing possible; since closures
         // are two words it'd be better if this were compiled in
         // 'dest' mode, but I can't find a nice way to structure the
@@ -302,31 +319,31 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
         let tcx = bcx.tcx();
         let closure_ty = expr_ty_adjusted(bcx, expr);
         debug!("add_env(closure_ty={})", closure_ty.repr(tcx));
-        let scratch = scratch_datum(bcx, closure_ty, "__adjust", false);
+        let scratch = rvalue_scratch_datum(bcx, closure_ty, "__adjust");
         let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]);
-        assert_eq!(datum.appropriate_mode(bcx.ccx()), ByValue);
-        Store(bcx, datum.to_appropriate_llval(bcx), llfn);
+        let llval = datum.to_llscalarish(bcx);
+        Store(bcx, llval, llfn);
         let llenv = GEPi(bcx, scratch.val, [0u, abi::fn_field_box]);
         Store(bcx, base::null_env_ptr(bcx.ccx()), llenv);
-        DatumBlock {bcx: bcx, datum: scratch}
+        DatumBlock(bcx, scratch.to_expr_datum())
     }
 
     fn auto_slice_and_ref<'a>(
                           bcx: &'a Block<'a>,
                           autoderefs: uint,
                           expr: &ast::Expr,
-                          datum: Datum)
-                          -> DatumBlock<'a> {
+                          datum: Datum<Expr>)
+                          -> DatumBlock<'a, Expr> {
         let DatumBlock { bcx, datum } = auto_slice(bcx, autoderefs, expr, datum);
-        auto_ref(bcx, datum)
+        auto_ref(bcx, datum, expr)
     }
 
     fn auto_borrow_obj<'a>(
                        mut bcx: &'a Block<'a>,
                        autoderefs: uint,
                        expr: &ast::Expr,
-                       source_datum: Datum)
-                       -> DatumBlock<'a> {
+                       source_datum: Datum<Expr>)
+                       -> DatumBlock<'a, Expr> {
         let tcx = bcx.tcx();
         let target_obj_ty = expr_ty_adjusted(bcx, expr);
         debug!("auto_borrow_obj(target={})",
@@ -343,7 +360,8 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
             }
         };
 
-        // check if any borrowing is really needed or we could reuse the source_datum instead
+        // check if any borrowing is really needed or we could reuse
+        // the source_datum instead
         match ty::get(target_obj_ty).sty {
             ty::ty_trait(_, _, ty::RegionTraitStore(target_scope), target_mutbl, _) => {
                 if target_mutbl == ast::MutImmutable && target_mutbl == source_mutbl {
@@ -361,14 +379,16 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
             _ => {}
         }
 
-        let scratch = scratch_datum(bcx, target_obj_ty,
-                                    "__auto_borrow_obj", false);
+        let scratch = rvalue_scratch_datum(bcx, target_obj_ty,
+                                           "__auto_borrow_obj");
 
         // Convert a @Object, ~Object, or &Object pair into an &Object pair.
 
         // Get a pointer to the source object, which is represented as
         // a (vtable, data) pair.
-        let source_llval = source_datum.to_ref_llval(bcx);
+        let source_datum = unpack_datum!(
+            bcx, source_datum.to_lvalue_datum(bcx, "auto_borrow_obj", expr.id));
+        let source_llval = source_datum.to_llref();
 
         // Set the vtable field of the new pair
         let vtable_ptr = GEPi(bcx, source_llval, [0u, abi::trt_field_vtable]);
@@ -378,172 +398,87 @@ pub fn trans_to_datum<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
         // Load the data for the source, which is either an @T,
         // ~T, or &T, depending on source_obj_ty.
         let source_data_ptr = GEPi(bcx, source_llval, [0u, abi::trt_field_box]);
-        let source_data = Load(bcx, source_data_ptr); // always a ptr
         let target_data = match source_store {
             ty::BoxTraitStore(..) => {
-                // For deref of @T, create a dummy datum and use the datum's
-                // deref method. This is more work than just calling GEPi
-                // ourselves. Note that we don't know the type T, so
-                // just substitute `i8`-- it doesn't really matter for
-                // our purposes right now.
+                // For deref of @T, create a dummy datum and use the
+                // datum's deref method. This is more work than just
+                // calling GEPi ourselves, but it ensures that any
+                // necessary rooting is performed. Note that we don't
+                // know the type T, so just substitute `i8`-- it
+                // doesn't really matter for our purposes right now.
                 let source_ty = ty::mk_box(tcx, ty::mk_i8());
-                let source_datum =
-                    Datum {val: source_data,
-                           ty: source_ty,
-                           mode: ByValue};
-                let derefd_datum =
-                    unpack_datum!(bcx,
-                                  source_datum.deref(bcx,
-                                                     expr,
-                                                     autoderefs));
-                derefd_datum.to_rptr(bcx).to_value_llval(bcx)
+                let source_datum = Datum(source_data_ptr, source_ty, LvalueExpr);
+                let derefd_datum = unpack_datum!(
+                    bcx, deref_once(bcx, expr, source_datum, autoderefs));
+                derefd_datum.assert_lvalue(bcx).to_llref()
             }
             ty::UniqTraitStore(..) | ty::RegionTraitStore(..) => {
-                source_data
+                Load(bcx, source_data_ptr)
             }
         };
         Store(bcx, target_data,
               GEPi(bcx, scratch.val, [0u, abi::trt_field_box]));
 
-        DatumBlock { bcx: bcx, datum: scratch }
+        DatumBlock(bcx, scratch.to_expr_datum())
     }
 }
 
-pub fn trans_into<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, dest: Dest)
-                  -> &'a Block<'a> {
-    let adjustment_found = {
-        let adjustments = bcx.tcx().adjustments.borrow();
-        adjustments.get().contains_key(&expr.id)
-    };
-    if adjustment_found {
-        // use trans_to_datum, which is mildly less efficient but
-        // which will perform the adjustments:
-        let datumblock = trans_to_datum(bcx, expr);
-        return match dest {
-            Ignore => datumblock.bcx,
-            SaveIn(lldest) => datumblock.store_to(INIT, lldest)
-        };
-    }
-
-    trans_into_unadjusted(bcx, expr, dest)
-}
-
-pub fn trans_into_unadjusted<'a>(
-                             bcx: &'a Block<'a>,
-                             expr: &ast::Expr,
-                             dest: Dest)
-                             -> &'a Block<'a> {
-    let ty = expr_ty(bcx, expr);
-
-    debug!("trans_into_unadjusted(expr={}, dest={})",
-           bcx.expr_to_str(expr),
-           dest.to_str(bcx.ccx()));
-    let _indenter = indenter();
-
-    debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
-
-    let dest = {
-        if ty::type_is_voidish(bcx.tcx(), ty) {
-            Ignore
-        } else {
-            dest
-        }
-    };
-
-    let kind = bcx.expr_kind(expr);
-    debug!("expr kind = {:?}", kind);
-    return match kind {
-        ty::LvalueExpr => {
-            let datumblock = trans_lvalue_unadjusted(bcx, expr);
-            match dest {
-                Ignore => datumblock.bcx,
-                SaveIn(lldest) => datumblock.store_to(INIT, lldest)
-            }
-        }
-        ty::RvalueDatumExpr => {
-            let datumblock = trans_rvalue_datum_unadjusted(bcx, expr);
-            match dest {
-                Ignore => datumblock.drop_val(),
-
-                // When processing an rvalue, the value will be newly
-                // allocated, so we always `move_to` so as not to
-                // unnecessarily inc ref counts and so forth:
-                SaveIn(lldest) => datumblock.move_to(INIT, lldest)
-            }
-        }
-        ty::RvalueDpsExpr => {
-            trans_rvalue_dps_unadjusted(bcx, expr, dest)
-        }
-        ty::RvalueStmtExpr => {
-            trans_rvalue_stmt_unadjusted(bcx, expr)
-        }
-    };
-}
-
-fn trans_lvalue<'a>(bcx: &'a Block<'a>, expr: &ast::Expr) -> DatumBlock<'a> {
+pub fn trans_to_lvalue<'a>(bcx: &'a Block<'a>,
+                           expr: &ast::Expr,
+                           name: &str)
+                           -> DatumBlock<'a, Lvalue> {
     /*!
+     * Translates an expression in "lvalue" mode -- meaning that it
+     * returns a reference to the memory that the expr represents.
+     *
+     * If this expression is an rvalue, this implies introducing a
+     * temporary.  In other words, something like `x().f` is
+     * translated into roughly the equivalent of
      *
-     * 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. */
+     *   { tmp = x(); tmp.f }
+     */
 
-    let adjustment_opt = {
-        let adjustments = bcx.tcx().adjustments.borrow();
-        adjustments.get().find_copy(&expr.id)
-    };
-    match adjustment_opt {
-        None => trans_lvalue_unadjusted(bcx, expr),
-        Some(_) => {
-            bcx.sess().span_bug(
-                expr.span,
-                format!("trans_lvalue() called on an expression \
-                      with adjustments"));
-        }
-    }
+    let mut bcx = bcx;
+    let datum = unpack_datum!(bcx, trans(bcx, expr));
+    return datum.to_lvalue_datum(bcx, name, expr.id);
 }
 
-fn trans_to_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
-                             -> DatumBlock<'a> {
+fn trans_unadjusted<'a>(bcx: &'a Block<'a>,
+                        expr: &ast::Expr)
+                        -> DatumBlock<'a, Expr> {
     /*!
-     * Translates an expression into a datum.  If this expression
-     * is an rvalue, this will result in a temporary value being
-     * created.  If you plan to store the value somewhere else,
-     * you should prefer `trans_into()` instead.
+     * A version of `trans` that ignores adjustments. You almost
+     * certainly do not want to call this directly.
      */
 
     let mut bcx = bcx;
 
-    debug!("trans_to_datum_unadjusted(expr={})", bcx.expr_to_str(expr));
+    debug!("trans_unadjusted(expr={})", bcx.expr_to_str(expr));
     let _indenter = indenter();
 
     debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
 
-    match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
-        ty::LvalueExpr => {
-            return trans_lvalue_unadjusted(bcx, expr);
-        }
-
-        ty::RvalueDatumExpr => {
+    return match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) {
+        ty::LvalueExpr | ty::RvalueDatumExpr => {
             let datum = unpack_datum!(bcx, {
-                trans_rvalue_datum_unadjusted(bcx, expr)
+                trans_datum_unadjusted(bcx, expr)
             });
-            datum.add_clean(bcx);
-            return DatumBlock {bcx: bcx, datum: datum};
+
+            DatumBlock {bcx: bcx, datum: datum}
         }
 
         ty::RvalueStmtExpr => {
             bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
-            return nil(bcx, expr_ty(bcx, expr));
+            nil(bcx, expr_ty(bcx, expr))
         }
 
         ty::RvalueDpsExpr => {
             let ty = expr_ty(bcx, expr);
-            if ty::type_is_voidish(bcx.tcx(), ty) {
+            if type_is_zero_size(bcx.ccx(), ty) {
                 bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
-                return nil(bcx, ty);
+                nil(bcx, ty)
             } else {
-                let scratch = scratch_datum(bcx, ty, "", false);
+                let scratch = rvalue_scratch_datum(bcx, ty, "");
                 bcx = trans_rvalue_dps_unadjusted(
                     bcx, expr, SaveIn(scratch.val));
 
@@ -556,38 +491,56 @@ fn trans_to_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
                 // Still, in practice it seems to increase
                 // performance, since we have fewer problems with
                 // morestack churn.
-                let scratch = scratch.to_appropriate_datum(bcx);
+                let scratch = unpack_datum!(
+                    bcx, scratch.to_appropriate_datum(bcx));
 
-                scratch.add_clean(bcx);
-                return DatumBlock {bcx: bcx, datum: scratch};
+                DatumBlock(bcx, scratch.to_expr_datum())
             }
         }
-    }
+    };
 
-    fn nil<'a>(bcx: &'a Block<'a>, ty: ty::t) -> DatumBlock<'a> {
-        let datum = immediate_rvalue(C_nil(), ty);
-        DatumBlock {
-            bcx: bcx,
-            datum: datum,
-        }
+    fn nil<'a>(bcx: &'a Block<'a>, ty: ty::t) -> DatumBlock<'a, Expr> {
+        let llval = C_undef(type_of::type_of(bcx.ccx(), ty));
+        let datum = immediate_rvalue(llval, ty);
+        DatumBlock(bcx, datum.to_expr_datum())
     }
 }
 
-fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
-                                 -> DatumBlock<'a> {
-    let _icx = push_ctxt("trans_rvalue_datum_unadjusted");
+fn trans_datum_unadjusted<'a>(bcx: &'a Block<'a>,
+                              expr: &ast::Expr)
+                              -> DatumBlock<'a, Expr> {
+    let mut bcx = bcx;
+    let fcx = bcx.fcx;
+    let _icx = push_ctxt("trans_datum_unadjusted");
 
     match expr.node {
+        ast::ExprParen(e) => {
+            trans(bcx, e)
+        }
         ast::ExprPath(_) | ast::ExprSelf => {
-            return trans_def_datum_unadjusted(bcx, expr, bcx.def(expr.id));
+            trans_def(bcx, expr, bcx.def(expr.id))
+        }
+        ast::ExprField(base, ident, _) => {
+            trans_rec_field(bcx, base, ident)
+        }
+        ast::ExprIndex(_, base, idx) => {
+            trans_index(bcx, expr, base, idx)
         }
         ast::ExprVstore(contents, ast::ExprVstoreBox) => {
-            return tvec::trans_uniq_or_managed_vstore(bcx, heap_managed,
-                                                      expr, contents);
+            fcx.push_ast_cleanup_scope(contents.id);
+            let datum = unpack_datum!(
+                bcx, tvec::trans_uniq_or_managed_vstore(bcx, heap_managed,
+                                                        expr, contents));
+            bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id);
+            DatumBlock(bcx, datum)
         }
         ast::ExprVstore(contents, ast::ExprVstoreUniq) => {
-            return tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
-                                                      expr, contents);
+            fcx.push_ast_cleanup_scope(contents.id);
+            let datum = unpack_datum!(
+                bcx, tvec::trans_uniq_or_managed_vstore(bcx, heap_exchange,
+                                                        expr, contents));
+            bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id);
+            DatumBlock(bcx, datum)
         }
         ast::ExprBox(_, contents) => {
             // Special case for `~T`. (The other case, for GC, is handled in
@@ -598,7 +551,7 @@ fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
             return trans_boxed_expr(bcx, box_ty, contents, contents_ty, heap)
         }
         ast::ExprLit(lit) => {
-            return trans_immediate_lit(bcx, expr, *lit);
+            trans_immediate_lit(bcx, expr, *lit)
         }
         ast::ExprBinary(_, op, lhs, rhs) => {
             // if overloaded, would be RvalueDpsExpr
@@ -607,22 +560,24 @@ fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
                 assert!(!method_map.get().contains_key(&expr.id));
             }
 
-            return trans_binary(bcx, expr, op, lhs, rhs);
+            trans_binary(bcx, expr, op, lhs, rhs)
+        }
+        ast::ExprUnary(_, ast::UnDeref, base) => {
+            let basedatum = unpack_datum!(bcx, trans(bcx, base));
+            deref_once(bcx, expr, basedatum, 0)
         }
         ast::ExprUnary(_, op, x) => {
-            return trans_unary_datum(bcx, expr, op, x);
+            trans_unary_datum(bcx, expr, op, x)
         }
         ast::ExprAddrOf(_, x) => {
-            return trans_addr_of(bcx, expr, x);
+            trans_addr_of(bcx, expr, x)
         }
         ast::ExprCast(val, _) => {
-            return trans_imm_cast(bcx, val, expr.id);
-        }
-        ast::ExprParen(e) => {
-            return trans_rvalue_datum_unadjusted(bcx, e);
+            // Datum output mode means this is a scalar cast:
+            trans_imm_cast(bcx, val, expr.id)
         }
         ast::ExprLogLevel => {
-            return trans_log_level(bcx);
+            trans_log_level(bcx)
         }
         _ => {
             bcx.tcx().sess.span_bug(
@@ -634,8 +589,152 @@ fn trans_rvalue_datum_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
     }
 }
 
-fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
-                                -> &'a Block<'a> {
+fn trans_rec_field<'a>(bcx: &'a Block<'a>,
+                       base: &ast::Expr,
+                       field: ast::Ident)
+                       -> DatumBlock<'a, Expr> {
+    //! Translates `base.field`.
+
+    let mut bcx = bcx;
+    let _icx = push_ctxt("trans_rec_field");
+
+    let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field"));
+    let repr = adt::represent_type(bcx.ccx(), base_datum.ty);
+    with_field_tys(bcx.tcx(), base_datum.ty, None, |discr, field_tys| {
+            let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys);
+            let d = base_datum.get_element(
+                field_tys[ix].mt.ty,
+                |srcval| adt::trans_field_ptr(bcx, repr, srcval, discr, ix));
+            DatumBlock { datum: d.to_expr_datum(), bcx: bcx }
+        })
+}
+
+fn trans_index<'a>(bcx: &'a Block<'a>,
+                   index_expr: &ast::Expr,
+                   base: &ast::Expr,
+                   idx: &ast::Expr)
+                   -> DatumBlock<'a, Expr> {
+    //! Translates `base[idx]`.
+
+    let _icx = push_ctxt("trans_index");
+    let ccx = bcx.ccx();
+    let mut bcx = bcx;
+
+    let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "index"));
+
+    // Translate index expression and cast to a suitable LLVM integer.
+    // Rust is less strict than LLVM in this regard.
+    let ix_datum = unpack_datum!(bcx, trans(bcx, idx));
+    let ix_val = ix_datum.to_llscalarish(bcx);
+    let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val));
+    let int_size = machine::llbitsize_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 (bcx, base, len) =
+        base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id, 0);
+
+    debug!("trans_index: base {}", bcx.val_to_str(base));
+    debug!("trans_index: len {}", bcx.val_to_str(len));
+
+    let bounds_check = ICmp(bcx, lib::llvm::IntUGE, ix_val, len);
+    let expect = ccx.intrinsics.get_copy(&("llvm.expect.i1"));
+    let expected = Call(bcx, expect, [bounds_check, C_i1(false)], []);
+    let bcx = with_cond(bcx, expected, |bcx| {
+            controlflow::trans_fail_bounds_check(bcx, index_expr.span, ix_val, len)
+        });
+    let elt = InBoundsGEP(bcx, base, [ix_val]);
+    let elt = PointerCast(bcx, elt, vt.llunit_ty.ptr_to());
+    DatumBlock(bcx, Datum(elt, vt.unit_ty, LvalueExpr))
+}
+
+fn trans_def<'a>(bcx: &'a Block<'a>,
+                 ref_expr: &ast::Expr,
+                 def: ast::Def)
+                 -> DatumBlock<'a, Expr>
+{
+    //! Translates a reference to a path.
+
+    let _icx = push_ctxt("trans_def_lvalue");
+    match def {
+        ast::DefFn(..) | ast::DefStaticMethod(..) => {
+            trans_def_fn_unadjusted(bcx, ref_expr, def)
+        }
+        ast::DefStatic(did, _) => {
+            let const_ty = expr_ty(bcx, ref_expr);
+
+            fn get_did(ccx: @CrateContext, did: ast::DefId)
+                       -> ast::DefId {
+                if did.crate != ast::LOCAL_CRATE {
+                    inline::maybe_instantiate_inline(ccx, did)
+                } else {
+                    did
+                }
+            }
+
+            fn get_val<'a>(bcx: &'a Block<'a>, did: ast::DefId, const_ty: ty::t)
+                       -> ValueRef {
+                // For external constants, we don't inline.
+                if did.crate == ast::LOCAL_CRATE {
+                    // The LLVM global has the type of its initializer,
+                    // which may not be equal to the enum's type for
+                    // non-C-like enums.
+                    let val = base::get_item_val(bcx.ccx(), did.node);
+                    let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to();
+                    PointerCast(bcx, val, pty)
+                } else {
+                    {
+                        let extern_const_values = bcx.ccx().extern_const_values.borrow();
+                        match extern_const_values.get().find(&did) {
+                            None => {}  // Continue.
+                            Some(llval) => {
+                                return *llval;
+                            }
+                        }
+                    }
+
+                    unsafe {
+                        let llty = type_of::type_of(bcx.ccx(), const_ty);
+                        let symbol = csearch::get_symbol(
+                            bcx.ccx().sess.cstore,
+                            did);
+                        let llval = symbol.with_c_str(|buf| {
+                                llvm::LLVMAddGlobal(bcx.ccx().llmod,
+                                                    llty.to_ref(),
+                                                    buf)
+                            });
+                        let mut extern_const_values = bcx.ccx().extern_const_values.borrow_mut();
+                        extern_const_values.get().insert(did, llval);
+                        llval
+                    }
+                }
+            }
+
+            let did = get_did(bcx.ccx(), did);
+            let val = get_val(bcx, did, const_ty);
+            DatumBlock(bcx, Datum(val, const_ty, LvalueExpr))
+        }
+        _ => {
+            DatumBlock(bcx, trans_local_var(bcx, def).to_expr_datum())
+        }
+    }
+}
+
+fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>,
+                                    expr: &ast::Expr)
+                                    -> &'a Block<'a> {
     let mut bcx = bcx;
     let _icx = push_ctxt("trans_rvalue_stmt");
 
@@ -644,38 +743,58 @@ fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
     }
 
     match expr.node {
+        ast::ExprParen(e) => {
+            trans_into(bcx, e, Ignore)
+        }
         ast::ExprBreak(label_opt) => {
-            return controlflow::trans_break(bcx, label_opt);
+            controlflow::trans_break(bcx, expr.id, label_opt)
         }
         ast::ExprAgain(label_opt) => {
-            return controlflow::trans_cont(bcx, label_opt);
+            controlflow::trans_cont(bcx, expr.id, label_opt)
         }
         ast::ExprRet(ex) => {
-            return controlflow::trans_ret(bcx, ex);
+            controlflow::trans_ret(bcx, ex)
         }
         ast::ExprWhile(cond, body) => {
-            return controlflow::trans_while(bcx, cond, body);
+            controlflow::trans_while(bcx, expr.id, cond, body)
         }
-        ast::ExprLoop(body, opt_label) => {
-            // FIXME #6993: map can go away when ast.rs is changed
-            return controlflow::trans_loop(bcx, body, opt_label.map(|x| x.name));
+        ast::ExprLoop(body, _) => {
+            controlflow::trans_loop(bcx, expr.id, body)
         }
         ast::ExprAssign(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);
+            let src_datum = unpack_datum!(bcx, trans(bcx, src));
+            let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign"));
+
+            if ty::type_needs_drop(bcx.tcx(), dst_datum.ty) {
+                // If there are destructors involved, make sure we
+                // are copying from an rvalue, since that cannot possible
+                // alias an lvalue. We are concerned about code like:
+                //
+                //   a = a
+                //
+                // but also
+                //
+                //   a = a.b
+                //
+                // where e.g. a : Option<Foo> and a.b :
+                // Option<Foo>. In that case, freeing `a` before the
+                // assignment may also free `a.b`!
+                //
+                // We could avoid this intermediary with some analysis
+                // to determine whether `dst` may possibly own `src`.
+                let src_datum = unpack_datum!(
+                    bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign"));
+                bcx = glue::drop_ty(bcx, dst_datum.val, dst_datum.ty);
+                src_datum.store_to(bcx, dst_datum.val)
+            } else {
+                src_datum.store_to(bcx, dst_datum.val)
+            }
         }
         ast::ExprAssignOp(callee_id, op, dst, src) => {
-            return trans_assign_op(bcx, expr, callee_id, op, dst, src);
-        }
-        ast::ExprParen(a) => {
-            return trans_rvalue_stmt_unadjusted(bcx, a);
+            trans_assign_op(bcx, expr, callee_id, op, dst, src)
         }
         ast::ExprInlineAsm(ref a) => {
-            return asm::trans_inline_asm(bcx, a);
+            asm::trans_inline_asm(bcx, a)
         }
         _ => {
             bcx.tcx().sess.span_bug(
@@ -684,38 +803,34 @@ fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
                       fall-through case: {:?}",
                      expr.node));
         }
-    };
+    }
 }
 
-fn trans_rvalue_dps_unadjusted<'a>(
-                               bcx: &'a Block<'a>,
-                               expr: &ast::Expr,
-                               dest: Dest)
-                               -> &'a Block<'a> {
+fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>,
+                                   expr: &ast::Expr,
+                                   dest: Dest)
+                                   -> &'a Block<'a> {
     let _icx = push_ctxt("trans_rvalue_dps_unadjusted");
+    let mut bcx = bcx;
     let tcx = bcx.tcx();
+    let fcx = bcx.fcx;
 
     match expr.node {
         ast::ExprParen(e) => {
-            return trans_rvalue_dps_unadjusted(bcx, e, dest);
+            return trans_into(bcx, e, dest);
         }
         ast::ExprPath(_) | ast::ExprSelf => {
             return trans_def_dps_unadjusted(bcx, expr,
                                             bcx.def(expr.id), dest);
         }
         ast::ExprIf(cond, thn, els) => {
-            return controlflow::trans_if(bcx, cond, thn, els, dest);
+            return controlflow::trans_if(bcx, expr.id, cond, thn, els, dest);
         }
         ast::ExprMatch(discr, ref arms) => {
             return _match::trans_match(bcx, expr, discr, *arms, dest);
         }
         ast::ExprBlock(blk) => {
-            return base::with_scope(bcx,
-                                    blk.info(),
-                                    "block-expr body",
-                                    |bcx| {
-                controlflow::trans_block(bcx, blk, dest)
-            });
+            controlflow::trans_block(bcx, blk, dest)
         }
         ast::ExprStruct(_, ref fields, base) => {
             return trans_rec_or_struct(bcx, (*fields), base, expr.span, expr.id, dest);
@@ -742,7 +857,9 @@ fn trans_rvalue_dps_unadjusted<'a>(
         }
         ast::ExprVstore(contents, ast::ExprVstoreSlice) |
         ast::ExprVstore(contents, ast::ExprVstoreMutSlice) => {
-            return tvec::trans_slice_vstore(bcx, expr, contents, dest);
+            fcx.push_ast_cleanup_scope(contents.id);
+            bcx = tvec::trans_slice_vstore(bcx, expr, contents, dest);
+            return fcx.pop_and_trans_ast_cleanup_scope(bcx, contents.id);
         }
         ast::ExprVec(..) | ast::ExprRepeat(..) => {
             return tvec::trans_fixed_vstore(bcx, expr, expr, dest);
@@ -803,10 +920,11 @@ fn trans_rvalue_dps_unadjusted<'a>(
                                        dest);
         }
         ast::ExprCast(val, _) => {
+            // DPS output mode means this is a trait cast:
             match ty::get(node_id_type(bcx, expr.id)).sty {
                 ty::ty_trait(..) => {
-                    return meth::trans_trait_cast(bcx, val, expr.id,
-                                                  dest, None);
+                    let datum = unpack_datum!(bcx, trans(bcx, val));
+                    return meth::trans_trait_cast(bcx, datum, expr.id, dest);
                 }
                 _ => {
                     bcx.tcx().sess.span_bug(expr.span,
@@ -885,11 +1003,10 @@ fn trans_def_dps_unadjusted<'a>(
     }
 }
 
-fn trans_def_datum_unadjusted<'a>(
-                              bcx: &'a Block<'a>,
-                              ref_expr: &ast::Expr,
-                              def: ast::Def)
-                              -> DatumBlock<'a> {
+fn trans_def_fn_unadjusted<'a>(bcx: &'a Block<'a>,
+                               ref_expr: &ast::Expr,
+                               def: ast::Def) -> DatumBlock<'a, Expr>
+{
     let _icx = push_ctxt("trans_def_datum_unadjusted");
 
     let fn_data = match def {
@@ -905,224 +1022,24 @@ fn trans_def_datum_unadjusted<'a>(
         }
         _ => {
             bcx.tcx().sess.span_bug(ref_expr.span, format!(
-                "Non-DPS def {:?} referened by {}",
-                def, bcx.node_id_to_str(ref_expr.id)));
+                    "trans_def_fn_unadjusted invoked on: {:?} for {}",
+                    def,
+                    ref_expr.repr(bcx.tcx())));
         }
     };
 
     let fn_ty = expr_ty(bcx, ref_expr);
-    DatumBlock {
-        bcx: bcx,
-        datum: Datum {
-            val: fn_data.llfn,
-            ty: fn_ty,
-            mode: ByValue
-        }
-    }
+    DatumBlock(bcx, Datum(fn_data.llfn, fn_ty, RvalueExpr(Rvalue(ByValue))))
 }
 
-fn trans_lvalue_unadjusted<'a>(bcx: &'a Block<'a>, expr: &ast::Expr)
-                           -> DatumBlock<'a> {
+pub fn trans_local_var<'a>(bcx: &'a Block<'a>,
+                           def: ast::Def)
+                           -> Datum<Lvalue> {
     /*!
-     *
-     * Translates an lvalue expression, always yielding a by-ref
-     * datum.  Does not apply any adjustments. */
-
-    let _icx = push_ctxt("trans_lval");
-    let mut bcx = bcx;
-
-    debug!("trans_lvalue(expr={})", bcx.expr_to_str(expr));
-    let _indenter = indenter();
-
-    return match expr.node {
-        ast::ExprParen(e) => {
-            trans_lvalue_unadjusted(bcx, e)
-        }
-        ast::ExprPath(_) | ast::ExprSelf => {
-            trans_def_lvalue(bcx, expr, bcx.def(expr.id))
-        }
-        ast::ExprField(base, ident, _) => {
-            trans_rec_field(bcx, base, ident)
-        }
-        ast::ExprIndex(_, base, idx) => {
-            trans_index(bcx, expr, base, idx)
-        }
-        ast::ExprUnary(_, ast::UnDeref, base) => {
-            let basedatum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-            basedatum.deref(bcx, expr, 0)
-        }
-        _ => {
-            bcx.tcx().sess.span_bug(
-                expr.span,
-                format!("trans_lvalue reached fall-through case: {:?}",
-                     expr.node));
-        }
-    };
-
-    fn trans_rec_field<'a>(
-                       bcx: &'a Block<'a>,
-                       base: &ast::Expr,
-                       field: ast::Ident)
-                       -> DatumBlock<'a> {
-        //! Translates `base.field`.
-
-        let mut bcx = bcx;
-        let _icx = push_ctxt("trans_rec_field");
-
-        let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-        let repr = adt::represent_type(bcx.ccx(), base_datum.ty);
-        with_field_tys(bcx.tcx(), base_datum.ty, None, |discr, field_tys| {
-            let ix = ty::field_idx_strict(bcx.tcx(), field.name, field_tys);
-            DatumBlock {
-                datum: base_datum.get_element(bcx,
-                                              field_tys[ix].mt.ty,
-                                              ZeroMem,
-                                              |srcval| {
-                    adt::trans_field_ptr(bcx, repr, srcval, discr, ix)
-                }),
-                bcx: bcx
-            }
-        })
-    }
-
-    fn trans_index<'a>(
-                   bcx: &'a Block<'a>,
-                   index_expr: &ast::Expr,
-                   base: &ast::Expr,
-                   idx: &ast::Expr)
-                   -> DatumBlock<'a> {
-        //! Translates `base[idx]`.
-
-        let _icx = push_ctxt("trans_index");
-        let ccx = bcx.ccx();
-        let mut bcx = bcx;
-
-        let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-
-        // 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_datum(bcx, idx).to_result();
-        let ix_size = machine::llbitsize_of_real(bcx.ccx(), val_ty(ix_val));
-        let int_size = machine::llbitsize_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 (bcx, base, len) =
-            base_datum.get_vec_base_and_len(bcx, index_expr.span, index_expr.id, 0);
-
-        debug!("trans_index: base {}", bcx.val_to_str(base));
-        debug!("trans_index: len {}", bcx.val_to_str(len));
-
-        let bounds_check = ICmp(bcx, lib::llvm::IntUGE, ix_val, len);
-        let expect = ccx.intrinsics.get_copy(&("llvm.expect.i1"));
-        let expected = Call(bcx, expect, [bounds_check, C_i1(false)], []);
-        let bcx = with_cond(bcx, expected, |bcx| {
-            controlflow::trans_fail_bounds_check(bcx, index_expr.span, ix_val, len)
-        });
-        let elt = InBoundsGEP(bcx, base, [ix_val]);
-        let elt = PointerCast(bcx, elt, vt.llunit_ty.ptr_to());
-        return DatumBlock {
-            bcx: bcx,
-            datum: Datum {val: elt,
-                          ty: vt.unit_ty,
-                          mode: ByRef(ZeroMem)}
-        };
-    }
-
-    fn trans_def_lvalue<'a>(
-                        bcx: &'a Block<'a>,
-                        ref_expr: &ast::Expr,
-                        def: ast::Def)
-                        -> DatumBlock<'a> {
-        //! Translates a reference to a path.
-
-        let _icx = push_ctxt("trans_def_lvalue");
-        match def {
-            ast::DefStatic(did, _) => {
-                let const_ty = expr_ty(bcx, ref_expr);
-
-                fn get_did(ccx: @CrateContext, did: ast::DefId)
-                    -> ast::DefId {
-                    if did.crate != ast::LOCAL_CRATE {
-                        inline::maybe_instantiate_inline(ccx, did)
-                    } else {
-                        did
-                    }
-                }
-
-                fn get_val(bcx: &Block, did: ast::DefId, const_ty: ty::t)
-                           -> ValueRef {
-                    // For external constants, we don't inline.
-                    if did.crate == ast::LOCAL_CRATE {
-                        // The LLVM global has the type of its initializer,
-                        // which may not be equal to the enum's type for
-                        // non-C-like enums.
-                        let val = base::get_item_val(bcx.ccx(), did.node);
-                        let pty = type_of::type_of(bcx.ccx(), const_ty).ptr_to();
-                        PointerCast(bcx, val, pty)
-                    } else {
-                        {
-                            let extern_const_values = bcx.ccx()
-                                                         .extern_const_values
-                                                         .borrow();
-                            match extern_const_values.get().find(&did) {
-                                None => {}  // Continue.
-                                Some(llval) => {
-                                    return *llval;
-                                }
-                            }
-                        }
-
-                        unsafe {
-                            let llty = type_of::type_of(bcx.ccx(), const_ty);
-                            let symbol = csearch::get_symbol(
-                                bcx.ccx().sess.cstore,
-                                did);
-                            let llval = symbol.with_c_str(|buf| {
-                                llvm::LLVMAddGlobal(bcx.ccx().llmod,
-                                                    llty.to_ref(),
-                                                    buf)
-                            });
-                            let mut extern_const_values =
-                                bcx.ccx().extern_const_values.borrow_mut();
-                            extern_const_values.get().insert(did, llval);
-                            llval
-                        }
-                    }
-                }
-
-                let did = get_did(bcx.ccx(), did);
-                let val = get_val(bcx, did, const_ty);
-                DatumBlock {
-                    bcx: bcx,
-                    datum: Datum {val: val,
-                                  ty: const_ty,
-                                  mode: ByRef(ZeroMem)}
-                }
-            }
-            _ => {
-                DatumBlock {
-                    bcx: bcx,
-                    datum: trans_local_var(bcx, def)
-                }
-            }
-        }
-    }
-}
+     * Translates a reference to a local variable or argument.
+     * This always results in an lvalue datum.
+     */
 
-pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum {
     let _icx = push_ctxt("trans_local_var");
 
     return match def {
@@ -1131,13 +1048,7 @@ pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum {
             let local_ty = node_id_type(bcx, nid);
             let llupvars = bcx.fcx.llupvars.borrow();
             match llupvars.get().find(&nid) {
-                Some(&val) => {
-                    Datum {
-                        val: val,
-                        ty: local_ty,
-                        mode: ByRef(ZeroMem)
-                    }
-                }
+                Some(&val) => Datum(val, local_ty, Lvalue),
                 None => {
                     bcx.sess().bug(format!(
                         "trans_local_var: no llval for upvar {:?} found", nid));
@@ -1173,9 +1084,10 @@ pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum {
         }
     };
 
-    fn take_local(bcx: &Block,
-                  table: &HashMap<ast::NodeId, Datum>,
-                  nid: ast::NodeId) -> Datum {
+    fn take_local<'a>(bcx: &'a Block<'a>,
+                      table: &HashMap<ast::NodeId, Datum<Lvalue>>,
+                      nid: ast::NodeId)
+                      -> Datum<Lvalue> {
         let datum = match table.find(&nid) {
             Some(&v) => v,
             None => {
@@ -1189,15 +1101,18 @@ pub fn trans_local_var(bcx: &Block, def: ast::Def) -> Datum {
     }
 }
 
-// The optional node ID here is the node ID of the path identifying the enum
-// variant in use. If none, this cannot possibly an enum variant (so, if it
-// is and `node_id_opt` is none, this function fails).
-pub fn with_field_tys<R>(
-                      tcx: ty::ctxt,
-                      ty: ty::t,
-                      node_id_opt: Option<ast::NodeId>,
-                      op: |ty::Disr, (&[ty::field])| -> R)
-                      -> R {
+pub fn with_field_tys<R>(tcx: ty::ctxt,
+                         ty: ty::t,
+                         node_id_opt: Option<ast::NodeId>,
+                         op: |ty::Disr, (&[ty::field])| -> R)
+                         -> R {
+    /*!
+     * Helper for enumerating the field types of structs, enums, or records.
+     * The optional node ID here is the node ID of the path identifying the enum
+     * variant in use. If none, this cannot possibly an enum variant (so, if it
+     * is and `node_id_opt` is none, this function fails).
+     */
+
     match ty::get(ty).sty {
         ty::ty_struct(did, ref substs) => {
             op(0, struct_fields(tcx, did, substs))
@@ -1330,6 +1245,7 @@ fn trans_adt<'a>(
              dest: Dest)
              -> &'a Block<'a> {
     let _icx = push_ctxt("trans_adt");
+    let fcx = bcx.fcx;
     let mut bcx = bcx;
     let addr = match dest {
         Ignore => {
@@ -1344,44 +1260,49 @@ fn trans_adt<'a>(
         }
         SaveIn(pos) => pos
     };
-    let mut temp_cleanups = ~[];
+
+    // This scope holds intermediates that must be cleaned should
+    // failure occur before the ADT as a whole is ready.
+    let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
+
     adt::trans_start_init(bcx, repr, addr, discr);
+
     for &(i, e) in fields.iter() {
         let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i);
         let e_ty = expr_ty_adjusted(bcx, e);
         bcx = trans_into(bcx, e, SaveIn(dest));
-        add_clean_temp_mem(bcx, dest, e_ty);
-        temp_cleanups.push(dest);
+        fcx.schedule_drop_mem(cleanup::CustomScope(custom_cleanup_scope),
+                              dest, e_ty);
     }
+
     for base in optbase.iter() {
         // FIXME #6573: is it sound to use the destination's repr on the base?
         // And, would it ever be reasonable to be here with discr != 0?
-        let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base.expr));
+        let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base.expr, "base"));
         for &(i, t) in base.fields.iter() {
-            let datum = base_datum.get_element(bcx, t, ZeroMem, |srcval| {
-                adt::trans_field_ptr(bcx, repr, srcval, discr, i)
-            });
+            let datum = base_datum.get_element(
+                t,
+                |srcval| adt::trans_field_ptr(bcx, repr, srcval, discr, i));
             let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i);
-            bcx = datum.store_to(bcx, INIT, dest);
+            bcx = datum.store_to(bcx, dest);
         }
     }
 
-    for cleanup in temp_cleanups.iter() {
-        revoke_clean(bcx, *cleanup);
-    }
+    fcx.pop_custom_cleanup_scope(custom_cleanup_scope);
+
     return bcx;
 }
 
 
-fn trans_immediate_lit<'a>(
-                       bcx: &'a Block<'a>,
-                       expr: &ast::Expr,
-                       lit: ast::Lit)
-                       -> DatumBlock<'a> {
+fn trans_immediate_lit<'a>(bcx: &'a Block<'a>,
+                           expr: &ast::Expr,
+                           lit: ast::Lit)
+                           -> DatumBlock<'a, Expr> {
     // must not be a string constant, that is a RvalueDpsExpr
     let _icx = push_ctxt("trans_immediate_lit");
     let ty = expr_ty(bcx, expr);
-    immediate_rvalue_bcx(bcx, consts::const_lit(bcx.ccx(), expr, lit), ty)
+    let v = consts::const_lit(bcx.ccx(), expr, lit);
+    immediate_rvalue_bcx(bcx, v, ty).to_expr_datumblock()
 }
 
 fn trans_unary_datum<'a>(
@@ -1389,7 +1310,8 @@ fn trans_unary_datum<'a>(
                      un_expr: &ast::Expr,
                      op: ast::UnOp,
                      sub_expr: &ast::Expr)
-                     -> DatumBlock<'a> {
+                     -> DatumBlock<'a, Expr> {
+    let mut bcx = bcx;
     let _icx = push_ctxt("trans_unary_datum");
 
     // if deref, would be LvalueExpr
@@ -1406,25 +1328,23 @@ fn trans_unary_datum<'a>(
 
     return match op {
         ast::UnNot => {
-            let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
-
-            // If this is a boolean type, we must not use the LLVM Not
-            // instruction, as that is a *bitwise* not and we want *logical*
-            // not on our 8-bit boolean values.
-            let llresult = match ty::get(un_ty).sty {
-                ty::ty_bool => {
-                    let llcond = ICmp(bcx,
-                                      lib::llvm::IntEQ,
-                                      val,
-                                      C_bool(false));
-                    Select(bcx, llcond, C_bool(true), C_bool(false))
-                }
-                _ => Not(bcx, val)
+            let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
+            let llresult = if ty::type_is_bool(un_ty) {
+                let val = datum.to_llscalarish(bcx);
+                let llcond = ICmp(bcx,
+                                  lib::llvm::IntEQ,
+                                  val,
+                                  C_bool(false));
+                Select(bcx, llcond, C_bool(true), C_bool(false))
+            } else {
+                // Note: `Not` is bitwise, not suitable for logical not.
+                Not(bcx, datum.to_llscalarish(bcx))
             };
-            immediate_rvalue_bcx(bcx, llresult, un_ty)
+            immediate_rvalue_bcx(bcx, llresult, un_ty).to_expr_datumblock()
         }
         ast::UnNeg => {
-            let Result {bcx, val} = trans_to_datum(bcx, sub_expr).to_result();
+            let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
+            let val = datum.to_llscalarish(bcx);
             let llneg = {
                 if ty::type_is_fp(un_ty) {
                     FNeg(bcx, val)
@@ -1432,7 +1352,7 @@ fn trans_unary_datum<'a>(
                     Neg(bcx, val)
                 }
             };
-            immediate_rvalue_bcx(bcx, llneg, un_ty)
+            immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock()
         }
         ast::UnBox => {
             trans_boxed_expr(bcx, un_ty, sub_expr, sub_ty, heap_managed)
@@ -1448,50 +1368,49 @@ fn trans_unary_datum<'a>(
     };
 }
 
-fn trans_boxed_expr<'a>(
-                    bcx: &'a Block<'a>,
-                    box_ty: ty::t,
-                    contents: &ast::Expr,
-                    contents_ty: ty::t,
-                    heap: heap)
-                    -> DatumBlock<'a> {
+fn trans_boxed_expr<'a>(bcx: &'a Block<'a>,
+                        box_ty: ty::t,
+                        contents: &ast::Expr,
+                        contents_ty: ty::t,
+                        heap: heap)
+                        -> DatumBlock<'a, Expr> {
     let _icx = push_ctxt("trans_boxed_expr");
+    let fcx = bcx.fcx;
     if heap == heap_exchange {
         let llty = type_of::type_of(bcx.ccx(), contents_ty);
         let size = llsize_of(bcx.ccx(), llty);
         let Result { bcx: bcx, val: val } = malloc_raw_dyn(bcx, contents_ty,
                                                            heap_exchange, size);
-        add_clean_free(bcx, val, heap_exchange);
+        let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
+        fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope),
+                                val, heap_exchange);
         let bcx = trans_into(bcx, contents, SaveIn(val));
-        revoke_clean(bcx, val);
-        return immediate_rvalue_bcx(bcx, val, box_ty);
+        fcx.pop_custom_cleanup_scope(custom_cleanup_scope);
+        immediate_rvalue_bcx(bcx, val, box_ty).to_expr_datumblock()
     } else {
-        let base::MallocResult {
-            bcx,
-            smart_ptr: bx,
-            body
-        } = base::malloc_general(bcx, contents_ty, heap);
-        add_clean_free(bcx, bx, heap);
+        let base::MallocResult { bcx, smart_ptr: bx, body } =
+            base::malloc_general(bcx, contents_ty, heap);
+        let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
+        fcx.schedule_free_value(cleanup::CustomScope(custom_cleanup_scope),
+                                bx, heap);
         let bcx = trans_into(bcx, contents, SaveIn(body));
-        revoke_clean(bcx, bx);
-        return immediate_rvalue_bcx(bcx, bx, box_ty);
+        fcx.pop_custom_cleanup_scope(custom_cleanup_scope);
+        immediate_rvalue_bcx(bcx, bx, box_ty).to_expr_datumblock()
     }
 }
 
-fn trans_addr_of<'a>(
-                 bcx: &'a Block<'a>,
-                 expr: &ast::Expr,
-                 subexpr: &ast::Expr)
-                 -> DatumBlock<'a> {
+fn trans_addr_of<'a>(bcx: &'a Block<'a>,
+                     expr: &ast::Expr,
+                     subexpr: &ast::Expr)
+                     -> DatumBlock<'a, Expr> {
     let _icx = push_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));
+    let sub_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, subexpr, "addr_of"));
+    let ty = expr_ty(bcx, expr);
+    return immediate_rvalue_bcx(bcx, sub_datum.val, ty).to_expr_datumblock();
 }
 
-pub fn trans_gc<'a>(
-                mut bcx: &'a Block<'a>,
+fn trans_gc<'a>(mut bcx: &'a Block<'a>,
                 expr: &ast::Expr,
                 contents: &ast::Expr,
                 dest: Dest)
@@ -1514,13 +1433,12 @@ pub fn trans_gc<'a>(
     let repr = adt::represent_type(bcx.ccx(), expr_ty);
     adt::trans_start_init(bcx, repr, addr, 0);
     let field_dest = adt::trans_field_ptr(bcx, repr, addr, 0, 0);
-    let contents_datum_block = trans_boxed_expr(bcx,
-                                                box_ty,
-                                                contents,
-                                                contents_ty,
-                                                heap_managed);
-    bcx = contents_datum_block.bcx;
-    bcx = contents_datum_block.datum.move_to(bcx, INIT, field_dest);
+    let contents_datum = unpack_datum!(bcx, trans_boxed_expr(bcx,
+                                                             box_ty,
+                                                             contents,
+                                                             contents_ty,
+                                                             heap_managed));
+    bcx = contents_datum.store_to(bcx, field_dest);
 
     // Next, wrap it up in the struct.
     bcx
@@ -1533,17 +1451,13 @@ fn trans_eager_binop<'a>(
                      binop_expr: &ast::Expr,
                      binop_ty: ty::t,
                      op: ast::BinOp,
-                     lhs_datum: &Datum,
-                     rhs_datum: &Datum)
-                     -> DatumBlock<'a> {
+                     lhs_t: ty::t,
+                     lhs: ValueRef,
+                     rhs_t: ty::t,
+                     rhs: ValueRef)
+                     -> DatumBlock<'a, Expr> {
     let _icx = push_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 mut intype = {
         if ty::type_is_bot(lhs_t) { rhs_t }
         else { lhs_t }
@@ -1626,7 +1540,7 @@ fn trans_eager_binop<'a>(
       }
     };
 
-    return immediate_rvalue_bcx(bcx, val, binop_ty);
+    immediate_rvalue_bcx(bcx, val, binop_ty).to_expr_datumblock()
 }
 
 // refinement types would obviate the need for this
@@ -1641,23 +1555,20 @@ fn trans_lazy_binop<'a>(
                     op: lazy_binop_ty,
                     a: &ast::Expr,
                     b: &ast::Expr)
-                    -> DatumBlock<'a> {
+                    -> DatumBlock<'a, Expr> {
     let _icx = push_ctxt("trans_lazy_binop");
     let binop_ty = expr_ty(bcx, binop_expr);
-    let bcx = bcx;
+    let fcx = bcx.fcx;
 
-    let Result {bcx: past_lhs, val: lhs} = {
-        base::with_scope_result(bcx, a.info(), "lhs", |bcx| {
-            trans_to_datum(bcx, a).to_result()
-        })
-    };
+    let DatumBlock {bcx: past_lhs, datum: lhs} = trans(bcx, a);
+    let lhs = lhs.to_llscalarish(past_lhs);
 
     if past_lhs.unreachable.get() {
-        return immediate_rvalue_bcx(past_lhs, lhs, binop_ty);
+        return immediate_rvalue_bcx(past_lhs, lhs, binop_ty).to_expr_datumblock();
     }
 
-    let join = base::sub_block(bcx, "join");
-    let before_rhs = base::sub_block(bcx, "rhs");
+    let join = fcx.new_id_block("join", binop_expr.id);
+    let before_rhs = fcx.new_id_block("before_rhs", b.id);
 
     let lhs_i1 = bool_to_i1(past_lhs, lhs);
     match op {
@@ -1665,21 +1576,18 @@ fn trans_lazy_binop<'a>(
       lazy_or => CondBr(past_lhs, lhs_i1, join.llbb, before_rhs.llbb)
     }
 
-    let Result {bcx: past_rhs, val: rhs} = {
-        base::with_scope_result(before_rhs, b.info(), "rhs", |bcx| {
-            trans_to_datum(bcx, b).to_result()
-        })
-    };
+    let DatumBlock {bcx: past_rhs, datum: rhs} = trans(before_rhs, b);
+    let rhs = rhs.to_llscalarish(past_rhs);
 
     if past_rhs.unreachable.get() {
-        return immediate_rvalue_bcx(join, lhs, binop_ty);
+        return immediate_rvalue_bcx(join, lhs, binop_ty).to_expr_datumblock();
     }
 
     Br(past_rhs, join.llbb);
     let phi = Phi(join, Type::bool(), [lhs, rhs], [past_lhs.llbb,
-                                               past_rhs.llbb]);
+                                                   past_rhs.llbb]);
 
-    return immediate_rvalue_bcx(join, phi, binop_ty);
+    return immediate_rvalue_bcx(join, phi, binop_ty).to_expr_datumblock();
 }
 
 fn trans_binary<'a>(
@@ -1688,8 +1596,9 @@ fn trans_binary<'a>(
                 op: ast::BinOp,
                 lhs: &ast::Expr,
                 rhs: &ast::Expr)
-                -> DatumBlock<'a> {
+                -> DatumBlock<'a, Expr> {
     let _icx = push_ctxt("trans_binary");
+    let ccx = bcx.ccx();
 
     match op {
         ast::BiAnd => {
@@ -1700,11 +1609,23 @@ fn trans_binary<'a>(
         }
         _ => {
             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 lhs_datum = unpack_datum!(bcx, trans(bcx, lhs));
+            let rhs_datum = unpack_datum!(bcx, trans(bcx, rhs));
             let binop_ty = expr_ty(bcx, binop_expr);
+
+            debug!("trans_binary (expr {}): lhs_datum={}",
+                   binop_expr.id,
+                   lhs_datum.to_str(ccx));
+            let lhs_ty = lhs_datum.ty;
+            let lhs = lhs_datum.to_llscalarish(bcx);
+
+            debug!("trans_binary (expr {}): rhs_datum={}",
+                   binop_expr.id,
+                   rhs_datum.to_str(ccx));
+            let rhs_ty = rhs_datum.ty;
+            let rhs = rhs_datum.to_llscalarish(bcx);
             trans_eager_binop(bcx, binop_expr, binop_ty, op,
-                              &lhs_datum, &rhs_datum)
+                              lhs_ty, lhs, rhs_ty, rhs)
         }
     }
 }
@@ -1724,14 +1645,15 @@ fn trans_overloaded_op<'a>(
     };
     let fty = node_id_type(bcx, callee_id);
     callee::trans_call_inner(bcx,
-                             expr.info(),
+                             Some(expr_info(expr)),
                              fty,
                              ret_ty,
-                             |bcx| {
+                             |bcx, arg_cleanup_scope| {
                                 meth::trans_method_callee(bcx,
                                                           callee_id,
                                                           rcvr,
-                                                          origin)
+                                                          origin,
+                                                          arg_cleanup_scope)
                              },
                              callee::ArgExprs(args),
                              Some(dest),
@@ -1799,85 +1721,90 @@ pub fn cast_type_kind(t: ty::t) -> cast_kind {
     }
 }
 
-fn trans_imm_cast<'a>(bcx: &'a Block<'a>, expr: &ast::Expr, id: ast::NodeId)
-                  -> DatumBlock<'a> {
+fn trans_imm_cast<'a>(bcx: &'a Block<'a>,
+                      expr: &ast::Expr,
+                      id: ast::NodeId)
+                      -> DatumBlock<'a, Expr> {
     let _icx = push_ctxt("trans_cast");
+    let mut bcx = bcx;
     let ccx = bcx.ccx();
 
-    let t_out = node_id_type(bcx, id);
-
-    let mut bcx = bcx;
-    let llexpr = unpack_result!(bcx, trans_to_datum(bcx, expr).to_result());
-    let ll_t_in = val_ty(llexpr);
     let t_in = expr_ty(bcx, expr);
-    let ll_t_out = type_of::type_of(ccx, t_out);
-
+    let t_out = node_id_type(bcx, id);
     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 ll_t_in = type_of::type_of(ccx, t_in);
+    let ll_t_out = type_of::type_of(ccx, t_out);
 
-    let newval =
-        match (k_in, k_out) {
-            (cast_integral, cast_integral) => {
-                int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in)
-            }
-            (cast_float, cast_float) => {
-                float_cast(bcx, ll_t_out, ll_t_in, llexpr)
-            }
-            (cast_integral, cast_float) => {
-                if s_in {
-                    SIToFP(bcx, llexpr, ll_t_out)
-                } else { UIToFP(bcx, llexpr, ll_t_out) }
-            }
-            (cast_float, cast_integral) => {
-                if ty::type_is_signed(t_out) {
-                    FPToSI(bcx, llexpr, ll_t_out)
-                } else { FPToUI(bcx, llexpr, ll_t_out) }
-            }
-            (cast_integral, cast_pointer) => {
-                IntToPtr(bcx, llexpr, ll_t_out)
-            }
-            (cast_pointer, cast_integral) => {
-                PtrToInt(bcx, llexpr, ll_t_out)
-            }
-            (cast_pointer, cast_pointer) => {
-                PointerCast(bcx, llexpr, ll_t_out)
-            }
-            (cast_enum, cast_integral) |
-            (cast_enum, cast_float) => {
-                let bcx = bcx;
-                let repr = adt::represent_type(ccx, t_in);
-                let llexpr_ptr;
-                if type_is_immediate(ccx, t_in) {
-                    llexpr_ptr = Alloca(bcx, ll_t_in, "");
-                    Store(bcx, llexpr, llexpr_ptr);
-                } else {
-                    llexpr_ptr = llexpr;
-                }
-                let lldiscrim_a = adt::trans_get_discr(bcx, repr, llexpr_ptr, Some(Type::i64()));
-                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(format!("translating unsupported cast: \
-                                           {} ({:?}) -> {} ({:?})",
-                                           t_in.repr(ccx.tcx), k_in,
-                                           t_out.repr(ccx.tcx), k_out))
-                }
+    // Convert the value to be cast into a ValueRef, either by-ref or
+    // by-value as appropriate given its type:
+    let datum = unpack_datum!(bcx, trans(bcx, expr));
+    let newval = match (k_in, k_out) {
+        (cast_integral, cast_integral) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in)
+        }
+        (cast_float, cast_float) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            float_cast(bcx, ll_t_out, ll_t_in, llexpr)
+        }
+        (cast_integral, cast_float) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            if s_in {
+                SIToFP(bcx, llexpr, ll_t_out)
+            } else { UIToFP(bcx, llexpr, ll_t_out) }
+        }
+        (cast_float, cast_integral) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            if ty::type_is_signed(t_out) {
+                FPToSI(bcx, llexpr, ll_t_out)
+            } else { FPToUI(bcx, llexpr, ll_t_out) }
+        }
+        (cast_integral, cast_pointer) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            IntToPtr(bcx, llexpr, ll_t_out)
+        }
+        (cast_pointer, cast_integral) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            PtrToInt(bcx, llexpr, ll_t_out)
+        }
+        (cast_pointer, cast_pointer) => {
+            let llexpr = datum.to_llscalarish(bcx);
+            PointerCast(bcx, llexpr, ll_t_out)
+        }
+        (cast_enum, cast_integral) |
+        (cast_enum, cast_float) => {
+            let mut bcx = bcx;
+            let repr = adt::represent_type(ccx, t_in);
+            let datum = unpack_datum!(
+                bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id));
+            let llexpr_ptr = datum.to_llref();
+            let lldiscrim_a =
+                adt::trans_get_discr(bcx, repr, llexpr_ptr, Some(Type::i64()));
+            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(format!("translating unsupported cast: \
+                                          {} ({:?}) -> {} ({:?})",
+                                          t_in.repr(ccx.tcx), k_in,
+                                          t_out.repr(ccx.tcx), k_out))
             }
-            _ => ccx.sess.bug(format!("translating unsupported cast: \
-                                   {} ({:?}) -> {} ({:?})",
-                                   t_in.repr(ccx.tcx), k_in,
-                                   t_out.repr(ccx.tcx), k_out))
-        };
-    return immediate_rvalue_bcx(bcx, newval, t_out);
+        }
+        _ => ccx.sess.bug(format!("translating unsupported cast: \
+                                  {} ({:?}) -> {} ({:?})",
+                                  t_in.repr(ccx.tcx), k_in,
+                                  t_out.repr(ccx.tcx), k_out))
+    };
+    return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock();
 }
 
 fn trans_assign_op<'a>(
                    bcx: &'a Block<'a>,
                    expr: &ast::Expr,
-                   callee_id: ast::NodeId,
+                   _callee_id: ast::NodeId,
                    op: ast::BinOp,
                    dst: &ast::Expr,
                    src: @ast::Expr)
@@ -1887,40 +1814,32 @@ fn trans_assign_op<'a>(
 
     debug!("trans_assign_op(expr={})", bcx.expr_to_str(expr));
 
-    // Evaluate LHS (destination), which should be an lvalue
-    let dst_datum = unpack_datum!(bcx, trans_lvalue_unadjusted(bcx, dst));
+    // User-defined operator methods cannot be used with `+=` etc right now
+    assert!({
+            let method_map = bcx.ccx().maps.method_map.borrow();
+            !method_map.get().find(&expr.id).is_some()
+        });
 
-    // A user-defined operator method
-    let found = {
-        let method_map = bcx.ccx().maps.method_map.borrow();
-        method_map.get().find(&expr.id).is_some()
-    };
-    if found {
-        // FIXME(#2528) evaluates the receiver twice!!
-        let scratch = scratch_datum(bcx, dst_datum.ty, "__assign_op", false);
-        let bcx = trans_overloaded_op(bcx,
-                                      expr,
-                                      callee_id,
-                                      dst,
-                                      ~[src],
-                                      dst_datum.ty,
-                                      SaveIn(scratch.val));
-        return scratch.move_to_datum(bcx, DROP_EXISTING, dst_datum);
-    }
+    // Evaluate LHS (destination), which should be an lvalue
+    let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op"));
+    assert!(!ty::type_needs_drop(bcx.tcx(), dst_datum.ty));
+    let dst_ty = dst_datum.ty;
+    let dst = Load(bcx, dst_datum.val);
 
-    // Evaluate RHS (source)
-    let src_datum = unpack_datum!(bcx, trans_to_datum(bcx, src));
+    // Evaluate RHS
+    let rhs_datum = unpack_datum!(bcx, trans(bcx, src));
+    let rhs_ty = rhs_datum.ty;
+    let rhs = rhs_datum.to_llscalarish(bcx);
 
     // 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.copy_to_datum(bcx, DROP_EXISTING, dst_datum);
+    let result_datum = unpack_datum!(
+        bcx, trans_eager_binop(bcx, expr, dst_datum.ty, op,
+                               dst_ty, dst, rhs_ty, rhs));
+    return result_datum.store_to(bcx, dst_datum.val);
 }
 
-pub fn trans_log_level<'a>(bcx: &'a Block<'a>) -> DatumBlock<'a> {
+fn trans_log_level<'a>(bcx: &'a Block<'a>)
+                       -> DatumBlock<'a, Expr> {
     let _icx = push_ctxt("trans_log_level");
     let ccx = bcx.ccx();
 
@@ -1974,6 +1893,193 @@ pub fn trans_log_level<'a>(bcx: &'a Block<'a>) -> DatumBlock<'a> {
         }
     };
 
-    return immediate_rvalue_bcx(bcx, Load(bcx, global), ty::mk_u32());
+    immediate_rvalue_bcx(bcx, Load(bcx, global), ty::mk_u32()).to_expr_datumblock()
+}
+
+fn deref_multiple<'a>(bcx: &'a Block<'a>,
+                      expr: &ast::Expr,
+                      datum: Datum<Expr>,
+                      times: uint)
+                      -> DatumBlock<'a, Expr> {
+    let mut bcx = bcx;
+    let mut datum = datum;
+    for i in range(1, times+1) {
+        datum = unpack_datum!(bcx, deref_once(bcx, expr, datum, i));
+    }
+    DatumBlock { bcx: bcx, datum: datum }
+}
+
+fn deref_once<'a>(bcx: &'a Block<'a>,
+                  expr: &ast::Expr,
+                  datum: Datum<Expr>,
+                  derefs: uint)
+                  -> DatumBlock<'a, Expr> {
+    let ccx = bcx.ccx();
+    let bcx = write_guard::root_and_write_guard(&datum, bcx, expr.span,
+                                                expr.id, derefs);
+
+    debug!("deref_once(expr={}, datum={}, derefs={})",
+           expr.repr(bcx.tcx()),
+           datum.to_str(ccx),
+           derefs);
+
+    let mut bcx = bcx;
+
+    let r = match ty::get(datum.ty).sty {
+        ty::ty_uniq(content_ty) => {
+            deref_owned_pointer(bcx, expr, datum, content_ty)
+        }
+
+        ty::ty_box(content_ty) => {
+            let datum = unpack_datum!(
+                bcx, datum.to_lvalue_datum(bcx, "deref", expr.id));
+            let llptrref = datum.to_llref();
+            let llptr = Load(bcx, llptrref);
+            let llbody = GEPi(bcx, llptr, [0u, abi::box_field_body]);
+            DatumBlock(bcx, Datum(llbody, content_ty, LvalueExpr))
+        }
+
+        ty::ty_ptr(ty::mt { ty: content_ty, .. }) |
+        ty::ty_rptr(_, ty::mt { ty: content_ty, .. }) => {
+            assert!(!ty::type_needs_drop(bcx.tcx(), datum.ty));
+
+            let ptr = datum.to_llscalarish(bcx);
+
+            // Always generate an lvalue datum, even if datum.mode is
+            // an rvalue.  This is because datum.mode is only an
+            // rvalue for non-owning pointers like &T or *T, in which
+            // case cleanup *is* scheduled elsewhere, by the true
+            // owner (or, in the case of *T, by the user).
+            DatumBlock(bcx, Datum(ptr, content_ty, LvalueExpr))
+        }
+
+        ty::ty_enum(..) |
+        ty::ty_struct(..) => {
+            // Subtle efficiency note: In the case where we have a
+            // newtype struct where the struct itself does not have a
+            // dtor, but the contents do, we could avoid forcing the
+            // data into Lvalue and instead return an Rvalue. But it
+            // doesn't seem worth the trouble.
+            let datum = unpack_datum!(bcx, ensure_cleanup(bcx, expr, datum));
+
+            // Unlike the pointer case above, we generate an
+            // rvalue datum if we are given an rvalue. There are
+            // two reasons that this makes sense here:
+            //
+            // 1. dereferencing a struct does not actually perform a
+            //    pointer load and hence the resulting value is not
+            //    naturally by reference, as would be required by an
+            //    lvalue result.
+            //
+            // 2. the struct always owns its contents, and hence and does not
+            //    itself have a dtor (else it would be in lvalue mode).
+            let repr = adt::represent_type(ccx, datum.ty);
+            let ty = adt::deref_ty(ccx, repr);
+            let Datum { val, kind, .. } = datum;
+            let r = match kind {
+                LvalueExpr => {
+                    Datum {
+                        val: adt::trans_field_ptr(bcx, repr, val, 0, 0),
+                        ty: ty,
+                        kind: LvalueExpr
+                    }
+                }
+                RvalueExpr(Rvalue { mode: ByRef }) => {
+                    Datum {
+                        val: adt::trans_field_ptr(bcx, repr, val, 0, 0),
+                        ty: ty,
+                        kind: RvalueExpr(Rvalue(ByValue))
+                    }
+                }
+                RvalueExpr(Rvalue { mode: ByValue }) => {
+                    Datum {
+                        val: ExtractValue(bcx, val, 0),
+                        ty: ty,
+                        kind: RvalueExpr(Rvalue(ByValue))
+                    }
+                }
+            };
+            DatumBlock(bcx, r)
+        }
+
+        _ => {
+            bcx.tcx().sess.span_bug(
+                expr.span,
+                format!("deref invoked on expr of illegal type {}",
+                        datum.ty.repr(bcx.tcx())));
+        }
+    };
+
+    debug!("deref_once(expr={}, derefs={}, result={})",
+           expr.id, derefs, r.datum.to_str(ccx));
+
+    return r;
+
+    fn ensure_cleanup<'a>(mut bcx: &'a Block<'a>,
+                          expr: &ast::Expr,
+                          datum: Datum<Expr>)
+                          -> DatumBlock<'a, Expr> {
+        /*!
+         * If the datum contains data that needs to be dropped,
+         * convert it to an lvalue, thus ensuring that cleanup
+         * is scheduled.
+         */
+
+        if ty::type_needs_drop(bcx.tcx(), datum.ty) {
+            let lv_datum = unpack_datum!(
+                bcx, datum.to_lvalue_datum(bcx, "deref", expr.id));
+            DatumBlock(bcx, lv_datum.to_expr_datum())
+        } else {
+            DatumBlock(bcx, datum)
+        }
+    }
+
+    fn deref_owned_pointer<'a>(bcx: &'a Block<'a>,
+                               expr: &ast::Expr,
+                               datum: Datum<Expr>,
+                               content_ty: ty::t)
+                               -> DatumBlock<'a, Expr> {
+        /*!
+         * We microoptimize derefs of owned pointers a bit here.
+         * Basically, the idea is to make the deref of an rvalue
+         * result in an rvalue. This helps to avoid intermediate stack
+         * slots in the resulting LLVM. The idea here is that, if the
+         * `~T` pointer is an rvalue, then we can schedule a *shallow*
+         * free of the `~T` pointer, and then return a ByRef rvalue
+         * into the pointer. Because the free is shallow, it is legit
+         * to return an rvalue, because we know that the contents are
+         * not yet scheduled to be freed. The language rules ensure that the
+         * contents will be used (or moved) before the free occurs.
+         */
+
+        match datum.kind {
+            RvalueExpr(Rvalue { mode: ByRef }) => {
+                let scope = cleanup::temporary_scope(bcx.tcx(), expr.id);
+                let ptr = Load(bcx, datum.val);
+                bcx.fcx.schedule_free_value(scope, ptr, heap_exchange);
+            }
+            RvalueExpr(Rvalue { mode: ByValue }) => {
+                let scope = cleanup::temporary_scope(bcx.tcx(), expr.id);
+                bcx.fcx.schedule_free_value(scope, datum.val, heap_exchange);
+            }
+            LvalueExpr => { }
+        }
+
+        // If we had an rvalue in, we produce an rvalue out.
+        let (llptr, kind) = match datum.kind {
+            LvalueExpr => {
+                (Load(bcx, datum.val), LvalueExpr)
+            }
+            RvalueExpr(Rvalue { mode: ByRef }) => {
+                (Load(bcx, datum.val), RvalueExpr(Rvalue(ByRef)))
+            }
+            RvalueExpr(Rvalue { mode: ByValue }) => {
+                (datum.val, RvalueExpr(Rvalue(ByRef)))
+            }
+        };
+
+        let datum = Datum { ty: content_ty, val: llptr, kind: kind };
+        DatumBlock { bcx: bcx, datum: datum }
+    }
 }
 
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 2b55c0aac0c..083a1c6988d 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -198,7 +198,7 @@ pub fn trans_native_call<'a>(
         _ => ccx.sess.bug("trans_native_call called on non-function type")
     };
     let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys);
-    let ret_def = !ty::type_is_voidish(bcx.tcx(), fn_sig.output);
+    let ret_def = !return_type_is_void(bcx.ccx(), fn_sig.output);
     let fn_type = cabi::compute_abi_info(ccx,
                                          llsig.llarg_tys,
                                          llsig.llret_ty,
@@ -282,20 +282,20 @@ pub fn trans_native_call<'a>(
             // FIXME(#8357) We really ought to report a span here
             ccx.sess.fatal(
                 format!("ABI string `{}` has no suitable ABI \
-                      for target architecture",
-                     fn_abis.user_string(ccx.tcx)));
+                        for target architecture",
+                        fn_abis.user_string(ccx.tcx)));
         }
     };
 
     // A function pointer is called without the declaration available, so we have to apply
     // any attributes with ABI implications directly to the call instruction. Right now, the
     // only attribute we need to worry about is `sret`.
-    let attrs;
-    if fn_type.ret_ty.is_indirect() {
-        attrs = &[(1, StructRetAttribute)];
+    let sret_attr = if fn_type.ret_ty.is_indirect() {
+        Some((1, StructRetAttribute))
     } else {
-        attrs = &[];
-    }
+        None
+    };
+    let attrs = sret_attr.as_slice();
     let llforeign_retval = CallWithConv(bcx, llfn, llargs_foreign, cc, attrs);
 
     // If the function we just called does not use an outpointer,
@@ -491,7 +491,6 @@ pub fn trans_rust_fn_with_foreign_abi(ccx: @CrateContext,
                        None,
                        None,
                        id,
-                       None,
                        []);
         return llfndecl;
     }
@@ -779,7 +778,7 @@ fn foreign_types_for_fn_ty(ccx: &CrateContext,
         _ => ccx.sess.bug("foreign_types_for_fn_ty called on non-function type")
     };
     let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs);
-    let ret_def = !ty::type_is_voidish(ccx.tcx, fn_sig.output);
+    let ret_def = !return_type_is_void(ccx, fn_sig.output);
     let fn_ty = cabi::compute_abi_info(ccx,
                                        llsig.llarg_tys,
                                        llsig.llret_ty,
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 9b3243fa3ef..ae03d48dbf0 100644
--- a/src/librustc/middle/trans/glue.rs
+++ b/src/librustc/middle/trans/glue.rs
@@ -21,9 +21,10 @@ use middle::lang_items::{FreeFnLangItem, ExchangeFreeFnLangItem};
 use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::callee;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::closure;
 use middle::trans::common::*;
-use middle::trans::datum::immediate_rvalue;
 use middle::trans::build::*;
 use middle::trans::expr;
 use middle::trans::machine::*;
@@ -269,25 +270,23 @@ fn call_tydesc_glue<'a>(cx: &'a Block<'a>, v: ValueRef, t: ty::t, field: uint)
 fn make_visit_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t)
                    -> &'a Block<'a> {
     let _icx = push_ctxt("make_visit_glue");
-    with_scope(bcx, None, "visitor cleanup", |bcx| {
-        let mut bcx = bcx;
-        let (visitor_trait, object_ty) = match ty::visitor_object_ty(bcx.tcx(),
-                                                                     ty::ReStatic) {
-            Ok(pair) => pair,
-            Err(s) => {
-                bcx.tcx().sess.fatal(s);
-            }
-        };
-        let v = PointerCast(bcx, v, type_of(bcx.ccx(), object_ty).ptr_to());
-        bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, visitor_trait.def_id);
-        // The visitor is a boxed object and needs to be dropped
-        add_clean(bcx, v, object_ty);
-        bcx
-    })
+    let mut bcx = bcx;
+    let (visitor_trait, object_ty) = match ty::visitor_object_ty(bcx.tcx(),
+                                                                 ty::ReStatic) {
+        Ok(pair) => pair,
+        Err(s) => {
+            bcx.tcx().sess.fatal(s);
+        }
+    };
+    let v = PointerCast(bcx, v, type_of(bcx.ccx(), object_ty).ptr_to());
+    bcx = reflect::emit_calls_to_trait_visit_ty(bcx, t, v, visitor_trait.def_id);
+    bcx
 }
 
-pub fn make_free_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t)
-                      -> &'a Block<'a> {
+pub fn make_free_glue<'a>(bcx: &'a Block<'a>,
+                          v: ValueRef,
+                          t: ty::t)
+                          -> &'a Block<'a> {
     // NB: v0 is an *alias* of type t here, not a direct value.
     let _icx = push_ctxt("make_free_glue");
     match ty::get(t).sty {
@@ -297,14 +296,13 @@ pub fn make_free_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t)
         let bcx = drop_ty(bcx, body, body_ty);
         trans_free(bcx, v)
       }
-      ty::ty_uniq(..) => {
-        let box_datum = immediate_rvalue(Load(bcx, v), t);
-        let not_null = IsNotNull(bcx, box_datum.val);
+      ty::ty_uniq(content_ty) => {
+        let llbox = Load(bcx, v);
+        let not_null = IsNotNull(bcx, llbox);
         with_cond(bcx, not_null, |bcx| {
-            let body_datum = box_datum.box_body(bcx);
-            let bcx = drop_ty(bcx, body_datum.to_ref_llval(bcx), body_datum.ty);
-            trans_exchange_free(bcx, box_datum.val)
-        })
+                    let bcx = drop_ty(bcx, llbox, content_ty);
+                    trans_exchange_free(bcx, llbox)
+                })
       }
       ty::ty_vec(_, ty::vstore_uniq) | ty::ty_str(ty::vstore_uniq) |
       ty::ty_vec(_, ty::vstore_box) | ty::ty_str(ty::vstore_box) => {
@@ -362,21 +360,24 @@ pub fn trans_struct_drop<'a>(
     // Be sure to put all of the fields into a scope so we can use an invoke
     // instruction to call the user destructor but still call the field
     // destructors if the user destructor fails.
-    with_scope(bcx, None, "field drops", |bcx| {
-        let self_arg = PointerCast(bcx, v0, params[0]);
-        let args = ~[self_arg];
-
-        // Add all the fields as a value which needs to be cleaned at the end of
-        // this scope.
-        let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
-        for (i, fld) in field_tys.iter().enumerate() {
-            let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
-            add_clean(bcx, llfld_a, fld.mt.ty);
-        }
+    let field_scope = bcx.fcx.push_custom_cleanup_scope();
+
+    let self_arg = PointerCast(bcx, v0, params[0]);
+    let args = ~[self_arg];
+
+    // Add all the fields as a value which needs to be cleaned at the end of
+    // this scope.
+    let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
+    for (i, fld) in field_tys.iter().enumerate() {
+        let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
+        bcx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope),
+                                  llfld_a,
+                                  fld.mt.ty);
+    }
 
-        let (_, bcx) = invoke(bcx, dtor_addr, args, [], None);
-        bcx
-    })
+    let (_, bcx) = invoke(bcx, dtor_addr, args, [], None);
+
+    bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, field_scope)
 }
 
 pub fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t)
@@ -451,11 +452,13 @@ pub fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t)
 fn decr_refcnt_maybe_free<'a>(bcx: &'a Block<'a>, box_ptr_ptr: ValueRef,
                               t: Option<ty::t>) -> &'a Block<'a> {
     let _icx = push_ctxt("decr_refcnt_maybe_free");
+    let fcx = bcx.fcx;
     let ccx = bcx.ccx();
 
-    let decr_bcx = sub_block(bcx, "decr");
-    let free_bcx = sub_block(decr_bcx, "free");
-    let next_bcx = sub_block(bcx, "next");
+    let decr_bcx = fcx.new_temp_block("decr");
+    let free_bcx = fcx.new_temp_block("free");
+    let next_bcx = fcx.new_temp_block("next");
+
     let box_ptr = Load(bcx, box_ptr_ptr);
     let llnotnull = IsNotNull(bcx, box_ptr);
     CondBr(bcx, llnotnull, decr_bcx.llbb, next_bcx.llbb);
@@ -593,7 +596,7 @@ fn make_generic_glue(ccx: @CrateContext, t: ty::t, llfn: ValueRef,
     let _s = StatRecorder::new(ccx, glue_name);
 
     let fcx = new_fn_ctxt(ccx, ~[], llfn, ty::mk_nil(), None);
-    init_function(&fcx, false, ty::mk_nil(), None, None);
+    init_function(&fcx, false, ty::mk_nil(), None);
 
     lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage);
     ccx.stats.n_glues_created.set(ccx.stats.n_glues_created.get() + 1u);
diff --git a/src/librustc/middle/trans/inline.rs b/src/librustc/middle/trans/inline.rs
index 7d0e5a43544..98e3593a9f1 100644
--- a/src/librustc/middle/trans/inline.rs
+++ b/src/librustc/middle/trans/inline.rs
@@ -178,7 +178,6 @@ pub fn maybe_instantiate_inline(ccx: @CrateContext, fn_id: ast::DefId)
                        self_kind,
                        None,
                        mth.id,
-                       Some(&*mth),
                        []);
           }
           local_def(mth.id)
diff --git a/src/librustc/middle/trans/intrinsic.rs b/src/librustc/middle/trans/intrinsic.rs
index ff5c22e726a..1fc749bb687 100644
--- a/src/librustc/middle/trans/intrinsic.rs
+++ b/src/librustc/middle/trans/intrinsic.rs
@@ -153,14 +153,14 @@ pub fn trans_intrinsic(ccx: @CrateContext,
 
     let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, item.id));
 
-    let fcx = new_fn_ctxt_w_id(ccx,
-                               path,
-                               decl,
-                               item.id,
-                               output_type,
-                               Some(substs),
-                               Some(item.span));
-    init_function(&fcx, true, output_type, Some(substs), None);
+    let fcx = new_fn_ctxt_detailed(ccx,
+                                   path,
+                                   decl,
+                                   item.id,
+                                   output_type,
+                                   Some(substs),
+                                   Some(item.span));
+    init_function(&fcx, true, output_type, Some(substs));
 
     set_always_inline(fcx.llfn);
 
@@ -254,27 +254,18 @@ pub fn trans_intrinsic(ccx: @CrateContext,
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Ret(bcx, C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty)));
         }
-        "move_val" => {
+        "move_val_init" => {
             // Create a datum reflecting the value being moved.
             // Use `appropriate_mode` so that the datum is by ref
             // if the value is non-immediate. Note that, with
             // intrinsics, there are no argument cleanups to
-            // concern ourselves with.
-            let tp_ty = substs.tys[0];
-            let mode = appropriate_mode(ccx, tp_ty);
-            let src = Datum {val: get_param(decl, first_real_arg + 1u),
-                             ty: tp_ty, mode: mode};
-            bcx = src.move_to(bcx, DROP_EXISTING,
-                              get_param(decl, first_real_arg));
-            RetVoid(bcx);
-        }
-        "move_val_init" => {
-            // See comments for `"move_val"`.
+            // concern ourselves with, so we can use an rvalue datum.
             let tp_ty = substs.tys[0];
-            let mode = appropriate_mode(ccx, tp_ty);
+            let mode = appropriate_rvalue_mode(ccx, tp_ty);
             let src = Datum {val: get_param(decl, first_real_arg + 1u),
-                             ty: tp_ty, mode: mode};
-            bcx = src.move_to(bcx, INIT, get_param(decl, first_real_arg));
+                             ty: tp_ty,
+                             kind: Rvalue(mode)};
+            bcx = src.store_to(bcx, get_param(decl, first_real_arg));
             RetVoid(bcx);
         }
         "min_align_of" => {
@@ -326,7 +317,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
         "uninit" => {
             // Do nothing, this is effectively a no-op
             let retty = substs.tys[0];
-            if type_is_immediate(ccx, retty) && !ty::type_is_nil(retty) {
+            if type_is_immediate(ccx, retty) && !return_type_is_void(ccx, retty) {
                 unsafe {
                     Ret(bcx, lib::llvm::llvm::LLVMGetUndef(type_of(ccx, retty).to_ref()));
                 }
@@ -365,7 +356,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                                          pluralize(out_type_size)));
             }
 
-            if !ty::type_is_voidish(ccx.tcx, out_type) {
+            if !return_type_is_void(ccx, out_type) {
                 let llsrcval = get_param(decl, first_real_arg);
                 if type_is_immediate(ccx, in_type) {
                     match fcx.llretptr.get() {
diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs
index f8aef908381..85b9e16e5cc 100644
--- a/src/librustc/middle/trans/meth.rs
+++ b/src/librustc/middle/trans/meth.rs
@@ -18,6 +18,7 @@ use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee::*;
 use middle::trans::callee;
+use middle::trans::cleanup;
 use middle::trans::common::*;
 use middle::trans::datum::*;
 use middle::trans::expr::{SaveIn, Ignore};
@@ -132,7 +133,6 @@ pub fn trans_method(ccx: @CrateContext,
              self_ty,
              param_substs,
              method.id,
-             Some(method),
              []);
     llfn
 }
@@ -141,7 +141,8 @@ pub fn trans_method_callee<'a>(
                            bcx: &'a Block<'a>,
                            callee_id: ast::NodeId,
                            this: &ast::Expr,
-                           mentry: typeck::method_map_entry)
+                           mentry: typeck::method_map_entry,
+                           arg_cleanup_scope: cleanup::ScopeId)
                            -> Callee<'a> {
     let _icx = push_ctxt("impl::trans_method_callee");
 
@@ -153,9 +154,8 @@ pub fn trans_method_callee<'a>(
     match mentry.origin {
         typeck::method_static(did) => {
             let self_ty = monomorphize_type(bcx, mentry.self_ty);
-            let mut temp_cleanups = ~[];
             let Result {bcx, val} = trans_arg_expr(bcx, self_ty, this,
-                                                   &mut temp_cleanups,
+                                                   arg_cleanup_scope,
                                                    DontAutorefArg);
             // HACK should not need the pointer cast, eventually trans_fn_ref
             // should return a function type with the right type for self.
@@ -168,7 +168,6 @@ pub fn trans_method_callee<'a>(
                 data: Method(MethodData {
                     llfn: llfn_val,
                     llself: val,
-                    temp_cleanup: temp_cleanups.head_opt().map(|v| *v)
                 })
             }
         }
@@ -186,7 +185,8 @@ pub fn trans_method_callee<'a>(
 
                     let vtbl = find_vtable(bcx.tcx(), substs, p, b);
                     trans_monomorphized_callee(bcx, callee_id, this, mentry,
-                                               trait_id, off, vtbl)
+                                               trait_id, off, vtbl,
+                                               arg_cleanup_scope)
                 }
                 // how to get rid of this?
                 None => fail!("trans_method_callee: missing param_substs")
@@ -197,7 +197,8 @@ pub fn trans_method_callee<'a>(
             trans_trait_callee(bcx,
                                callee_id,
                                mt.real_index,
-                               this)
+                               this,
+                               arg_cleanup_scope)
         }
     }
 }
@@ -319,7 +320,8 @@ pub fn trans_monomorphized_callee<'a>(
                                   mentry: typeck::method_map_entry,
                                   trait_id: ast::DefId,
                                   n_method: uint,
-                                  vtbl: typeck::vtable_origin)
+                                  vtbl: typeck::vtable_origin,
+                                  arg_cleanup_scope: cleanup::ScopeId)
                                   -> Callee<'a> {
     let _icx = push_ctxt("impl::trans_monomorphized_callee");
     return match vtbl {
@@ -330,9 +332,8 @@ pub fn trans_monomorphized_callee<'a>(
 
           // obtain the `self` value:
           let self_ty = monomorphize_type(bcx, mentry.self_ty);
-          let mut temp_cleanups = ~[];
           let Result {bcx, val} = trans_arg_expr(bcx, self_ty, base,
-                                                 &mut temp_cleanups,
+                                                 arg_cleanup_scope,
                                                  DontAutorefArg);
 
           // create a concatenated set of substitutions which includes
@@ -361,7 +362,6 @@ pub fn trans_monomorphized_callee<'a>(
               data: Method(MethodData {
                   llfn: llfn_val,
                   llself: val,
-                  temp_cleanup: temp_cleanups.head_opt().map(|v| *v)
               })
           }
       }
@@ -425,7 +425,8 @@ pub fn trans_trait_callee<'a>(
                           bcx: &'a Block<'a>,
                           callee_id: ast::NodeId,
                           n_method: uint,
-                          self_expr: &ast::Expr)
+                          self_expr: &ast::Expr,
+                          arg_cleanup_scope: cleanup::ScopeId)
                           -> Callee<'a> {
     /*!
      * Create a method callee where the method is coming from a trait
@@ -439,38 +440,31 @@ pub fn trans_trait_callee<'a>(
     let _icx = push_ctxt("impl::trans_trait_callee");
     let mut bcx = bcx;
 
-    // make a local copy for trait if needed
-    let self_ty = expr_ty_adjusted(bcx, self_expr);
-    let self_scratch = match ty::get(self_ty).sty {
-        ty::ty_trait(_, _, ty::RegionTraitStore(..), _, _) => {
-            unpack_datum!(bcx, expr::trans_to_datum(bcx, self_expr))
-        }
-        _ => {
-            let d = scratch_datum(bcx, self_ty, "__trait_callee", false);
-            bcx = expr::trans_into(bcx, self_expr, expr::SaveIn(d.val));
-            // Arrange a temporary cleanup for the object in case something
-            // should go wrong before the method is actually *invoked*.
-            d.add_clean(bcx);
-            d
-        }
-    };
+    // Translate self_datum and take ownership of the value by
+    // converting to an rvalue.
+    let self_datum = unpack_datum!(
+        bcx, expr::trans(bcx, self_expr));
+    let self_datum = unpack_datum!(
+        bcx, self_datum.to_rvalue_datum(bcx, "trait_callee"));
+
+    // Convert to by-ref since `trans_trait_callee_from_llval` wants it
+    // that way.
+    let self_datum = unpack_datum!(
+        bcx, self_datum.to_ref_datum(bcx));
 
+    // Arrange cleanup in case something should go wrong before the
+    // actual call occurs.
+    let llval = self_datum.add_clean(bcx.fcx, arg_cleanup_scope);
 
     let callee_ty = node_id_type(bcx, callee_id);
-    trans_trait_callee_from_llval(bcx,
-                                  callee_ty,
-                                  n_method,
-                                  self_scratch.val,
-                                  Some(self_scratch.val))
+    trans_trait_callee_from_llval(bcx, callee_ty, n_method, llval)
 }
 
-pub fn trans_trait_callee_from_llval<'a>(
-                                     bcx: &'a Block<'a>,
-                                     callee_ty: ty::t,
-                                     n_method: uint,
-                                     llpair: ValueRef,
-                                     temp_cleanup: Option<ValueRef>)
-                                     -> Callee<'a> {
+pub fn trans_trait_callee_from_llval<'a>(bcx: &'a Block<'a>,
+                                         callee_ty: ty::t,
+                                         n_method: uint,
+                                         llpair: ValueRef)
+                                         -> Callee<'a> {
     /*!
      * Same as `trans_trait_callee()` above, except that it is given
      * a by-ref pointer to the object pair.
@@ -501,7 +495,6 @@ pub fn trans_trait_callee_from_llval<'a>(
         data: Method(MethodData {
             llfn: mptr,
             llself: llself,
-            temp_cleanup: temp_cleanup
         })
     };
 }
@@ -632,41 +625,38 @@ fn emit_vtable_methods(bcx: &Block,
     })
 }
 
-pub fn trans_trait_cast<'a>(
-                        bcx: &'a Block<'a>,
-                        val: &ast::Expr,
-                        id: ast::NodeId,
-                        dest: expr::Dest,
-                        obj: Option<Datum>)
-                        -> &'a Block<'a> {
+pub fn trans_trait_cast<'a>(bcx: &'a Block<'a>,
+                            datum: Datum<Expr>,
+                            id: ast::NodeId,
+                            dest: expr::Dest)
+                            -> &'a Block<'a> {
+    /*!
+     * Generates the code to convert from a pointer (`~T`, `&T`, etc)
+     * into an object (`~Trait`, `&Trait`, etc). This means creating a
+     * pair where the first word is the vtable and the second word is
+     * the pointer.
+     */
+
     let mut bcx = bcx;
     let _icx = push_ctxt("impl::trans_cast");
 
     let lldest = match dest {
         Ignore => {
-            return expr::trans_into(bcx, val, Ignore);
+            return datum.clean(bcx, "trait_cast", id);
         }
         SaveIn(dest) => dest
     };
 
     let ccx = bcx.ccx();
-    let v_ty = expr_ty(bcx, val);
+    let v_ty = datum.ty;
+    let llbox_ty = type_of(bcx.ccx(), datum.ty);
 
+    // Store the pointer into the first half of pair.
     let mut llboxdest = GEPi(bcx, lldest, [0u, abi::trt_field_box]);
-    // Just store the pointer into the pair. (Region/borrowed
-    // and boxed trait objects are represented as pairs, and
-    // have no type descriptor field.)
-    llboxdest = PointerCast(bcx,
-                            llboxdest,
-                            type_of(bcx.ccx(), v_ty).ptr_to());
-    bcx = match obj {
-        Some(datum) => {
-            datum.store_to_dest(bcx, SaveIn(llboxdest))
-        }
-        None => expr::trans_into(bcx, val, SaveIn(llboxdest))
-    };
+    llboxdest = PointerCast(bcx, llboxdest, llbox_ty.ptr_to());
+    bcx = datum.store_to(bcx, llboxdest);
 
-    // Store the vtable into the pair or triple.
+    // Store the vtable into the second half of pair.
     // This is structured a bit funny because of dynamic borrow failures.
     let origins = {
         let res = {
@@ -677,9 +667,9 @@ pub fn trans_trait_cast<'a>(
         res[0]
     };
     let vtable = get_vtable(bcx, v_ty, origins);
-    Store(bcx, vtable, PointerCast(bcx,
-                                   GEPi(bcx, lldest, [0u, abi::trt_field_vtable]),
-                                   val_ty(vtable).ptr_to()));
+    let llvtabledest = GEPi(bcx, lldest, [0u, abi::trt_field_vtable]);
+    let llvtabledest = PointerCast(bcx, llvtabledest, val_ty(vtable).ptr_to());
+    Store(bcx, vtable, llvtabledest);
 
     bcx
 }
diff --git a/src/librustc/middle/trans/mod.rs b/src/librustc/middle/trans/mod.rs
index e534e087cb8..7ac491edfeb 100644
--- a/src/librustc/middle/trans/mod.rs
+++ b/src/librustc/middle/trans/mod.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+pub mod doc;
 pub mod macros;
 pub mod inline;
 pub mod monomorphize;
@@ -44,3 +45,4 @@ pub mod type_;
 pub mod value;
 pub mod basic_block;
 pub mod llrepr;
+pub mod cleanup;
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index a05ca4296b1..a128a953eac 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -238,7 +238,6 @@ pub fn monomorphic_fn(ccx: @CrateContext,
                          None,
                          Some(psubsts),
                          fn_id.node,
-                         None,
                          []);
                 d
             }
diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs
index 2a8c23a6c32..c13bb139da7 100644
--- a/src/librustc/middle/trans/reflect.rs
+++ b/src/librustc/middle/trans/reflect.rs
@@ -61,7 +61,7 @@ impl<'a> Reflector<'a> {
         let bcx = self.bcx;
         let str_vstore = ty::vstore_slice(ty::ReStatic);
         let str_ty = ty::mk_str(bcx.tcx(), str_vstore);
-        let scratch = scratch_datum(bcx, str_ty, "", false);
+        let scratch = rvalue_scratch_datum(bcx, str_ty, "");
         let len = C_uint(bcx.ccx(), s.len());
         let c_str = PointerCast(bcx, C_cstr(bcx.ccx(), s), Type::i8p());
         Store(bcx, c_str, GEPi(bcx, scratch.val, [ 0, 0 ]));
@@ -90,6 +90,7 @@ impl<'a> Reflector<'a> {
     }
 
     pub fn visit(&mut self, ty_name: &str, args: &[ValueRef]) {
+        let fcx = self.bcx.fcx;
         let tcx = self.bcx.tcx();
         let mth_idx = ty::method_idx(
             tcx.sess.ident_of(~"visit_" + ty_name),
@@ -106,14 +107,13 @@ impl<'a> Reflector<'a> {
         let bool_ty = ty::mk_bool();
         let result = unpack_result!(bcx, callee::trans_call_inner(
             self.bcx, None, mth_ty, bool_ty,
-            |bcx| meth::trans_trait_callee_from_llval(bcx,
-                                                      mth_ty,
-                                                      mth_idx,
-                                                      v,
-                                                      None),
+            |bcx, _| meth::trans_trait_callee_from_llval(bcx,
+                                                         mth_ty,
+                                                         mth_idx,
+                                                         v),
             ArgVals(args), None, DontAutorefArg));
         let result = bool_to_i1(bcx, result);
-        let next_bcx = sub_block(bcx, "next");
+        let next_bcx = fcx.new_temp_block("next");
         CondBr(bcx, result, next_bcx.llbb, self.final_bcx.llbb);
         self.bcx = next_bcx
     }
@@ -298,7 +298,7 @@ impl<'a> Reflector<'a> {
                                       llfdecl,
                                       ty::mk_u64(),
                                       None);
-                init_function(&fcx, false, ty::mk_u64(), None, None);
+                init_function(&fcx, false, ty::mk_u64(), None);
 
                 let arg = unsafe {
                     //
@@ -308,13 +308,13 @@ impl<'a> Reflector<'a> {
                     //
                     llvm::LLVMGetParam(llfdecl, fcx.arg_pos(0u) as c_uint)
                 };
-                let mut bcx = fcx.entry_bcx.get().unwrap();
+                let bcx = fcx.entry_bcx.get().unwrap();
                 let arg = BitCast(bcx, arg, llptrty);
                 let ret = adt::trans_get_discr(bcx, repr, arg, Some(Type::i64()));
                 Store(bcx, ret, fcx.llretptr.get().unwrap());
                 match fcx.llreturn.get() {
-                    Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn),
-                    None => bcx = cleanup_block(bcx, Some(bcx.llbb))
+                    Some(llreturn) => Br(bcx, llreturn),
+                    None => {}
                 };
                 finish_fn(&fcx, bcx);
                 llfdecl
@@ -389,7 +389,8 @@ pub fn emit_calls_to_trait_visit_ty<'a>(
                                     visitor_val: ValueRef,
                                     visitor_trait_id: DefId)
                                     -> &'a Block<'a> {
-    let final = sub_block(bcx, "final");
+    let fcx = bcx.fcx;
+    let final = fcx.new_temp_block("final");
     let tydesc_ty = ty::get_tydesc_ty(bcx.ccx().tcx).unwrap();
     let tydesc_ty = type_of(bcx.ccx(), tydesc_ty);
     let mut r = Reflector {
diff --git a/src/librustc/middle/trans/tvec.rs b/src/librustc/middle/trans/tvec.rs
index 100f28af97d..1642d333a9a 100644
--- a/src/librustc/middle/trans/tvec.rs
+++ b/src/librustc/middle/trans/tvec.rs
@@ -17,6 +17,8 @@ use middle::trans::base::*;
 use middle::trans::base;
 use middle::trans::build::*;
 use middle::trans::callee;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common::*;
 use middle::trans::datum::*;
 use middle::trans::expr::{Dest, Ignore, SaveIn};
@@ -26,7 +28,6 @@ use middle::trans::machine::{llsize_of, nonzero_llsize_of, llsize_of_alloc};
 use middle::trans::type_::Type;
 use middle::trans::type_of;
 use middle::ty;
-use util::common::indenter;
 use util::ppaux::ty_to_str;
 
 use syntax::ast;
@@ -193,7 +194,6 @@ pub fn trans_fixed_vstore<'a>(
 
     debug!("trans_fixed_vstore(vstore_expr={}, dest={:?})",
            bcx.expr_to_str(vstore_expr), dest.to_str(bcx.ccx()));
-    let _indenter = indenter();
 
     let vt = vec_types_from_expr(bcx, vstore_expr);
 
@@ -214,17 +214,18 @@ pub fn trans_slice_vstore<'a>(
                           content_expr: &ast::Expr,
                           dest: expr::Dest)
                           -> &'a Block<'a> {
-    //!
-    //
-    // &[...] 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();
+    /*!
+     * &[...] 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 fcx = bcx.fcx;
+    let ccx = fcx.ccx;
+    let mut bcx = bcx;
 
     debug!("trans_slice_vstore(vstore_expr={}, dest={})",
            bcx.expr_to_str(vstore_expr), dest.to_str(ccx));
-    let _indenter = indenter();
 
     // Handle the &"..." case:
     match content_expr.node {
@@ -244,21 +245,29 @@ pub fn trans_slice_vstore<'a>(
     let count = elements_required(bcx, content_expr);
     debug!("vt={}, 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_vec(bcx.tcx(),
-                              ty::mt {ty: vt.unit_ty, mutbl: ast::MutMutable},
-                              ty::vstore_fixed(count));
-    let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to();
-    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,
+    let llfixed;
+    if count == 0 {
+        // Zero-length array: just use NULL as the data pointer
+        llfixed = C_null(vt.llunit_ty.ptr_to());
+    } else {
+        // Make a fixed-length backing array and allocate it on the stack.
+        llfixed = base::arrayalloca(bcx, vt.llunit_ty, llcount);
+
+        // Arrange for the backing array to be cleaned up.
+        let fixed_ty = ty::mk_vec(bcx.tcx(),
+                                  ty::mt {ty: vt.unit_ty,
+                                          mutbl: ast::MutMutable},
+                                  ty::vstore_fixed(count));
+        let llfixed_ty = type_of::type_of(bcx.ccx(), fixed_ty).ptr_to();
+        let llfixed_casted = BitCast(bcx, llfixed, llfixed_ty);
+        let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id);
+        fcx.schedule_drop_mem(cleanup_scope, llfixed_casted, fixed_ty);
+
+        // Generate the content into the backing array.
+        bcx = write_content(bcx, &vt, vstore_expr,
                             content_expr, SaveIn(llfixed));
+    }
 
     // Finally, create the slice pair itself.
     match dest {
@@ -278,16 +287,15 @@ pub fn trans_lit_str<'a>(
                      str_lit: @str,
                      dest: Dest)
                      -> &'a Block<'a> {
-    //!
-    //
-    // 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.
+    /*!
+     * 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={}, dest={})",
            bcx.expr_to_str(lit_expr),
            dest.to_str(bcx.ccx()));
-    let _indenter = indenter();
 
     match dest {
         Ignore => bcx,
@@ -308,20 +316,19 @@ pub fn trans_lit_str<'a>(
 }
 
 
-pub fn trans_uniq_or_managed_vstore<'a>(
-                                    bcx: &'a Block<'a>,
-                                    heap: heap,
-                                    vstore_expr: &ast::Expr,
-                                    content_expr: &ast::Expr)
-                                    -> DatumBlock<'a> {
-    //!
-    //
-    // @[...] or ~[...] (also @"..." or ~"...") allocate boxes in the
-    // appropriate heap and write the array elements into them.
+pub fn trans_uniq_or_managed_vstore<'a>(bcx: &'a Block<'a>,
+                                        heap: heap,
+                                        vstore_expr: &ast::Expr,
+                                        content_expr: &ast::Expr)
+                                        -> DatumBlock<'a, Expr> {
+    /*!
+     * @[...] or ~[...] (also @"..." or ~"...") allocate boxes in the
+     * appropriate heap and write the array elements into them.
+     */
 
     debug!("trans_uniq_or_managed_vstore(vstore_expr={}, heap={:?})",
            bcx.expr_to_str(vstore_expr), heap);
-    let _indenter = indenter();
+    let fcx = bcx.fcx;
 
     // Handle ~"".
     match heap {
@@ -334,7 +341,7 @@ pub fn trans_uniq_or_managed_vstore<'a>(
                             let llptrval = PointerCast(bcx, llptrval, Type::i8p());
                             let llsizeval = C_uint(bcx.ccx(), s.len());
                             let typ = ty::mk_str(bcx.tcx(), ty::vstore_uniq);
-                            let lldestval = scratch_datum(bcx, typ, "", false);
+                            let lldestval = rvalue_scratch_datum(bcx, typ, "");
                             let alloc_fn = langcall(bcx,
                                                     Some(lit.span),
                                                     "",
@@ -343,11 +350,8 @@ pub fn trans_uniq_or_managed_vstore<'a>(
                                 bcx,
                                 alloc_fn,
                                 [ llptrval, llsizeval ],
-                                Some(expr::SaveIn(lldestval.to_ref_llval(bcx)))).bcx;
-                            return DatumBlock {
-                                bcx: bcx,
-                                datum: lldestval
-                            };
+                                Some(expr::SaveIn(lldestval.val))).bcx;
+                            return DatumBlock(bcx, lldestval).to_expr_datumblock();
                         }
                         _ => {}
                     }
@@ -364,7 +368,11 @@ pub fn trans_uniq_or_managed_vstore<'a>(
 
     let Result {bcx, val} = alloc_vec(bcx, vt.unit_ty, count, heap);
 
-    add_clean_free(bcx, val, heap);
+    // Create a temporary scope lest execution should fail while
+    // constructing the vector.
+    let temp_scope = fcx.push_custom_cleanup_scope();
+    fcx.schedule_free_value(cleanup::CustomScope(temp_scope), val, heap);
+
     let dataptr = get_dataptr(bcx, get_bodyptr(bcx, val, vt.vec_ty));
 
     debug!("alloc_vec() returned val={}, dataptr={}",
@@ -373,9 +381,9 @@ pub fn trans_uniq_or_managed_vstore<'a>(
     let bcx = write_content(bcx, &vt, vstore_expr,
                             content_expr, SaveIn(dataptr));
 
-    revoke_clean(bcx, val);
+    fcx.pop_custom_cleanup_scope(temp_scope);
 
-    return immediate_rvalue_bcx(bcx, val, vt.vec_ty);
+    return immediate_rvalue_bcx(bcx, val, vt.vec_ty).to_expr_datumblock();
 }
 
 pub fn write_content<'a>(
@@ -386,13 +394,13 @@ pub fn write_content<'a>(
                      dest: Dest)
                      -> &'a Block<'a> {
     let _icx = push_ctxt("tvec::write_content");
+    let fcx = bcx.fcx;
     let mut bcx = bcx;
 
     debug!("write_content(vt={}, dest={}, 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::ExprLit(lit) => {
@@ -430,19 +438,19 @@ pub fn write_content<'a>(
                 }
 
                 SaveIn(lldest) => {
-                    let mut temp_cleanups = ~[];
+                    let temp_scope = fcx.push_custom_cleanup_scope();
                     for (i, element) in elements.iter().enumerate() {
                         let lleltptr = GEPi(bcx, lldest, [i]);
                         debug!("writing index {:?} with lleltptr={:?}",
                                i, bcx.val_to_str(lleltptr));
                         bcx = expr::trans_into(bcx, *element,
                                                SaveIn(lleltptr));
-                        add_clean_temp_mem(bcx, lleltptr, vt.unit_ty);
-                        temp_cleanups.push(lleltptr);
-                    }
-                    for cleanup in temp_cleanups.iter() {
-                        revoke_clean(bcx, *cleanup);
+                        fcx.schedule_drop_mem(
+                            cleanup::CustomScope(temp_scope),
+                            lleltptr,
+                            vt.unit_ty);
                     }
+                    fcx.pop_custom_cleanup_scope(temp_scope);
                 }
             }
             return bcx;
@@ -463,14 +471,16 @@ pub fn write_content<'a>(
                     // this can only happen as a result of OOM. So we just skip out on the
                     // cleanup since things would *probably* be broken at that point anyways.
 
-                    let elem = unpack_datum!(bcx, {
-                        expr::trans_to_datum(bcx, element)
-                    });
+                    let elem = unpack_datum!(bcx, expr::trans(bcx, element));
+                    assert!(!ty::type_moves_by_default(bcx.tcx(), elem.ty));
 
-                    iter_vec_loop(bcx, lldest, vt,
+                    let bcx = iter_vec_loop(bcx, lldest, vt,
                                   C_uint(bcx.ccx(), count), |set_bcx, lleltptr, _| {
-                        elem.copy_to(set_bcx, INIT, lleltptr)
-                    })
+                        elem.shallow_copy_and_take(set_bcx, lleltptr)
+                    });
+
+                    elem.add_clean_if_rvalue(bcx, element.id);
+                    bcx
                 }
             }
         }
@@ -522,15 +532,16 @@ pub fn elements_required(bcx: &Block, content_expr: &ast::Expr) -> uint {
     }
 }
 
-pub fn get_base_and_byte_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t)
+pub fn get_base_and_byte_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_byte_len()` which will handle any conversions for
-    // you.
+    /*!
+     * Converts a vector into the slice pair.  The vector should be
+     * stored in `llval` which should be by ref. If you have a datum,
+     * you would probably prefer to call
+     * `Datum::get_base_and_byte_len()`.
+     */
 
     let ccx = bcx.ccx();
     let vt = vec_types(bcx, vec_ty);
@@ -547,27 +558,32 @@ pub fn get_base_and_byte_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t)
             (base, len)
         }
         ty::vstore_slice(_) => {
+            assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty));
             let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base]));
             let count = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len]));
             let len = Mul(bcx, count, vt.llunit_size);
             (base, len)
         }
         ty::vstore_uniq | ty::vstore_box => {
+            assert!(type_is_immediate(bcx.ccx(), vt.vec_ty));
+            let llval = Load(bcx, llval);
             let body = get_bodyptr(bcx, llval, vec_ty);
             (get_dataptr(bcx, body), get_fill(bcx, body))
         }
     }
 }
 
-pub fn get_base_and_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t)
+pub 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.
+    /*!
+     * Converts a vector into the slice pair.  The vector should be
+     * stored in `llval` which should be by-reference.  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 = bcx.ccx();
     let vt = vec_types(bcx, vec_ty);
@@ -583,11 +599,14 @@ pub fn get_base_and_len(bcx: &Block, llval: ValueRef, vec_ty: ty::t)
             (base, C_uint(ccx, n))
         }
         ty::vstore_slice(_) => {
+            assert!(!type_is_immediate(bcx.ccx(), vt.vec_ty));
             let base = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_base]));
             let count = Load(bcx, GEPi(bcx, llval, [0u, abi::slice_elt_len]));
             (base, count)
         }
         ty::vstore_uniq | ty::vstore_box => {
+            assert!(type_is_immediate(bcx.ccx(), vt.vec_ty));
+            let llval = Load(bcx, llval);
             let body = get_bodyptr(bcx, llval, vec_ty);
             (get_dataptr(bcx, body), UDiv(bcx, get_fill(bcx, body), vt.llunit_size))
         }
@@ -606,12 +625,13 @@ pub fn iter_vec_loop<'r,
                      f: iter_vec_block<'r,'b>)
                      -> &'b Block<'b> {
     let _icx = push_ctxt("tvec::iter_vec_loop");
+    let fcx = bcx.fcx;
 
-    let next_bcx = sub_block(bcx, "iter_vec_loop: while next");
-    let loop_bcx = loop_scope_block(bcx, next_bcx, None, "iter_vec_loop", None);
-    let cond_bcx = scope_block(loop_bcx, None, "iter_vec_loop: loop cond");
-    let body_bcx = scope_block(loop_bcx, None, "iter_vec_loop: body: main");
-    let inc_bcx = scope_block(loop_bcx, None, "iter_vec_loop: loop inc");
+    let next_bcx = fcx.new_temp_block("expr_repeat: while next");
+    let loop_bcx = fcx.new_temp_block("expr_repeat");
+    let cond_bcx = fcx.new_temp_block("expr_repeat: loop cond");
+    let body_bcx = fcx.new_temp_block("expr_repeat: body: set");
+    let inc_bcx = fcx.new_temp_block("expr_repeat: body: inc");
     Br(bcx, loop_bcx.llbb);
 
     let loop_counter = {
@@ -663,6 +683,7 @@ pub fn iter_vec_raw<'r,
                     f: iter_vec_block<'r,'b>)
                     -> &'b Block<'b> {
     let _icx = push_ctxt("tvec::iter_vec_raw");
+    let fcx = bcx.fcx;
 
     let vt = vec_types(bcx, vec_ty);
     if (vt.llunit_alloc_size == 0) {
@@ -676,14 +697,14 @@ pub fn iter_vec_raw<'r,
         let data_end_ptr = pointer_add_byte(bcx, data_ptr, fill);
 
         // Now perform the iteration.
-        let header_bcx = base::sub_block(bcx, "iter_vec_loop_header");
+        let header_bcx = fcx.new_temp_block("iter_vec_loop_header");
         Br(bcx, header_bcx.llbb);
         let data_ptr =
             Phi(header_bcx, val_ty(data_ptr), [data_ptr], [bcx.llbb]);
         let not_yet_at_end =
             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");
+        let body_bcx = fcx.new_temp_block("iter_vec_loop_body");
+        let next_bcx = fcx.new_temp_block("iter_vec_next");
         CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb);
         let body_bcx = f(body_bcx, data_ptr, vt.unit_ty);
         AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr,
@@ -691,7 +712,6 @@ pub fn iter_vec_raw<'r,
                          body_bcx.llbb);
         Br(body_bcx, header_bcx.llbb);
         next_bcx
-
     }
 }
 
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index ae96f43b07d..4db89fbecce 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -68,10 +68,10 @@ pub fn type_of_rust_fn(cx: &CrateContext,
     atys.push_all(type_of_explicit_args(cx, inputs));
 
     // Use the output as the actual return value if it's immediate.
-    if !use_out_pointer && !ty::type_is_voidish(cx.tcx, output) {
-        Type::func(atys, &lloutputtype)
-    } else {
+    if use_out_pointer || return_type_is_void(cx, output) {
         Type::func(atys, &Type::void())
+    } else {
+        Type::func(atys, &lloutputtype)
     }
 }
 
diff --git a/src/librustc/middle/trans/write_guard.rs b/src/librustc/middle/trans/write_guard.rs
index 0b6a18dd7e1..5b310feb58d 100644
--- a/src/librustc/middle/trans/write_guard.rs
+++ b/src/librustc/middle/trans/write_guard.rs
@@ -16,19 +16,17 @@
 
 
 use middle::borrowck::{RootInfo, root_map_key};
-use middle::trans::base::*;
+use middle::trans::cleanup;
 use middle::trans::common::*;
 use middle::trans::datum::*;
 use syntax::codemap::Span;
 use syntax::ast;
 
-pub fn root_and_write_guard<'a>(
-                            datum: &Datum,
-                            bcx: &'a Block<'a>,
-                            span: Span,
-                            expr_id: ast::NodeId,
-                            derefs: uint)
-                            -> &'a Block<'a> {
+pub fn root_and_write_guard<'a, K:KindOps>(datum: &Datum<K>,
+                                           bcx: &'a Block<'a>,
+                                           span: Span,
+                                           expr_id: ast::NodeId,
+                                           derefs: uint) -> &'a Block<'a> {
     let key = root_map_key { id: expr_id, derefs: derefs };
     debug!("write_guard::root_and_write_guard(key={:?})", key);
 
@@ -43,13 +41,11 @@ pub fn root_and_write_guard<'a>(
     }
 }
 
-fn root<'a>(
-        datum: &Datum,
-        bcx: &'a Block<'a>,
-        _: Span,
-        root_key: root_map_key,
-        root_info: RootInfo)
-        -> &'a Block<'a> {
+fn root<'a, K:KindOps>(datum: &Datum<K>,
+                       bcx: &'a Block<'a>,
+                       _span: Span,
+                       root_key: root_map_key,
+                       root_info: RootInfo) -> &'a Block<'a> {
     //! 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
@@ -58,17 +54,12 @@ fn root<'a>(
     debug!("write_guard::root(root_key={:?}, root_info={:?}, datum={:?})",
            root_key, root_info, datum.to_str(bcx.ccx()));
 
-    // First, root the datum. Note that we must zero this value,
+    // Root the datum. Note that we must zero this value,
     // because sometimes we root on one path but not another.
     // See e.g. #4904.
-    let scratch = scratch_datum(bcx, datum.ty, "__write_guard", true);
-    datum.copy_to_datum(bcx, INIT, scratch);
-    let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope);
-    add_clean_temp_mem_in_scope(cleanup_bcx,
-                                root_info.scope,
-                                scratch.val,
-                                scratch.ty);
-
-    bcx
+    lvalue_scratch_datum(
+        bcx, datum.ty, "__write_guard", true,
+        cleanup::AstScope(root_info.scope), (),
+        |(), bcx, llval| datum.shallow_copy_and_take(bcx, llval)).bcx
 }
 
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index fd3bf0deae7..538a3c89bef 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -1455,11 +1455,6 @@ pub fn subst(cx: ctxt,
 
 // Type utilities
 
-pub fn type_is_voidish(tcx: ctxt, ty: t) -> bool {
-    //! "nil" and "bot" are void types in that they represent 0 bits of information
-    type_is_nil(ty) || type_is_bot(ty) || type_is_empty(tcx, ty)
-}
-
 pub fn type_is_nil(ty: t) -> bool { get(ty).sty == ty_nil }
 
 pub fn type_is_bot(ty: t) -> bool {
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 1f643cfc80c..99cdbacbd5b 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -3983,8 +3983,31 @@ pub fn ast_expr_vstore_to_vstore(fcx: @FnCtxt,
         ast::ExprVstoreUniq => ty::vstore_uniq,
         ast::ExprVstoreBox => ty::vstore_box,
         ast::ExprVstoreSlice | ast::ExprVstoreMutSlice => {
-            let r = fcx.infcx().next_region_var(infer::AddrOfSlice(e.span));
-            ty::vstore_slice(r)
+            match e.node {
+                ast::ExprLit(..) |
+                ast::ExprVec([], _) => {
+                    // string literals and *empty slices* live in static memory
+                    ty::vstore_slice(ty::ReStatic)
+                }
+                ast::ExprRepeat(..) |
+                ast::ExprVec(..) => {
+                    // vector literals are temporaries on the stack
+                    match fcx.tcx().region_maps.temporary_scope(e.id) {
+                        Some(scope) => {
+                            let r = ty::ReScope(scope);
+                            ty::vstore_slice(r)
+                        }
+                        None => {
+                            // this slice occurs in a static somewhere
+                            ty::vstore_slice(ty::ReStatic)
+                        }
+                    }
+                }
+                _ => {
+                    fcx.ccx.tcx.sess.span_bug(
+                        e.span, format!("vstore with unexpected contents"))
+                }
+            }
         }
     }
 }
@@ -4103,7 +4126,7 @@ pub fn check_intrinsic_type(ccx: @CrateCtxt, it: &ast::ForeignItem) {
             "uninit" => (1u, ~[], param(ccx, 0u)),
             "forget" => (1u, ~[ param(ccx, 0) ], ty::mk_nil()),
             "transmute" => (2, ~[ param(ccx, 0) ], param(ccx, 1)),
-            "move_val" | "move_val_init" => {
+            "move_val_init" => {
                 (1u,
                  ~[
                     ty::mk_mut_rptr(tcx, ty::ReLateBound(it.id, ty::BrAnon(0)), param(ccx, 0)),
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index ad0482cf4f4..c6e43bf968e 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -55,21 +55,26 @@ pub struct Rcx {
     repeating_scope: ast::NodeId,
 }
 
-fn encl_region_of_def(fcx: @FnCtxt, def: ast::Def) -> ty::Region {
+fn region_of_def(fcx: @FnCtxt, def: ast::Def) -> ty::Region {
+    /*!
+     * Returns the validity region of `def` -- that is, how long
+     * is `def` valid?
+     */
+
     let tcx = fcx.tcx();
     match def {
         DefLocal(node_id, _) | DefArg(node_id, _) |
         DefSelf(node_id, _) | DefBinding(node_id, _) => {
-            tcx.region_maps.encl_region(node_id)
+            tcx.region_maps.var_region(node_id)
         }
         DefUpvar(_, subdef, closure_id, body_id) => {
             match ty::ty_closure_sigil(fcx.node_ty(closure_id)) {
-                BorrowedSigil => encl_region_of_def(fcx, *subdef),
+                BorrowedSigil => region_of_def(fcx, *subdef),
                 ManagedSigil | OwnedSigil => ReScope(body_id)
             }
         }
         _ => {
-            tcx.sess.bug(format!("unexpected def in encl_region_of_def: {:?}",
+            tcx.sess.bug(format!("unexpected def in region_of_def: {:?}",
                               def))
         }
     }
@@ -193,7 +198,6 @@ fn visit_item(_rcx: &mut Rcx, _item: &ast::Item) {
 }
 
 fn visit_block(rcx: &mut Rcx, b: &ast::Block) {
-    rcx.fcx.tcx().region_maps.record_cleanup_scope(b.id);
     visit::walk_block(rcx, b, ());
 }
 
@@ -209,6 +213,7 @@ fn visit_arm(rcx: &mut Rcx, arm: &ast::Arm) {
 fn visit_local(rcx: &mut Rcx, l: &ast::Local) {
     // see above
     constrain_bindings_in_pat(l.pat, rcx);
+    guarantor::for_local(rcx, l);
     visit::walk_local(rcx, l, ());
 }
 
@@ -239,9 +244,9 @@ fn constrain_bindings_in_pat(pat: &ast::Pat, rcx: &mut Rcx) {
         // that the lifetime of any regions that appear in a
         // variable's type enclose at least the variable's scope.
 
-        let encl_region = tcx.region_maps.encl_region(id);
+        let var_region = tcx.region_maps.var_region(id);
         constrain_regions_in_type_of_node(
-            rcx, id, encl_region,
+            rcx, id, var_region,
             infer::BindingTypeIsNotValidAtDecl(span));
     })
 }
@@ -255,55 +260,6 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
         method_map.get().contains_key(&expr.id)
     };
 
-    // Record cleanup scopes, which are used by borrowck to decide the
-    // maximum lifetime of a temporary rvalue.  These were derived by
-    // examining where trans creates block scopes, not because this
-    // reflects some principled decision around temporary lifetimes.
-    // Ordinarily this would seem like something that should be setup
-    // in region, but we need to know which uses of operators are
-    // overloaded.  See #3511.
-    let tcx = rcx.fcx.tcx();
-    match expr.node {
-        // You'd think that x += y where `+=` is overloaded would be a
-        // cleanup scope. You'd be... kind of right. In fact the
-        // handling of `+=` and friends in trans for overloaded
-        // operators is a hopeless mess and I can't figure out how to
-        // represent it. - ndm
-        //
-        // ast::expr_assign_op(..) |
-
-        ast::ExprIndex(..) |
-        ast::ExprBinary(..) |
-        ast::ExprUnary(..) if has_method_map => {
-            tcx.region_maps.record_cleanup_scope(expr.id);
-        }
-        ast::ExprBinary(_, ast::BiAnd, lhs, rhs) |
-        ast::ExprBinary(_, ast::BiOr, lhs, rhs) => {
-            tcx.region_maps.record_cleanup_scope(lhs.id);
-            tcx.region_maps.record_cleanup_scope(rhs.id);
-        }
-        ast::ExprCall(..) |
-        ast::ExprMethodCall(..) => {
-            tcx.region_maps.record_cleanup_scope(expr.id);
-        }
-        ast::ExprMatch(_, ref arms) => {
-            tcx.region_maps.record_cleanup_scope(expr.id);
-            for arm in arms.iter() {
-                for guard in arm.guard.iter() {
-                    tcx.region_maps.record_cleanup_scope(guard.id);
-                }
-            }
-        }
-        ast::ExprLoop(ref body, _) => {
-            tcx.region_maps.record_cleanup_scope(body.id);
-        }
-        ast::ExprWhile(cond, ref body) => {
-            tcx.region_maps.record_cleanup_scope(cond.id);
-            tcx.region_maps.record_cleanup_scope(body.id);
-        }
-        _ => {}
-    }
-
     // Check any autoderefs or autorefs that appear.
     {
         let adjustments = rcx.fcx.inh.adjustments.borrow();
@@ -701,10 +657,10 @@ fn constrain_free_variables(rcx: &mut Rcx,
     for freevar in get_freevars(tcx, expr.id).iter() {
         debug!("freevar def is {:?}", freevar.def);
         let def = freevar.def;
-        let en_region = encl_region_of_def(rcx.fcx, def);
-        debug!("en_region = {}", en_region.repr(tcx));
+        let def_region = region_of_def(rcx.fcx, def);
+        debug!("def_region = {}", def_region.repr(tcx));
         rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span),
-                        region, en_region);
+                        region, def_region);
     }
 }
 
@@ -873,6 +829,30 @@ pub mod guarantor {
         }
     }
 
+    pub fn for_local(rcx: &mut Rcx, local: &ast::Local) {
+        /*!
+         * Link the lifetimes of any ref bindings in a let
+         * pattern to the lifetimes in the initializer.
+         *
+         * For example, given something like this:
+         *
+         *    let &Foo(ref x) = ...;
+         *
+         * this would ensure that the lifetime 'a of the
+         * region pointer being matched must be >= the lifetime
+         * of the ref binding.
+         */
+
+        debug!("regionck::for_match()");
+        let init_expr = match local.init {
+            None => { return; }
+            Some(e) => e
+        };
+        let init_guarantor = guarantor(rcx, init_expr);
+        debug!("init_guarantor={}", init_guarantor.repr(rcx.tcx()));
+        link_ref_bindings_in_pat(rcx, local.pat, init_guarantor);
+    }
+
     pub fn for_autoref(rcx: &mut Rcx,
                        expr: &ast::Expr,
                        autoderefs: uint,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 213b4ebf257..b9916749fe9 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -673,6 +673,14 @@ impl Repr for ast::Item {
     }
 }
 
+impl Repr for ast::Stmt {
+    fn repr(&self, tcx: ctxt) -> ~str {
+        format!("stmt({}: {})",
+                ast_util::stmt_id(self),
+                pprust::stmt_to_str(self, tcx.sess.intr()))
+    }
+}
+
 impl Repr for ast::Pat {
     fn repr(&self, tcx: ctxt) -> ~str {
         format!("pat({}: {})",
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 74f94ba00f5..b9a36137db2 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -276,7 +276,8 @@ pub fn run(mut crate: clean::Crate, dst: Path) {
         write!(w, "var allPaths = \\{");
         for (i, (&id, &(ref fqp, short))) in cache.paths.iter().enumerate() {
             if i > 0 { write!(w, ","); }
-            write!(w, "'{}':\\{type:'{}',name:'{}'\\}", id, short, *fqp.last());
+            write!(w, "'{}':\\{type:'{}',name:'{}'\\}",
+                   id, short, *fqp.last());
         }
         write!(w, "\\};");
         w.flush();
diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs
index c52ff2d088d..546f5550387 100644
--- a/src/libstd/ascii.rs
+++ b/src/libstd/ascii.rs
@@ -521,7 +521,8 @@ mod tests {
 
     #[test]
     fn test_ascii_vec() {
-        assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
+        let test = &[40u8, 32u8, 59u8];
+        assert_eq!(test.to_ascii(), v2ascii!([40, 32, 59]));
         assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
         // FIXME: #5475 borrowchk error, owned vectors do not live long enough
         // if chained-from directly
@@ -587,14 +588,18 @@ mod tests {
 
         assert_eq!("zoä华".to_ascii_opt(), None);
 
-        assert_eq!((&[127u8, 128u8, 255u8]).to_ascii_opt(), None);
+        let test1 = &[127u8, 128u8, 255u8];
+        assert_eq!((test1).to_ascii_opt(), None);
 
         let v = [40u8, 32u8, 59u8];
-        assert_eq!(v.to_ascii_opt(), Some(v2ascii!(&[40, 32, 59])));
+        let v2 = v2ascii!(&[40, 32, 59]);
+        assert_eq!(v.to_ascii_opt(), Some(v2));
         let v = [127u8, 128u8, 255u8];
         assert_eq!(v.to_ascii_opt(), None);
 
-        assert_eq!("( ;".to_ascii_opt(), Some(v2ascii!(&[40, 32, 59])));
+        let v = "( ;";
+        let v2 = v2ascii!(&[40, 32, 59]);
+        assert_eq!(v.to_ascii_opt(), Some(v2));
         assert_eq!("zoä华".to_ascii_opt(), None);
 
         assert_eq!((~[40u8, 32u8, 59u8]).into_ascii_opt(), Some(v2ascii!(~[40, 32, 59])));
diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs
index bf9e28f3e97..985a387ee2b 100644
--- a/src/libstd/comm/mod.rs
+++ b/src/libstd/comm/mod.rs
@@ -1202,7 +1202,7 @@ mod test {
     })
 
     test!(fn oneshot_single_thread_peek_open() {
-        let (port, _) = Chan::<int>::new();
+        let (port, _chan) = Chan::<int>::new();
         assert_eq!(port.try_recv(), Empty);
     })
 
diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs
index f036131d211..a6caa1bfc29 100644
--- a/src/libstd/io/mem.rs
+++ b/src/libstd/io/mem.rs
@@ -406,7 +406,8 @@ mod test {
 
     #[test]
     fn test_read_char() {
-        let mut r = BufReader::new(bytes!("Việt"));
+        let b = bytes!("Việt");
+        let mut r = BufReader::new(b);
         assert_eq!(r.read_char(), Some('V'));
         assert_eq!(r.read_char(), Some('i'));
         assert_eq!(r.read_char(), Some('ệ'));
@@ -416,7 +417,8 @@ mod test {
 
     #[test]
     fn test_read_bad_char() {
-        let mut r = BufReader::new(bytes!(0x80));
+        let b = bytes!(0x80);
+        let mut r = BufReader::new(b);
         assert_eq!(r.read_char(), None);
     }
 
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index a8b7e8e00ea..4c8a640a849 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -181,7 +181,7 @@ mod tests {
         let io = ~[];
         let args = ProcessConfig {
             program: "/bin/sh",
-            args: [~"-c", ~"true"],
+            args: &[~"-c", ~"true"],
             env: None,
             cwd: None,
             io: io,
@@ -198,7 +198,7 @@ mod tests {
         let io = ~[];
         let args = ProcessConfig {
             program: "if-this-is-a-binary-then-the-world-has-ended",
-            args: [],
+            args: &[],
             env: None,
             cwd: None,
             io: io,
@@ -215,7 +215,7 @@ mod tests {
         let io = ~[];
         let args = ProcessConfig {
             program: "/bin/sh",
-            args: [~"-c", ~"exit 1"],
+            args: &[~"-c", ~"exit 1"],
             env: None,
             cwd: None,
             io: io,
@@ -231,7 +231,7 @@ mod tests {
         let io = ~[];
         let args = ProcessConfig {
             program: "/bin/sh",
-            args: [~"-c", ~"kill -1 $$"],
+            args: &[~"-c", ~"kill -1 $$"],
             env: None,
             cwd: None,
             io: io,
@@ -274,7 +274,7 @@ mod tests {
         let io = ~[Ignored, CreatePipe(false, true)];
         let args = ProcessConfig {
             program: "/bin/sh",
-            args: [~"-c", ~"echo foobar"],
+            args: &[~"-c", ~"echo foobar"],
             env: None,
             cwd: None,
             io: io,
@@ -289,7 +289,7 @@ mod tests {
         let cwd = Some("/");
         let args = ProcessConfig {
             program: "/bin/sh",
-            args: [~"-c", ~"pwd"],
+            args: &[~"-c", ~"pwd"],
             env: None,
             cwd: cwd,
             io: io,
@@ -304,7 +304,7 @@ mod tests {
                    CreatePipe(false, true)];
         let args = ProcessConfig {
             program: "/bin/sh",
-            args: [~"-c", ~"read line; echo $line"],
+            args: &[~"-c", ~"read line; echo $line"],
             env: None,
             cwd: None,
             io: io,
diff --git a/src/libstd/option.rs b/src/libstd/option.rs
index bdec67e5d9f..aab98f19e15 100644
--- a/src/libstd/option.rs
+++ b/src/libstd/option.rs
@@ -48,6 +48,7 @@ use kinds::Send;
 use str::OwnedStr;
 use to_str::ToStr;
 use util;
+use vec;
 
 /// The option type
 #[deriving(Clone, DeepClone, Eq, Ord, TotalEq, TotalOrd, ToStr)]
@@ -98,6 +99,24 @@ impl<T> Option<T> {
         match *self { Some(ref mut x) => Some(x), None => None }
     }
 
+    /// Convert from `Option<T>` to `&[T]` (without copying)
+    #[inline]
+    pub fn as_slice<'r>(&'r self) -> &'r [T] {
+        match *self {
+            Some(ref x) => vec::ref_slice(x),
+            None => &[]
+        }
+    }
+
+    /// Convert from `Option<T>` to `&[T]` (without copying)
+    #[inline]
+    pub fn as_mut_slice<'r>(&'r mut self) -> &'r mut [T] {
+        match *self {
+            Some(ref mut x) => vec::mut_ref_slice(x),
+            None => &mut []
+        }
+    }
+
     /////////////////////////////////////////////////////////////////////////
     // Getting to contained values
     /////////////////////////////////////////////////////////////////////////
diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs
index 7b94de6c094..e2ddabc1714 100644
--- a/src/libstd/path/posix.rs
+++ b/src/libstd/path/posix.rs
@@ -237,7 +237,10 @@ impl GenericPath for Path {
             let mut ita = self.components();
             let mut itb = other.components();
             if bytes!(".") == self.repr {
-                return itb.next() != Some(bytes!(".."));
+                return match itb.next() {
+                    None => true,
+                    Some(b) => b != bytes!("..")
+                };
             }
             loop {
                 match (ita.next(), itb.next()) {
@@ -463,7 +466,10 @@ mod tests {
 
     macro_rules! b(
         ($($arg:expr),+) => (
-            bytes!($($arg),+)
+            {
+                static the_bytes: &'static [u8] = bytes!($($arg),+);
+                the_bytes
+            }
         )
     )
 
@@ -689,7 +695,8 @@ mod tests {
             );
             (v: $path:expr, $op:ident, $exp:expr) => (
                 {
-                    let path = Path::new($path);
+                    let arg = $path;
+                    let path = Path::new(arg);
                     assert_eq!(path.$op(), $exp);
                 }
             );
diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs
index f8d80599151..a42fdabef88 100644
--- a/src/libstd/path/windows.rs
+++ b/src/libstd/path/windows.rs
@@ -1079,7 +1079,10 @@ mod tests {
 
     macro_rules! b(
         ($($arg:expr),+) => (
-            bytes!($($arg),+)
+            {
+                static the_bytes: &'static [u8] = bytes!($($arg),+);
+                the_bytes
+            }
         )
     )
 
@@ -1377,20 +1380,23 @@ mod tests {
         macro_rules! t(
             (s: $path:expr, $op:ident, $exp:expr) => (
                 {
-                    let path = Path::new($path);
+                    let path = $path;
+                    let path = Path::new(path);
                     assert_eq!(path.$op(), Some($exp));
                 }
             );
             (s: $path:expr, $op:ident, $exp:expr, opt) => (
                 {
-                    let path = Path::new($path);
+                    let path = $path;
+                    let path = Path::new(path);
                     let left = path.$op();
                     assert_eq!(left, $exp);
                 }
             );
             (v: $path:expr, $op:ident, $exp:expr) => (
                 {
-                    let path = Path::new($path);
+                    let path = $path;
+                    let path = Path::new(path);
                     assert_eq!(path.$op(), $exp);
                 }
             )
diff --git a/src/libstd/rt/crate_map.rs b/src/libstd/rt/crate_map.rs
index 16c1ad25448..6ea12659e77 100644
--- a/src/libstd/rt/crate_map.rs
+++ b/src/libstd/rt/crate_map.rs
@@ -130,14 +130,14 @@ mod tests {
         let child_crate = CrateMap {
             version: 2,
             entries: entries,
-            children: [],
+            children: &[],
             event_loop_factory: None,
         };
 
         let root_crate = CrateMap {
             version: 2,
-            entries: [],
-            children: [&child_crate, &child_crate],
+            entries: &[],
+            children: &[&child_crate, &child_crate],
             event_loop_factory: None,
         };
 
@@ -157,29 +157,29 @@ mod tests {
         let mut level3: u32 = 3;
         let child_crate2 = CrateMap {
             version: 2,
-            entries: [
+            entries: &[
                 ModEntry { name: "c::m1", log_level: &mut level2},
                 ModEntry { name: "c::m2", log_level: &mut level3},
             ],
-            children: [],
+            children: &[],
             event_loop_factory: None,
         };
 
         let child_crate1 = CrateMap {
             version: 2,
-            entries: [
+            entries: &[
                 ModEntry { name: "t::f1", log_level: &mut 1},
             ],
-            children: [&child_crate2],
+            children: &[&child_crate2],
             event_loop_factory: None,
         };
 
         let root_crate = CrateMap {
             version: 2,
-            entries: [
+            entries: &[
                 ModEntry { name: "t::f2", log_level: &mut 0},
             ],
-            children: [&child_crate1],
+            children: &[&child_crate1],
             event_loop_factory: None,
         };
 
diff --git a/src/libstd/unstable/intrinsics.rs b/src/libstd/unstable/intrinsics.rs
index 2649ca897e5..198df3090ee 100644
--- a/src/libstd/unstable/intrinsics.rs
+++ b/src/libstd/unstable/intrinsics.rs
@@ -292,12 +292,6 @@ extern "rust-intrinsic" {
     /// elements.
     pub fn size_of<T>() -> uint;
 
-    /// Move a value to a memory location containing a value.
-    ///
-    /// Drop glue is run on the destination, which must contain a
-    /// valid Rust value.
-    pub fn move_val<T>(dst: &mut T, src: T);
-
     /// Move a value to an uninitialized memory location.
     ///
     /// Drop glue is not run on the destination.
diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs
index 20684bf4c49..a7310bc75f1 100644
--- a/src/libstd/vec.rs
+++ b/src/libstd/vec.rs
@@ -209,6 +209,25 @@ pub fn build<A>(size: Option<uint>, builder: |push: |v: A||) -> ~[A] {
     vec
 }
 
+/**
+ * Converts a pointer to A into a slice of length 1 (without copying).
+ */
+pub fn ref_slice<'a, A>(s: &'a A) -> &'a [A] {
+    unsafe {
+        cast::transmute(Slice { data: s, len: 1 })
+    }
+}
+
+/**
+ * Converts a pointer to A into a slice of length 1 (without copying).
+ */
+pub fn mut_ref_slice<'a, A>(s: &'a mut A) -> &'a mut [A] {
+    unsafe {
+        let ptr: *A = cast::transmute(s);
+        cast::transmute(Slice { data: ptr, len: 1 })
+    }
+}
+
 /// An iterator over the slices of a vector separated by elements that
 /// match a predicate function.
 pub struct SplitIterator<'a, T> {
@@ -2048,6 +2067,9 @@ pub trait MutableVector<'a, T> {
     /// Returns an iterator that allows modifying each value
     fn mut_iter(self) -> VecMutIterator<'a, T>;
 
+    /// Returns a mutable pointer to the last item in the vector.
+    fn mut_last(self) -> &'a mut T;
+
     /// Returns a reversed iterator that allows modifying each value
     fn mut_rev_iter(self) -> MutRevIterator<'a, T>;
 
@@ -2311,6 +2333,13 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] {
     }
 
     #[inline]
+    fn mut_last(self) -> &'a mut T {
+        let len = self.len();
+        if len == 0 { fail!("mut_last: empty vector") }
+        &mut self[len - 1]
+    }
+
+    #[inline]
     fn mut_rev_iter(self) -> MutRevIterator<'a, T> {
         self.mut_iter().invert()
     }
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 89aa03a46ba..598e30957e4 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -567,11 +567,7 @@ pub fn visit_ids_for_inlined_item<O: IdVisitingOperation>(item: &InlinedItem,
         visited_outermost: false,
     };
 
-    match *item {
-        IIItem(i) => id_visitor.visit_item(i, ()),
-        IIForeign(i) => id_visitor.visit_foreign_item(i, ()),
-        IIMethod(_, _, m) => visit::walk_method_helper(&mut id_visitor, m, ()),
-    }
+    visit::walk_inlined_item(&mut id_visitor, item, ());
 }
 
 struct IdRangeComputingVisitor {
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index b1b38d6dc90..7d46f6e4594 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -138,15 +138,16 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
 
             // to:
             //
-            // {
-            //   let _i = &mut <src_expr>;
-            //   ['<ident>:] loop {
-            //       match i.next() {
+            //   match &mut <src_expr> {
+            //     i => {
+            //       ['<ident>:] loop {
+            //         match i.next() {
             //           None => break,
             //           Some(<src_pat>) => <src_loop_block>
+            //         }
             //       }
+            //     }
             //   }
-            // }
 
             let local_ident = token::gensym_ident("i");
             let next_ident = fld.cx.ident_of("next");
@@ -155,10 +156,6 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
             let local_path = fld.cx.path_ident(span, local_ident);
             let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
 
-            // `let i = &mut <src_expr>`
-            let iter_decl_stmt = fld.cx.stmt_let(span, false, local_ident,
-                                                 fld.cx.expr_mut_addr_of(span, src_expr));
-
             // `None => break ['<ident>];`
             let none_arm = {
                 // FIXME #6993: this map goes away:
@@ -186,16 +183,13 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
                                         ast::ExprLoop(fld.cx.block_expr(match_expr),
                                                       opt_ident));
 
-            // `{ let ... ;  loop { ... } }`
-            let block = fld.cx.block(span,
-                                     ~[iter_decl_stmt],
-                                     Some(loop_expr));
+            // `i => loop { ... }`
 
-            @ast::Expr {
-                id: ast::DUMMY_NODE_ID,
-                node: ast::ExprBlock(block),
-                span: span,
-            }
+            // `match &mut <src_expr> { i => loop { ... } }`
+            let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
+            let i_pattern = fld.cx.pat_ident(span, local_ident);
+            let arm = fld.cx.arm(span, ~[i_pattern], loop_expr);
+            fld.cx.expr_match(span, discrim, ~[arm])
         }
 
         _ => noop_fold_expr(e, fld)
diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs
index 9ae13ddeb02..33832a8c62d 100644
--- a/src/libsyntax/ext/format.rs
+++ b/src/libsyntax/ext/format.rs
@@ -634,17 +634,24 @@ impl<'a> Context<'a> {
                                      self.ecx.expr_ident(e.span, lname)));
         }
 
+        // Now create a vector containing all the arguments
+        let slicename = self.ecx.ident_of("__args_vec");
+        {
+            let args = names.move_iter().map(|a| a.unwrap());
+            let mut args = locals.move_iter().chain(args);
+            let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
+            lets.push(self.ecx.stmt_let(self.fmtsp, false, slicename, args));
+        }
+
         // Now create the fmt::Arguments struct with all our locals we created.
-        let args = names.move_iter().map(|a| a.unwrap());
-        let mut args = locals.move_iter().chain(args);
         let fmt = self.ecx.expr_ident(self.fmtsp, static_name);
-        let args = self.ecx.expr_vec_slice(self.fmtsp, args.collect());
+        let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
         let result = self.ecx.expr_call_global(self.fmtsp, ~[
                 self.ecx.ident_of("std"),
                 self.ecx.ident_of("fmt"),
                 self.ecx.ident_of("Arguments"),
                 self.ecx.ident_of("new"),
-            ], ~[fmt, args]);
+            ], ~[fmt, args_slice]);
 
         // We did all the work of making sure that the arguments
         // structure is safe, so we can safely have an unsafe block.
diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs
index 247962e0b94..326f712d5b2 100644
--- a/src/libsyntax/opt_vec.rs
+++ b/src/libsyntax/opt_vec.rs
@@ -42,12 +42,31 @@ impl<T> OptVec<T> {
                 v.push(t);
                 return;
             }
-            Empty => {}
+            Empty => {
+                *self = Vec(~[t]);
+            }
         }
+    }
+
+    pub fn pop(&mut self) -> T {
+        match *self {
+            Vec(ref mut v) => v.pop(),
+            Empty => fail!("pop from empty opt_vec")
+        }
+    }
 
-        // FIXME(#5074): flow insensitive means we can't move
-        // assignment inside `match`
-        *self = Vec(~[t]);
+    pub fn last<'a>(&'a self) -> &'a T {
+        match *self {
+            Vec(ref v) => v.last(),
+            Empty => fail!("last on empty opt_vec")
+        }
+    }
+
+    pub fn mut_last<'a>(&'a mut self) -> &'a mut T {
+        match *self {
+            Vec(ref mut v) => v.mut_last(),
+            Empty => fail!("mut_last on empty opt_vec")
+        }
     }
 
     pub fn map<U>(&self, op: |&T| -> U) -> OptVec<U> {
@@ -82,6 +101,16 @@ impl<T> OptVec<T> {
         }
     }
 
+    pub fn swap_remove(&mut self, index: uint) {
+        match *self {
+            Empty => { fail!("Index out of bounds"); }
+            Vec(ref mut v) => {
+                assert!(index < v.len());
+                v.swap_remove(index);
+            }
+        }
+    }
+
     #[inline]
     pub fn iter<'r>(&'r self) -> OptVecIterator<'r, T> {
         match *self {
@@ -166,6 +195,16 @@ impl<'a, T> Iterator<&'a T> for OptVecIterator<'a, T> {
     }
 }
 
+impl<'a, T> DoubleEndedIterator<&'a T> for OptVecIterator<'a, T> {
+    #[inline]
+    fn next_back(&mut self) -> Option<&'a T> {
+        match self.iter {
+            Some(ref mut x) => x.next_back(),
+            None => None
+        }
+    }
+}
+
 impl<A> FromIterator<A> for OptVec<A> {
     fn from_iterator<T: Iterator<A>>(iterator: &mut T) -> OptVec<A> {
         let mut r = Empty;
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index d79522b7103..484f8dce1f7 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -121,6 +121,17 @@ pub trait Visitor<E: Clone> {
     }
 }
 
+pub fn walk_inlined_item<E: Clone, V: Visitor<E>>(visitor: &mut V,
+                                                  item: &ast::InlinedItem,
+                                                  env: E) {
+    match *item {
+        IIItem(i) => visitor.visit_item(i, env),
+        IIForeign(i) => visitor.visit_foreign_item(i, env),
+        IIMethod(_, _, m) => walk_method_helper(visitor, m, env),
+    }
+}
+
+
 pub fn walk_crate<E: Clone, V: Visitor<E>>(visitor: &mut V, crate: &Crate, env: E) {
     visitor.visit_mod(&crate.module, crate.span, CRATE_NODE_ID, env)
 }
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 1fb05b89381..280df8cb10f 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -522,6 +522,17 @@ extern "C" char *LLVMTypeToString(LLVMTypeRef Type) {
     return strdup(os.str().data());
 }
 
+extern "C" char *LLVMValueToString(LLVMValueRef Value) {
+    std::string s;
+    llvm::raw_string_ostream os(s);
+    os << "(";
+    unwrap<llvm::Value>(Value)->getType()->print(os);
+    os << ":";
+    unwrap<llvm::Value>(Value)->print(os);
+    os << ")";
+    return strdup(os.str().data());
+}
+
 extern "C" bool
 LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) {
     Module *Dst = unwrap(dst);
diff --git a/src/test/compile-fail/borrowck-borrow-from-temporary.rs b/src/test/compile-fail/borrowck-borrow-from-temporary.rs
new file mode 100644
index 00000000000..a2f5e28de3b
--- /dev/null
+++ b/src/test/compile-fail/borrowck-borrow-from-temporary.rs
@@ -0,0 +1,22 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test lifetimes are linked properly when we take reference
+// to interior.
+
+struct Foo(int);
+
+fn foo() -> &int {
+    let &Foo(ref x) = &Foo(3); //~ ERROR borrowed value does not live long enough
+    x
+}
+
+pub fn main() {
+}
diff --git a/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs
new file mode 100644
index 00000000000..85ddfd9424c
--- /dev/null
+++ b/src/test/compile-fail/borrowck-reborrow-from-shorter-lived-andmut.rs
@@ -0,0 +1,31 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that assignments to an `&mut` pointer which is found in a
+// borrowed (but otherwise non-aliasable) location is illegal.
+
+struct S<'a> {
+    pointer: &'a mut int
+}
+
+fn copy_borrowed_ptr<'a,'b>(p: &'a mut S<'b>) -> S<'b> {
+    S { pointer: &mut *p.pointer } //~ ERROR lifetime of `p` is too short to guarantee its contents can be safely reborrowed
+}
+
+fn main() {
+    let mut x = 1;
+
+    {
+        let mut y = S { pointer: &mut x };
+        let z = copy_borrowed_ptr(&mut y);
+        *y.pointer += 1;
+        *z.pointer += 1;
+    }
+}
diff --git a/src/test/compile-fail/borrowck-rvalues-mutable-bad.rs b/src/test/compile-fail/borrowck-rvalues-mutable-bad.rs
deleted file mode 100644
index 10bef907a28..00000000000
--- a/src/test/compile-fail/borrowck-rvalues-mutable-bad.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// Tests that rvalue lifetimes is limited to the enclosing trans
-// cleanup scope. It is unclear that this is the correct lifetime for
-// rvalues, but that's what it is right now.
-
-struct Counter {
-    value: uint
-}
-
-impl Counter {
-    fn new(v: uint) -> Counter {
-        Counter {value: v}
-    }
-
-    fn inc<'a>(&'a mut self) -> &'a mut Counter {
-        self.value += 1;
-        self
-    }
-
-    fn get(&self) -> uint {
-        self.value
-    }
-}
-
-pub fn main() {
-    let v = Counter::new(22).inc().inc().get();
-    //~^ ERROR borrowed value does not live long enough
-    assert_eq!(v, 24);;
-}
diff --git a/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs b/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs
new file mode 100644
index 00000000000..47b7b51b8f7
--- /dev/null
+++ b/src/test/compile-fail/cleanup-rvalue-scopes-cf.rs
@@ -0,0 +1,45 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the borrow checker prevents pointers to temporaries
+// with statement lifetimes from escaping.
+
+#[feature(macro_rules)];
+
+use std::ops::Drop;
+
+static mut FLAGS: u64 = 0;
+
+struct Box<T> { f: T }
+struct AddFlags { bits: u64 }
+
+fn AddFlags(bits: u64) -> AddFlags {
+    AddFlags { bits: bits }
+}
+
+fn arg<'a>(x: &'a AddFlags) -> &'a AddFlags {
+    x
+}
+
+impl AddFlags {
+    fn get<'a>(&'a self) -> &'a AddFlags {
+        self
+    }
+}
+
+pub fn main() {
+    let _x = arg(&AddFlags(1)); //~ ERROR value does not live long enough
+    let _x = AddFlags(1).get(); //~ ERROR value does not live long enough
+    let _x = &*arg(&AddFlags(1)); //~ ERROR value does not live long enough
+    let ref _x = *arg(&AddFlags(1)); //~ ERROR value does not live long enough
+    let &ref _x = arg(&AddFlags(1)); //~ ERROR value does not live long enough
+    let _x = AddFlags(1).get(); //~ ERROR value does not live long enough
+    let Box { f: _x } = Box { f: AddFlags(1).get() }; //~ ERROR value does not live long enough
+}
diff --git a/src/test/debug-info/destructured-local.rs b/src/test/debug-info/destructured-local.rs
index bbe453594bc..0d415a85172 100644
--- a/src/test/debug-info/destructured-local.rs
+++ b/src/test/debug-info/destructured-local.rs
@@ -196,13 +196,13 @@ fn main() {
     let Unit(ii) = Unit(51);
 
     // univariant enum with ref      binding
-    let Unit(ref jj) = Unit(52);
+    let &Unit(ref jj) = &Unit(52);
 
     // tuple struct
-    let TupleStruct(kk, ll) = TupleStruct(53.0, 54);
+    let &TupleStruct(kk, ll) = &TupleStruct(53.0, 54);
 
     // tuple struct with ref binding
-    let TupleStruct(mm, ref nn) = TupleStruct(55.0, 56);
+    let &TupleStruct(mm, ref nn) = &TupleStruct(55.0, 56);
 
     zzz();
 }
diff --git a/src/test/run-pass/borrowck-rvalues-mutable.rs b/src/test/run-pass/borrowck-rvalues-mutable.rs
index cf5a9341c9d..d4de4ef34d3 100644
--- a/src/test/run-pass/borrowck-rvalues-mutable.rs
+++ b/src/test/run-pass/borrowck-rvalues-mutable.rs
@@ -17,6 +17,15 @@ impl Counter {
         Counter {value: v}
     }
 
+    fn inc<'a>(&'a mut self) -> &'a mut Counter {
+        self.value += 1;
+        self
+    }
+
+    fn get(&self) -> uint {
+        self.value
+    }
+
     fn get_and_inc(&mut self) -> uint {
         let v = self.value;
         self.value += 1;
@@ -27,4 +36,7 @@ impl Counter {
 pub fn main() {
     let v = Counter::new(22).get_and_inc();
     assert_eq!(v, 22);
+
+    let v = Counter::new(22).inc().inc().get();
+    assert_eq!(v, 24);;
 }
diff --git a/src/test/run-pass/cleanup-arm-conditional.rs b/src/test/run-pass/cleanup-arm-conditional.rs
new file mode 100644
index 00000000000..afe1489ce23
--- /dev/null
+++ b/src/test/run-pass/cleanup-arm-conditional.rs
@@ -0,0 +1,43 @@
+// copyright 2014 the rust project developers. see the copyright
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/copyright.
+//
+// licensed under the apache license, version 2.0 <license-apache or
+// http://www.apache.org/licenses/license-2.0> or the mit license
+// <license-mit or http://opensource.org/licenses/mit>, at your
+// option. this file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that cleanup scope for temporaries created in a match
+// arm is confined to the match arm itself.
+
+use std::{os, run};
+use std::io::process;
+
+struct Test { x: int }
+
+impl Test {
+    fn get_x(&self) -> Option<~int> {
+        Some(~self.x)
+    }
+}
+
+fn do_something(t: &Test) -> int {
+
+    // The cleanup scope for the result of `t.get_x()` should be the
+    // arm itself and not the match, otherwise we'll (potentially) get
+    // a crash trying to free an uninitialized stack slot.
+
+    match t {
+        &Test { x: 2 } if t.get_x().is_some() => {
+            t.x * 2
+        }
+        _ => { 22 }
+    }
+}
+
+pub fn main() {
+    let t = Test { x: 1 };
+    do_something(&t);
+}
+
diff --git a/src/test/run-pass/cleanup-rvalue-for-scope.rs b/src/test/run-pass/cleanup-rvalue-for-scope.rs
new file mode 100644
index 00000000000..68441a2d894
--- /dev/null
+++ b/src/test/run-pass/cleanup-rvalue-for-scope.rs
@@ -0,0 +1,70 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that the lifetime of rvalues in for loops is extended
+// to the for loop itself.
+
+#[feature(macro_rules)];
+
+use std::ops::Drop;
+
+static mut FLAGS: u64 = 0;
+
+struct Box<T> { f: T }
+struct AddFlags { bits: u64 }
+
+fn AddFlags(bits: u64) -> AddFlags {
+    AddFlags { bits: bits }
+}
+
+fn arg(exp: u64, _x: &AddFlags) {
+    check_flags(exp);
+}
+
+fn pass<T>(v: T) -> T {
+    v
+}
+
+fn check_flags(exp: u64) {
+    unsafe {
+        let x = FLAGS;
+        FLAGS = 0;
+        println!("flags {}, expected {}", x, exp);
+        assert_eq!(x, exp);
+    }
+}
+
+impl AddFlags {
+    fn check_flags<'a>(&'a self, exp: u64) -> &'a AddFlags {
+        check_flags(exp);
+        self
+    }
+
+    fn bits(&self) -> u64 {
+        self.bits
+    }
+}
+
+impl Drop for AddFlags {
+    fn drop(&mut self) {
+        unsafe {
+            FLAGS = FLAGS + self.bits;
+        }
+    }
+}
+
+pub fn main() {
+    // The array containing [AddFlags] should not be dropped until
+    // after the for loop:
+    for x in [AddFlags(1)].iter() {
+        check_flags(0);
+    }
+    check_flags(1);
+}
diff --git a/src/test/run-pass/cleanup-rvalue-scopes.rs b/src/test/run-pass/cleanup-rvalue-scopes.rs
new file mode 100644
index 00000000000..7ed59ec74b4
--- /dev/null
+++ b/src/test/run-pass/cleanup-rvalue-scopes.rs
@@ -0,0 +1,137 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that destructors for rvalue temporaries run either at end of
+// statement or end of block, as appropriate given the temporary
+// lifetime rules.
+
+#[feature(macro_rules)];
+
+use std::ops::Drop;
+
+static mut FLAGS: u64 = 0;
+
+struct Box<T> { f: T }
+struct AddFlags { bits: u64 }
+
+fn AddFlags(bits: u64) -> AddFlags {
+    AddFlags { bits: bits }
+}
+
+fn arg(exp: u64, _x: &AddFlags) {
+    check_flags(exp);
+}
+
+fn pass<T>(v: T) -> T {
+    v
+}
+
+fn check_flags(exp: u64) {
+    unsafe {
+        let x = FLAGS;
+        FLAGS = 0;
+        println!("flags {}, expected {}", x, exp);
+        assert_eq!(x, exp);
+    }
+}
+
+impl AddFlags {
+    fn check_flags<'a>(&'a self, exp: u64) -> &'a AddFlags {
+        check_flags(exp);
+        self
+    }
+
+    fn bits(&self) -> u64 {
+        self.bits
+    }
+}
+
+impl Drop for AddFlags {
+    fn drop(&mut self) {
+        unsafe {
+            FLAGS = FLAGS + self.bits;
+        }
+    }
+}
+
+macro_rules! end_of_block(
+    ($pat:pat, $expr:expr) => (
+        {
+            println!("end_of_block({})", stringify!({let $pat = $expr;}));
+
+            {
+                // Destructor here does not run until exit from the block.
+                let $pat = $expr;
+                check_flags(0);
+            }
+            check_flags(1);
+        }
+    )
+)
+
+macro_rules! end_of_stmt(
+    ($pat:pat, $expr:expr) => (
+        {
+            println!("end_of_stmt({})", stringify!($expr));
+
+            {
+                // Destructor here run after `let` statement
+                // terminates.
+                let $pat = $expr;
+                check_flags(1);
+            }
+
+            check_flags(0);
+        }
+    )
+)
+
+pub fn main() {
+
+    // In all these cases, we trip over the rules designed to cover
+    // the case where we are taking addr of rvalue and storing that
+    // addr into a stack slot, either via `let ref` or via a `&` in
+    // the initializer.
+
+    end_of_block!(_x, AddFlags(1));
+    end_of_block!(_x, &AddFlags(1));
+    end_of_block!(_x, & &AddFlags(1));
+    end_of_block!(_x, Box { f: AddFlags(1) });
+    end_of_block!(_x, Box { f: &AddFlags(1) });
+    end_of_block!(_x, Box { f: &AddFlags(1) });
+    end_of_block!(_x, pass(AddFlags(1)));
+    end_of_block!(ref _x, AddFlags(1));
+    end_of_block!(AddFlags { bits: ref _x }, AddFlags(1));
+    end_of_block!(&AddFlags { bits }, &AddFlags(1));
+    end_of_block!((_, ref _y), (AddFlags(1), 22));
+    end_of_block!(~ref _x, ~AddFlags(1));
+    end_of_block!(~_x, ~AddFlags(1));
+    end_of_block!(_, { { check_flags(0); &AddFlags(1) } });
+    end_of_block!(_, &((Box { f: AddFlags(1) }).f));
+    end_of_block!(_, &(([AddFlags(1)])[0]));
+    end_of_block!(_, &((&~[AddFlags(1)])[0]));
+
+    // LHS does not create a ref binding, so temporary lives as long
+    // as statement, and we do not move the AddFlags out:
+    end_of_stmt!(_, AddFlags(1));
+    end_of_stmt!((_, _), (AddFlags(1), 22));
+
+    // `&` operator appears inside an arg to a function,
+    // so it is not prolonged:
+    end_of_stmt!(ref _x, arg(0, &AddFlags(1)));
+
+    // autoref occurs inside receiver, so temp lifetime is not
+    // prolonged:
+    end_of_stmt!(ref _x, AddFlags(1).check_flags(0).bits());
+
+    // No reference is created on LHS, thus RHS is moved into
+    // a temporary that lives just as long as the statement.
+    end_of_stmt!(AddFlags { bits }, AddFlags(1));
+}
diff --git a/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs
new file mode 100644
index 00000000000..53a009ecc13
--- /dev/null
+++ b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs
@@ -0,0 +1,39 @@
+// Test cleanup of rvalue temporary that occurs while `~` construction
+// is in progress. This scenario revealed a rather terrible bug.  The
+// ingredients are:
+//
+// 1. Partial cleanup of `~` is in scope,
+// 2. cleanup of return value from `get_bar()` is in scope,
+// 3. do_it() fails.
+//
+// This led to a bug because `the top-most frame that was to be
+// cleaned (which happens to be the partial cleanup of `~`) required
+// multiple basic blocks, which led to us dropping part of the cleanup
+// from the top-most frame.
+//
+// It's unclear how likely such a bug is to recur, but it seems like a
+// scenario worth testing.
+
+use std::task;
+
+enum Conzabble {
+    Bickwick(Foo)
+}
+
+struct Foo { field: ~uint }
+
+fn do_it(x: &[uint]) -> Foo {
+    fail!()
+}
+
+fn get_bar(x: uint) -> ~[uint] { ~[x * 2] }
+
+pub fn fails() {
+    let x = 2;
+    let mut y = ~[];
+    y.push(~Bickwick(do_it(get_bar(x))));
+}
+
+pub fn main() {
+    task::try(fails);
+}
diff --git a/src/test/run-pass/cleanup-shortcircuit.rs b/src/test/run-pass/cleanup-shortcircuit.rs
new file mode 100644
index 00000000000..982a0d92c4c
--- /dev/null
+++ b/src/test/run-pass/cleanup-shortcircuit.rs
@@ -0,0 +1,30 @@
+// copyright 2014 the rust project developers. see the copyright
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/copyright.
+//
+// licensed under the apache license, version 2.0 <license-apache or
+// http://www.apache.org/licenses/license-2.0> or the mit license
+// <license-mit or http://opensource.org/licenses/mit>, at your
+// option. this file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that cleanups for the RHS of shorcircuiting operators work.
+
+use std::{os, run};
+use std::io::process;
+
+pub fn main() {
+    let args = os::args();
+
+    // Here, the rvalue `~"signal"` requires cleanup. Older versions
+    // of the code had a problem that the cleanup scope for this
+    // expression was the end of the `if`, and as the `~"signal"`
+    // expression was never evaluated, we wound up trying to clean
+    // uninitialized memory.
+
+    if args.len() >= 2 && args[1] == ~"signal" {
+        // Raise a segfault.
+        unsafe { *(0 as *mut int) = 0; }
+    }
+}
+
diff --git a/src/test/run-pass/intrinsic-move-val.rs b/src/test/run-pass/intrinsic-move-val.rs
index 1d2b0197f08..f42d5ff2e52 100644
--- a/src/test/run-pass/intrinsic-move-val.rs
+++ b/src/test/run-pass/intrinsic-move-val.rs
@@ -8,20 +8,22 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[feature(managed_boxes)];
+use std::cast::transmute;
 
 mod rusti {
     extern "rust-intrinsic" {
+        pub fn init<T>() -> T;
         pub fn move_val_init<T>(dst: &mut T, src: T);
-        pub fn move_val<T>(dst: &mut T, src: T);
     }
 }
 
 pub fn main() {
     unsafe {
-        let x = @1;
-        let mut y = @2;
-        rusti::move_val(&mut y, x);
+        let x = ~1;
+        let mut y = rusti::init();
+        let mut z: *uint = transmute(&x);
+        rusti::move_val_init(&mut y, x);
         assert_eq!(*y, 1);
+        assert_eq!(*z, 0); // `x` is nulled out, not directly visible
     }
 }
diff --git a/src/test/run-pass/issue-10626.rs b/src/test/run-pass/issue-10626.rs
index 020db44bb87..4d5ed9f701a 100644
--- a/src/test/run-pass/issue-10626.rs
+++ b/src/test/run-pass/issue-10626.rs
@@ -30,10 +30,10 @@ pub fn main () {
 
     let config = process::ProcessConfig {
         program : args[0].as_slice(),
-        args : [~"child"],
+        args : &[~"child"],
         env : None,
         cwd : None,
-        io : []
+        io : &[]
     };
 
     let mut p = process::Process::new(config).unwrap();
diff --git a/src/test/run-pass/issue-9382.rs b/src/test/run-pass/issue-9382.rs
index a5f87e6c536..f6bbd8ebef8 100644
--- a/src/test/run-pass/issue-9382.rs
+++ b/src/test/run-pass/issue-9382.rs
@@ -28,10 +28,10 @@ struct Thing2<'a> {
 
 pub fn main() {
     let _t1_fixed = Thing1 {
-        baz: [],
+        baz: &[],
         bar: ~32,
     };
-    let _t1_uniq = Thing1 {
+    Thing1 {
         baz: ~[],
         bar: ~32,
     };
@@ -40,10 +40,10 @@ pub fn main() {
         bar: ~32,
     };
     let _t2_fixed = Thing2 {
-        baz: [],
+        baz: &[],
         bar: 32,
     };
-    let _t2_uniq = Thing2 {
+    Thing2 {
         baz: ~[],
         bar: 32,
     };
diff --git a/src/test/run-pass/move-1.rs b/src/test/run-pass/move-1.rs
index c3335a0d455..983c701d820 100644
--- a/src/test/run-pass/move-1.rs
+++ b/src/test/run-pass/move-1.rs
@@ -15,6 +15,7 @@ struct Triple { x: int, y: int, z: int }
 fn test(x: bool, foo: @Triple) -> int {
     let bar = foo;
     let mut y: @Triple;
+    y = bar;
     if x { y = bar; } else { y = @Triple{x: 4, y: 5, z: 6}; }
     return y.y;
 }
diff --git a/src/test/run-pass/regions-dependent-let-ref.rs b/src/test/run-pass/regions-dependent-let-ref.rs
new file mode 100644
index 00000000000..980fcfb2e9e
--- /dev/null
+++ b/src/test/run-pass/regions-dependent-let-ref.rs
@@ -0,0 +1,19 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test lifetimes are linked properly when we take reference
+// to interior.
+
+struct Foo(int);
+pub fn main() {
+    // Here the lifetime of the `&` should be at least the
+    // block, since a ref binding is created to the interior.
+    let &Foo(ref _x) = &Foo(3);
+}
diff --git a/src/test/run-pass/uninit-empty-types.rs b/src/test/run-pass/uninit-empty-types.rs
new file mode 100644
index 00000000000..4a5c859d4a7
--- /dev/null
+++ b/src/test/run-pass/uninit-empty-types.rs
@@ -0,0 +1,24 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test the uninit() construct returning various empty types.
+
+use std::vec;
+use std::unstable::intrinsics;
+
+#[deriving(Clone)]
+struct Foo;
+
+pub fn main() {
+    unsafe {
+        let _x: Foo = intrinsics::uninit();
+        let _x: [Foo, ..2] = intrinsics::uninit();
+    }
+}