about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2013-06-20 15:23:52 -0400
committerNiko Matsakis <niko@alum.mit.edu>2013-07-08 13:55:10 -0400
commit2d3262ca7b94b53178daa06fa72d5427584ae842 (patch)
treededbb99dc7506e50c842483c06420a6e9fdb0023
parent41efcdf2996f842394277298a2f8045c432ba169 (diff)
Update trans to use type to decide when to move, not the moves table (simpler
for cases where it's hard to decide what id to use for the lookup); modify
irrefutable bindings code to move or copy depending on the type, rather than
threading through a flag. Also updates how local variables and arguments are
registered. These changes were hard to isolate.
-rw-r--r--src/librustc/middle/astencode.rs11
-rw-r--r--src/librustc/middle/const_eval.rs1
-rw-r--r--src/librustc/middle/trans/_match.rs312
-rw-r--r--src/librustc/middle/trans/base.rs259
-rw-r--r--src/librustc/middle/trans/callee.rs2
-rw-r--r--src/librustc/middle/trans/controlflow.rs3
-rw-r--r--src/librustc/middle/trans/datum.rs16
-rw-r--r--src/librustc/middle/trans/expr.rs58
8 files changed, 387 insertions, 275 deletions
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 72b6f8e1c80..7412eba1156 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -53,7 +53,6 @@ pub struct Maps {
     method_map: middle::typeck::method_map,
     vtable_map: middle::typeck::vtable_map,
     write_guard_map: middle::borrowck::write_guard_map,
-    moves_map: middle::moves::MovesMap,
     capture_map: middle::moves::CaptureMap,
 }
 
@@ -952,12 +951,6 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
         }
     }
 
-    if maps.moves_map.contains(&id) {
-        do ebml_w.tag(c::tag_table_moves_map) |ebml_w| {
-            ebml_w.id(id);
-        }
-    }
-
     {
         let r = maps.capture_map.find(&id);
         for r.iter().advance |&cap_vars| {
@@ -1121,9 +1114,7 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
                 xcx.dcx.tcx.sess.bug(
                     fmt!("unknown tag found in side tables: %x", tag));
             }
-            Some(value) => if value == c::tag_table_moves_map {
-                dcx.maps.moves_map.insert(id);
-            } else {
+            Some(value) => {
                 let val_doc = entry_doc.get(c::tag_table_val as uint);
                 let mut val_dsr = reader::Decoder(val_doc);
                 let val_dsr = &mut val_dsr;
diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs
index bf91b6771dc..af39dea6d79 100644
--- a/src/librustc/middle/const_eval.rs
+++ b/src/librustc/middle/const_eval.rs
@@ -187,7 +187,6 @@ pub fn lookup_const_by_id(tcx: ty::ctxt,
             method_map: @mut HashMap::new(),
             vtable_map: @mut HashMap::new(),
             write_guard_map: @mut HashSet::new(),
-            moves_map: @mut HashSet::new(),
             capture_map: @mut HashMap::new()
         };
         match csearch::maybe_get_item_ast(tcx, def_id,
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index a58ee284046..a9a3c9317b4 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -316,7 +316,7 @@ pub fn variant_opt(bcx: block, pat_id: ast::node_id)
 }
 
 pub enum TransBindingMode {
-    TrByValue(/*ismove:*/ bool, /*llbinding:*/ ValueRef),
+    TrByValue(/*llbinding:*/ ValueRef),
     TrByRef,
 }
 
@@ -1138,18 +1138,11 @@ fn store_non_ref_bindings(bcx: block,
     let mut bcx = bcx;
     for bindings_map.each_value |&binding_info| {
         match binding_info.trmode {
-            TrByValue(is_move, lldest) => {
+            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 = {
-                    if is_move {
-                        datum.move_to(bcx, INIT, lldest)
-                    } else {
-                        datum.copy_to(bcx, INIT, lldest)
-                    }
-                };
-
+                bcx = datum.store_to(bcx, INIT, lldest);
                 do opt_temp_cleanups.mutate |temp_cleanups| {
                     add_clean_temp_mem(bcx, lldest, binding_info.ty);
                     temp_cleanups.push(lldest);
@@ -1181,7 +1174,7 @@ fn insert_lllocals(bcx: block,
         let llval = match binding_info.trmode {
             // By value bindings: use the stack slot that we
             // copied/moved the value into
-            TrByValue(_, lldest) => {
+            TrByValue(lldest) => {
                 if add_cleans {
                     add_clean(bcx, lldest, binding_info.ty);
                 }
@@ -1245,7 +1238,7 @@ pub fn compile_guard(bcx: block,
         let mut bcx = bcx;
         for data.bindings_map.each_value |&binding_info| {
             match binding_info.trmode {
-                TrByValue(_, llval) => {
+                TrByValue(llval) => {
                     bcx = glue::drop_ty(bcx, llval, binding_info.ty);
                 }
                 TrByRef => {}
@@ -1737,53 +1730,204 @@ pub enum IrrefutablePatternBindingMode {
     BindArgument
 }
 
-// Not match-related, but similar to the pattern-munging code above
-pub fn bind_irrefutable_pat(bcx: block,
-                            pat: @ast::pat,
-                            val: ValueRef,
-                            make_copy: bool,
-                            binding_mode: IrrefutablePatternBindingMode)
-                         -> block {
-    let _icx = push_ctxt("match::bind_irrefutable_pat");
-    let ccx = bcx.fcx.ccx;
+pub fn store_local(bcx: block,
+                   pat: @ast::pat,
+                   opt_init_expr: Option<@ast::expr>)
+                               -> block {
+    /*!
+     * Generates code for a local variable declaration like
+     * `let <pat>;` or `let <pat> = <opt_init_expr>`.
+     */
     let mut bcx = bcx;
 
-    // Necessary since bind_irrefutable_pat is called outside trans_match
-    match pat.node {
-        ast::pat_ident(_, _, ref inner) => {
-            if pat_is_variant_or_struct(bcx.tcx().def_map, pat) {
-                return bcx;
+    return match opt_init_expr {
+        Some(init_expr) => {
+            // Optimize the "let x = expr" case. This just writes
+            // the result of evaluating `expr` directly into the alloca
+            // for `x`. Often the general path results in similar or the
+            // same code post-optimization, but not always. In particular,
+            // in unsafe code, you can have expressions like
+            //
+            //    let x = intrinsics::uninit();
+            //
+            // In such cases, the more general path is unsafe, because
+            // it assumes it is matching against a valid value.
+            match simple_identifier(pat) {
+                Some(path) => {
+                    return mk_binding_alloca(
+                        bcx, pat.id, path, BindLocal,
+                        |bcx, _, llval| expr::trans_into(bcx, init_expr,
+                                                         expr::SaveIn(llval)));
+                }
+
+                None => {}
             }
 
-            if make_copy {
-                let binding_ty = node_id_type(bcx, pat.id);
-                let datum = Datum {val: val, ty: binding_ty,
-                                   mode: ByRef(RevokeClean)};
-                let scratch = scratch_datum(bcx, binding_ty, false);
-                datum.copy_to_datum(bcx, INIT, scratch);
-                match binding_mode {
-                    BindLocal => {
-                        bcx.fcx.lllocals.insert(pat.id, scratch.val);
-                    }
-                    BindArgument => {
-                        bcx.fcx.llargs.insert(pat.id, scratch.val);
-                    }
-                }
-                add_clean(bcx, scratch.val, binding_ty);
+            // General path.
+            let init_datum =
+                unpack_datum!(
+                    bcx,
+                    expr::trans_to_datum(bcx, init_expr));
+            if ty::type_is_bot(expr_ty(bcx, init_expr)) {
+                create_dummy_locals(bcx, pat)
             } else {
-                match binding_mode {
-                    BindLocal => {
-                        bcx.fcx.lllocals.insert(pat.id, val);
-                    }
-                    BindArgument => {
-                        bcx.fcx.llargs.insert(pat.id, val);
-                    }
+                if bcx.sess().asm_comments() {
+                    add_comment(bcx, "creating zeroable ref llval");
                 }
+                let llptr = init_datum.to_zeroable_ref_llval(bcx);
+                return bind_irrefutable_pat(bcx, pat, llptr, BindLocal);
             }
+        }
+        None => {
+            create_dummy_locals(bcx, pat)
+        }
+    };
 
-            for inner.iter().advance |inner_pat| {
-                bcx = bind_irrefutable_pat(
-                    bcx, *inner_pat, val, true, binding_mode);
+    fn create_dummy_locals(mut bcx: block, pat: @ast::pat) -> block {
+        // create dummy memory for the variables if we have no
+        // value to store into them immediately
+        let tcx = bcx.tcx();
+        do pat_bindings(tcx.def_map, pat) |_, p_id, _, path| {
+            bcx = mk_binding_alloca(
+                bcx, p_id, path, BindLocal,
+                |bcx, var_ty, llval| { zero_mem(bcx, llval, var_ty); bcx });
+        }
+        bcx
+    }
+}
+
+pub fn store_arg(mut bcx: block,
+                 pat: @ast::pat,
+                 llval: ValueRef)
+                 -> block {
+    /*!
+     * Generates code for argument patterns like `fn foo(<pat>: T)`.
+     * Creates entries in the `llargs` map for each of the bindings
+     * in `pat`.
+     *
+     * # Arguments
+     *
+     * - `pat` is the argument pattern
+     * - `llval` is a pointer to the argument value (in other words,
+     *   if the argument type is `T`, then `llval` is a `T*`). In some
+     *   cases, this code may zero out the memory `llval` points at.
+     */
+
+    // 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`)
+    let arg_ty = node_id_type(bcx, pat.id);
+    add_clean(bcx, llval, arg_ty);
+
+    match simple_identifier(pat) {
+        Some(_) => {
+            // 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`.
+            bcx.fcx.llargs.insert(pat.id, llval);
+        }
+
+        None => {
+            // General path. Copy out the values that are used in the
+            // pattern.
+            bcx = bind_irrefutable_pat(bcx, pat, llval, BindArgument);
+        }
+    }
+
+    return bcx;
+}
+
+fn mk_binding_alloca(mut bcx: block,
+                     p_id: ast::node_id,
+                     path: @ast::Path,
+                     binding_mode: IrrefutablePatternBindingMode,
+                     populate: &fn(block, ty::t, ValueRef) -> block) -> block {
+    let var_ty = node_id_type(bcx, p_id);
+    let ident = ast_util::path_to_ident(path);
+    let llval = alloc_ty(bcx, var_ty, bcx.ident(ident));
+    bcx = populate(bcx, var_ty, llval);
+    let llmap = match binding_mode {
+        BindLocal => bcx.fcx.lllocals,
+        BindArgument => bcx.fcx.llargs
+    };
+    llmap.insert(p_id, llval);
+    add_clean(bcx, llval, var_ty);
+    return bcx;
+}
+
+fn bind_irrefutable_pat(bcx: block,
+                        pat: @ast::pat,
+                        val: ValueRef,
+                        binding_mode: IrrefutablePatternBindingMode)
+                        -> block {
+    /*!
+     * A simple version of the pattern matching code that only handles
+     * irrefutable patterns. This is used in let/argument patterns,
+     * not in match statements. Unifying this code with the code above
+     * sounds nice, but in practice it produces very inefficient code,
+     * since the match code is so much more general. In most cases,
+     * LLVM is able to optimize the code, but it causes longer compile
+     * times and makes the generated code nigh impossible to read.
+     *
+     * # 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.
+     * - binding_mode: is this for an argument or a local variable?
+     */
+
+    debug!("bind_irrefutable_pat(bcx=%s, pat=%s, val=%s, binding_mode=%?)",
+           bcx.to_str(),
+           pat_to_str(pat, bcx.sess().intr()),
+           val_str(bcx.ccx().tn, val),
+           binding_mode);
+
+    if bcx.sess().asm_comments() {
+        add_comment(bcx, fmt!("bind_irrefutable_pat(pat=%s)",
+                              pat_to_str(pat, bcx.sess().intr())));
+    }
+
+    let _indenter = indenter();
+
+    let _icx = bcx.insn_ctxt("alt::bind_irrefutable_pat");
+    let mut bcx = bcx;
+    let tcx = bcx.tcx();
+    let ccx = bcx.ccx();
+    match pat.node {
+        ast::pat_ident(pat_binding_mode, path, inner) => {
+            if pat_is_binding(tcx.def_map, pat) {
+                // Allocate the stack slot where the value of this
+                // binding will live and place it into the appropriate
+                // map.
+                bcx = mk_binding_alloca(
+                    bcx, pat.id, path, binding_mode,
+                    |bcx, variable_ty, llvariable_val| {
+                        match pat_binding_mode {
+                            ast::bind_infer => {
+                                // By value binding: move the value that `val`
+                                // points at into the binding's stack slot.
+                                let datum = Datum {val: val,
+                                                   ty: variable_ty,
+                                                   mode: ByRef(ZeroMem)};
+                                datum.store_to(bcx, INIT, llvariable_val)
+                            }
+
+                            ast::bind_by_ref(_) => {
+                                // By ref binding: the value of the variable
+                                // is the pointer `val` itself.
+                                Store(bcx, val, llvariable_val);
+                                bcx
+                            }
+                        }
+                    });
+            }
+
+            for inner.iter().advance |&inner_pat| {
+                bcx = bind_irrefutable_pat(bcx, inner_pat, val, binding_mode);
             }
         }
         ast::pat_enum(_, ref sub_pats) => {
@@ -1799,11 +1943,8 @@ pub fn bind_irrefutable_pat(bcx: block,
                                                     val);
                     for sub_pats.iter().advance |sub_pat| {
                         for args.vals.iter().enumerate().advance |(i, argval)| {
-                            bcx = bind_irrefutable_pat(bcx,
-                                                       sub_pat[i],
-                                                       *argval,
-                                                       make_copy,
-                                                       binding_mode);
+                            bcx = bind_irrefutable_pat(bcx, sub_pat[i],
+                                                       *argval, binding_mode);
                         }
                     }
                 }
@@ -1818,19 +1959,14 @@ pub fn bind_irrefutable_pat(bcx: block,
                             let repr = adt::represent_node(bcx, pat.id);
                             for elems.iter().enumerate().advance |(i, elem)| {
                                 let fldptr = adt::trans_field_ptr(bcx, repr,
-                                                            val, 0, i);
-                                bcx = bind_irrefutable_pat(bcx,
-                                                           *elem,
-                                                           fldptr,
-                                                           make_copy,
-                                                           binding_mode);
+                                                                  val, 0, i);
+                                bcx = bind_irrefutable_pat(bcx, *elem,
+                                                           fldptr, binding_mode);
                             }
                         }
                     }
                 }
                 Some(&ast::def_static(_, false)) => {
-                    bcx = bind_irrefutable_pat(bcx, pat, val, make_copy,
-                                               binding_mode);
                 }
                 _ => {
                     // Nothing to do here.
@@ -1845,12 +1981,8 @@ pub fn bind_irrefutable_pat(bcx: block,
                 for fields.iter().advance |f| {
                     let ix = ty::field_idx_strict(tcx, f.ident, field_tys);
                     let fldptr = adt::trans_field_ptr(bcx, pat_repr, val,
-                                                discr, ix);
-                    bcx = bind_irrefutable_pat(bcx,
-                                               f.pat,
-                                               fldptr,
-                                               make_copy,
-                                               binding_mode);
+                                                      discr, ix);
+                    bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, binding_mode);
                 }
             }
         }
@@ -1858,11 +1990,7 @@ pub fn bind_irrefutable_pat(bcx: block,
             let repr = adt::represent_node(bcx, pat.id);
             for elems.iter().enumerate().advance |(i, elem)| {
                 let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i);
-                bcx = bind_irrefutable_pat(bcx,
-                                           *elem,
-                                           fldptr,
-                                           make_copy,
-                                           binding_mode);
+                bcx = bind_irrefutable_pat(bcx, *elem, fldptr, binding_mode);
             }
         }
         ast::pat_box(inner) | ast::pat_uniq(inner) => {
@@ -1872,22 +2000,30 @@ pub fn bind_irrefutable_pat(bcx: block,
                 ty::ty_uniq(*) if !ty::type_contents(bcx.tcx(), pat_ty).contains_managed() => llbox,
                     _ => GEPi(bcx, llbox, [0u, abi::box_field_body])
             };
-            bcx = bind_irrefutable_pat(bcx,
-                                       inner,
-                                       unboxed,
-                                       true,
-                                       binding_mode);
+            bcx = bind_irrefutable_pat(bcx, inner, unboxed, binding_mode);
         }
         ast::pat_region(inner) => {
             let loaded_val = Load(bcx, val);
-            bcx = bind_irrefutable_pat(bcx,
-                                       inner,
-                                       loaded_val,
-                                       true,
-                                       binding_mode);
-        }
-        ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
-        ast::pat_vec(*) => ()
+            bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode);
+        }
+        ast::pat_vec(*) => {
+            bcx.tcx().sess.span_bug(
+                pat.span,
+                fmt!("vector patterns are never irrefutable!"));
+        }
+        ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => ()
     }
     return bcx;
 }
+
+fn simple_identifier(pat: @ast::pat) -> Option<@ast::Path> {
+    match pat.node {
+        ast::pat_ident(ast::bind_infer, path, None) => {
+            Some(path)
+        }
+        _ => {
+            None
+        }
+    }
+}
+
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 8da7c1351c7..75d9f89a8d7 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -59,6 +59,7 @@ use middle::trans::type_of::*;
 use middle::ty;
 use util::common::indenter;
 use util::ppaux::{Repr, ty_to_str};
+use middle::pat_util;
 
 use middle::trans::type_::Type;
 
@@ -75,7 +76,7 @@ use extra::time;
 use extra::sort;
 use syntax::ast::ident;
 use syntax::ast_map::{path, path_elt_to_str, path_name};
-use syntax::ast_util::{local_def, path_to_ident};
+use syntax::ast_util::{local_def};
 use syntax::attr;
 use syntax::codemap::span;
 use syntax::parse::token;
@@ -1121,9 +1122,6 @@ pub fn init_local(bcx: block, local: &ast::local) -> block {
     let _indenter = indenter();
 
     let _icx = push_ctxt("init_local");
-    let ty = node_id_type(bcx, local.node.id);
-
-    debug!("ty=%s", bcx.ty_to_str(ty));
 
     if ignore_lhs(bcx, local) {
         // Handle let _ = e; just like e;
@@ -1135,36 +1133,7 @@ pub fn init_local(bcx: block, local: &ast::local) -> block {
         }
     }
 
-    let llptr = match bcx.fcx.lllocals.find_copy(&local.node.id) {
-        Some(v) => v,
-        _ => {
-            bcx.tcx().sess.span_bug(local.span,
-                                    "init_local: Someone forgot to document why it's\
-                                     safe to assume local.node.init must be local_mem!");
-        }
-    };
-
-    let mut bcx = bcx;
-    match local.node.init {
-        Some(init) => {
-            bcx = expr::trans_into(bcx, init, expr::SaveIn(llptr));
-        }
-        _ => {
-            zero_mem(bcx, llptr, ty);
-        }
-    }
-
-    // Make a note to drop this slot on the way out.
-    debug!("adding clean for %?/%s to bcx=%s",
-           local.node.id, bcx.ty_to_str(ty),
-           bcx.to_str());
-    add_clean(bcx, llptr, ty);
-
-    return _match::bind_irrefutable_pat(bcx,
-                                       local.node.pat,
-                                       llptr,
-                                       false,
-                                       _match::BindLocal);
+    _match::store_local(bcx, local.node.pat, local.node.init)
 }
 
 pub fn trans_stmt(cx: block, s: &ast::stmt) -> block {
@@ -1469,6 +1438,7 @@ pub fn block_locals(b: &ast::blk, it: &fn(@ast::local)) {
     }
 }
 
+<<<<<<< variant A
 pub fn alloc_local(cx: block, local: &ast::local) -> block {
     let _icx = push_ctxt("alloc_local");
     let t = node_id_type(cx, local.node.id);
@@ -1491,6 +1461,31 @@ pub fn alloc_local(cx: block, local: &ast::local) -> block {
 }
 
 
+>>>>>>> variant B
+####### Ancestor
+pub fn alloc_local(cx: block, local: @ast::local) -> block {
+    let _icx = push_ctxt("alloc_local");
+    let t = node_id_type(cx, local.node.id);
+    let simple_name = match local.node.pat.node {
+      ast::pat_ident(_, pth, None) => Some(path_to_ident(pth)),
+      _ => None
+    };
+    let val = alloc_ty(cx, t);
+    if cx.sess().opts.debuginfo {
+        for simple_name.iter().advance |name| {
+            str::as_c_str(cx.ccx().sess.str_of(*name), |buf| {
+                unsafe {
+                    llvm::LLVMSetValueName(val, buf)
+                }
+            });
+        }
+    }
+    cx.fcx.lllocals.insert(local.node.id, val);
+    cx
+}
+
+
+======= end
 pub fn with_cond(bcx: block, val: ValueRef, f: &fn(block) -> block) -> block {
     let _icx = push_ctxt("with_cond");
     let next_cx = base::sub_block(bcx, "next");
@@ -1739,6 +1734,7 @@ pub fn create_llargs_for_fn_args(cx: fn_ctxt,
             let arg = &args[i];
             let llarg = llvm::LLVMGetParam(cx.llfn, arg_n as c_uint);
 
+            // FIXME #7260: aliasing should be determined by monomorphized ty::t
             match arg.ty.node {
                 // `~` pointers never alias other parameters, because ownership was transferred
                 ast::ty_uniq(_) => {
@@ -1783,7 +1779,6 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
     for uint::range(0, arg_tys.len()) |arg_n| {
         let arg_ty = arg_tys[arg_n];
         let raw_llarg = raw_llargs[arg_n];
-        let arg_id = args[arg_n].id;
 
         // For certain mode/type combinations, the raw llarg values are passed
         // by value.  However, within the fn body itself, we want to always
@@ -1794,22 +1789,13 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
         // the event it's not truly needed.
         // only by value if immediate:
         let llarg = if datum::appropriate_mode(bcx.tcx(), arg_ty).is_by_value() {
-            let alloc = alloc_ty(bcx, arg_ty);
+            let alloc = alloc_ty(bcx, arg_ty, "__arg");
             Store(bcx, raw_llarg, alloc);
             alloc
         } else {
             raw_llarg
         };
-
-        add_clean(bcx, llarg, arg_ty);
-
-        bcx = _match::bind_irrefutable_pat(bcx,
-                                          args[arg_n].pat,
-                                          llarg,
-                                          false,
-                                          _match::BindArgument);
-
-        fcx.llargs.insert(arg_id, llarg);
+        bcx = _match::store_arg(bcx, args[arg_n].pat, llarg);
 
         if fcx.ccx.sess.opts.extra_debuginfo && fcx_has_nonzero_span(fcx) {
             debuginfo::create_arg(bcx, &args[arg_n], args[arg_n].ty.span);
@@ -1968,81 +1954,51 @@ pub fn trans_fn(ccx: @mut CrateContext,
                   |_bcx| { });
 }
 
+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 uint::range(0, fn_args.len()) |i| {
+        debug!("setting type of argument %u (pat node %d) to %s",
+               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];
+        tcx.node_types.insert(pat_id as uint, arg_ty);
+    }
+}
+
 pub fn trans_enum_variant(ccx: @mut CrateContext,
-                          enum_id: ast::node_id,
+                          _enum_id: ast::node_id,
                           variant: &ast::variant,
                           args: &[ast::variant_arg],
                           disr: int,
                           param_substs: Option<@param_substs>,
                           llfndecl: ValueRef) {
     let _icx = push_ctxt("trans_enum_variant");
-    // Translate variant arguments to function arguments.
-    let fn_args = do args.map |varg| {
-        ast::arg {
-            is_mutbl: false,
-            ty: copy varg.ty,
-            pat: ast_util::ident_to_pat(
-                ccx.tcx.sess.next_node_id(),
-                codemap::dummy_sp(),
-                special_idents::arg),
-            id: varg.id,
-        }
-    };
 
-    let ty_param_substs = match param_substs {
-        Some(ref substs) => { copy substs.tys }
-        None => ~[]
-    };
-    let enum_ty = ty::subst_tps(ccx.tcx,
-                                ty_param_substs,
-                                None,
-                                ty::node_id_to_type(ccx.tcx, enum_id));
-    let fcx = new_fn_ctxt_w_id(ccx,
-                               ~[],
-                               llfndecl,
-                               variant.node.id,
-                               enum_ty,
-                               param_substs,
-                               None);
-
-    let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args);
-    let bcx = top_scope_block(fcx, None);
-    let lltop = bcx.llbb;
-    let arg_tys = ty::ty_fn_args(node_id_type(bcx, variant.node.id));
-    let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
-
-    // XXX is there a better way to reconstruct the ty::t?
-    let repr = adt::represent_type(ccx, enum_ty);
-
-    debug!("trans_enum_variant: name=%s tps=%s repr=%? enum_ty=%s",
-           unsafe { str::raw::from_c_str(llvm::LLVMGetValueName(llfndecl)) },
-           ~"[" + ty_param_substs.map(|&t| ty_to_str(ccx.tcx, t)).connect(", ") + "]",
-           repr, ty_to_str(ccx.tcx, enum_ty));
-
-    adt::trans_start_init(bcx, repr, fcx.llretptr.get(), disr);
-    for args.iter().enumerate().advance |(i, va)| {
-        let lldestptr = adt::trans_field_ptr(bcx,
-                                             repr,
-                                             fcx.llretptr.get(),
-                                             disr,
-                                             i);
-
-        // If this argument to this function is a enum, it'll have come in to
-        // this function as an opaque blob due to the way that type_of()
-        // works. So we have to cast to the destination's view of the type.
-        let llarg = match fcx.llargs.find(&va.id) {
-            Some(&x) => x,
-            _ => fail!("trans_enum_variant: how do we know this works?"),
-        };
-        let arg_ty = arg_tys[i];
-        memcpy_ty(bcx, lldestptr, llarg, arg_ty);
-    }
-    build_return(bcx);
-    finish_fn(fcx, lltop);
+    trans_enum_variant_or_tuple_like_struct(
+        ccx,
+        variant.node.id,
+        args,
+        disr,
+        param_substs,
+        llfndecl);
 }
 
-// NB: In theory this should be merged with the function above. But the AST
-// structures are completely different, so very little code would be shared.
 pub fn trans_tuple_struct(ccx: @mut CrateContext,
                           fields: &[@ast::struct_field],
                           ctor_id: ast::node_id,
@@ -2050,37 +2006,72 @@ pub fn trans_tuple_struct(ccx: @mut CrateContext,
                           llfndecl: ValueRef) {
     let _icx = push_ctxt("trans_tuple_struct");
 
-    // Translate struct fields to function arguments.
-    let fn_args = do fields.map |field| {
+    trans_enum_variant_or_tuple_like_struct(
+        ccx,
+        ctor_id,
+        fields,
+        0,
+        param_substs,
+        llfndecl);
+}
+
+trait IdAndTy {
+    fn id(&self) -> ast::node_id;
+    fn ty(&self) -> @ast::Ty;
+}
+
+impl IdAndTy for ast::variant_arg {
+    fn id(&self) -> ast::node_id { self.id }
+    fn ty(&self) -> @ast::Ty { self.ty }
+}
+
+impl IdAndTy for @ast::struct_field {
+    fn id(&self) -> ast::node_id { self.node.id }
+    fn ty(&self) -> @ast::Ty { self.node.ty }
+}
+
+pub fn trans_enum_variant_or_tuple_like_struct<A:IdAndTy>(
+    ccx: @mut CrateContext,
+    ctor_id: ast::node_id,
+    args: &[A],
+    disr: int,
+    param_substs: Option<@param_substs>,
+    llfndecl: ValueRef)
+{
+    // Translate variant arguments to function arguments.
+    let fn_args = do args.map |varg| {
         ast::arg {
             is_mutbl: false,
-            ty: copy field.node.ty,
-            pat: ast_util::ident_to_pat(ccx.tcx.sess.next_node_id(),
-                                        codemap::dummy_sp(),
-                                        special_idents::arg),
-            id: field.node.id
+            ty: varg.ty(),
+            pat: ast_util::ident_to_pat(
+                ccx.tcx.sess.next_node_id(),
+                codemap::dummy_sp(),
+                special_idents::arg),
+            id: varg.id(),
         }
     };
 
-    // XXX is there a better way to reconstruct the ty::t?
     let ty_param_substs = match param_substs {
         Some(ref substs) => { copy substs.tys }
         None => ~[]
     };
+
     let ctor_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
                                 ty::node_id_to_type(ccx.tcx, ctor_id));
-    let tup_ty = match ty::get(ctor_ty).sty {
+
+    let result_ty = match ty::get(ctor_ty).sty {
         ty::ty_bare_fn(ref bft) => bft.sig.output,
-        _ => ccx.sess.bug(fmt!("trans_tuple_struct: unexpected ctor \
-                                return type %s",
-                               ty_to_str(ccx.tcx, ctor_ty)))
+        _ => ccx.sess.bug(
+            fmt!("trans_enum_variant_or_tuple_like_struct: \
+                  unexpected ctor return type %s",
+                 ty_to_str(ccx.tcx, ctor_ty)))
     };
 
     let fcx = new_fn_ctxt_w_id(ccx,
                                ~[],
                                llfndecl,
                                ctor_id,
-                               tup_ty,
+                               result_ty,
                                param_substs,
                                None);
 
@@ -2088,23 +2079,23 @@ pub fn trans_tuple_struct(ccx: @mut CrateContext,
 
     let bcx = top_scope_block(fcx, None);
     let lltop = bcx.llbb;
-    let arg_tys = ty::ty_fn_args(node_id_type(bcx, ctor_id));
-    let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
+    let arg_tys = ty::ty_fn_args(ctor_ty);
 
-    let repr = adt::represent_type(ccx, tup_ty);
-    adt::trans_start_init(bcx, repr, fcx.llretptr.get(), 0);
+    insert_synthetic_type_entries(bcx, fn_args, arg_tys);
+    let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
 
-    for fields.iter().enumerate().advance |(i, field)| {
+    let repr = adt::represent_type(ccx, result_ty);
+    adt::trans_start_init(bcx, repr, fcx.llretptr.get(), disr);
+    for fn_args.iter().enumerate().advance |(i, fn_arg)| {
         let lldestptr = adt::trans_field_ptr(bcx,
                                              repr,
                                              fcx.llretptr.get(),
-                                             0,
+                                             disr,
                                              i);
-        let llarg = fcx.llargs.get_copy(&field.node.id);
+        let llarg = fcx.llargs.get_copy(&fn_arg.pat.id);
         let arg_ty = arg_tys[i];
         memcpy_ty(bcx, lldestptr, llarg, arg_ty);
     }
-
     build_return(bcx);
     finish_fn(fcx, lltop);
 }
@@ -3039,8 +3030,8 @@ pub fn trans_crate(sess: session::Session,
         }
     }
     if ccx.sess.count_llvm_insns() {
-        for ccx.stats.llvm_insns.iter().advance |(&k, &v)| {
-            io::println(fmt!("%-7u %s", v, k));
+        for ccx.stats.llvm_insns.each |k, v| {
+            io::println(fmt!("%-7u %s", *v, *k));
         }
     }
 
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index cf81def3abf..216338e1117 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -860,8 +860,6 @@ pub fn trans_arg_expr(bcx: block,
         // FIXME(#3548) use the adjustments table
         match autoref_arg {
             DoAutorefArg => {
-                assert!(!
-                    bcx.ccx().maps.moves_map.contains(&arg_expr.id));
                 val = arg_datum.to_ref_llval(bcx);
             }
             DontAutorefArg => {
diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs
index 8ca4253ead8..904e6e14e28 100644
--- a/src/librustc/middle/trans/controlflow.rs
+++ b/src/librustc/middle/trans/controlflow.rs
@@ -35,9 +35,6 @@ use syntax::codemap::span;
 pub fn trans_block(bcx: block, b: &ast::blk, dest: expr::Dest) -> block {
     let _icx = push_ctxt("trans_block");
     let mut bcx = bcx;
-    do block_locals(b) |local| {
-        bcx = alloc_local(bcx, local);
-    };
     for b.node.stmts.iter().advance |s| {
         debuginfo::update_source_pos(bcx, b.span);
         bcx = trans_stmt(bcx, *s);
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index ed0adbcff87..1de619433af 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -70,8 +70,8 @@
  *   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 by consulting the
- *   moves_map computed by `middle::moves`.
+ * - `store_to()` either performs a copy or a move depending on the
+ *   Rust type of the datum.
  *
  * # Scratch datum
  *
@@ -208,7 +208,6 @@ pub fn appropriate_mode(tcx: ty::ctxt, ty: ty::t) -> DatumMode {
 impl Datum {
     pub fn store_to(&self,
                     bcx: block,
-                    id: ast::node_id,
                     action: CopyAction,
                     dst: ValueRef)
                     -> block {
@@ -218,7 +217,7 @@ impl Datum {
          * `id` is located in the move table, but copies otherwise.
          */
 
-        if bcx.ccx().maps.moves_map.contains(&id) {
+        if ty::type_moves_by_default(bcx.tcx(), self.ty) {
             self.move_to(bcx, action, dst)
         } else {
             self.copy_to(bcx, action, dst)
@@ -227,7 +226,6 @@ impl Datum {
 
     pub fn store_to_dest(&self,
                          bcx: block,
-                         id: ast::node_id,
                          dest: expr::Dest)
                          -> block {
         match dest {
@@ -235,21 +233,20 @@ impl Datum {
                 return bcx;
             }
             expr::SaveIn(addr) => {
-                return self.store_to(bcx, id, INIT, addr);
+                return self.store_to(bcx, INIT, addr);
             }
         }
     }
 
     pub fn store_to_datum(&self,
                           bcx: block,
-                          id: ast::node_id,
                           action: CopyAction,
                           datum: Datum)
                           -> block {
         debug!("store_to_datum(self=%s, action=%?, datum=%s)",
                self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
         assert!(datum.mode.is_by_ref());
-        self.store_to(bcx, id, action, datum.val)
+        self.store_to(bcx, action, datum.val)
     }
 
     pub fn move_to_datum(&self, bcx: block, action: CopyAction, datum: Datum)
@@ -828,11 +825,10 @@ impl DatumBlock {
     }
 
     pub fn store_to(&self,
-                    id: ast::node_id,
                     action: CopyAction,
                     dst: ValueRef)
                     -> block {
-        self.datum.store_to(self.bcx, id, action, dst)
+        self.datum.store_to(self.bcx, action, dst)
     }
 
     pub fn copy_to(&self, action: CopyAction, dst: ValueRef) -> block {
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 2d5ac23b3b3..19a0f7262ff 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -23,7 +23,8 @@ 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 and moves for you.
+handle all automatic adjustments for you. The value will be moved if
+its type is linear and copied otherwise.
 
 ## Translation to a datum
 
@@ -42,18 +43,18 @@ This function generates code to evaluate the expression and return a
 tries to return its result in the most efficient way possible, without
 introducing extra copies or sacrificing information.  Therefore, for
 lvalue expressions, you always get a by-ref `Datum` in return that
-points at the memory for this lvalue (almost, see [1]).  For rvalue
-expressions, we will return a by-value `Datum` whenever possible, but
-it is often necessary to allocate a stack slot, store the result of
-the rvalue in there, and then return a pointer to the slot (see the
-discussion later on about the different kinds of rvalues).
+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
-any moves that may be relevant.  If you are transing an expression
-whose result should be moved, you should either use the Datum methods
-`move_to()` (for unconditional moves) or `store_to()` (for moves
-conditioned on the type of the expression) at some point.
+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
 
@@ -110,13 +111,6 @@ generate phi nodes).
 Finally, statement rvalues are rvalues that always produce a nil
 return type, such as `while` loops or assignments (`a = b`).
 
-## Caveats
-
-[1] Actually, some lvalues are only stored by value and not by
-reference.  An example (as of this writing) would be immutable
-arguments or pattern bindings of immediate type.  However, mutable
-lvalues are *never* stored by value.
-
 */
 
 
@@ -315,7 +309,7 @@ pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
         let datumblock = trans_to_datum(bcx, expr);
         return match dest {
             Ignore => datumblock.bcx,
-            SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
+            SaveIn(lldest) => datumblock.store_to(INIT, lldest)
         };
     }
 
@@ -343,7 +337,7 @@ pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
             let datumblock = trans_lvalue_unadjusted(bcx, expr);
             match dest {
                 Ignore => datumblock.bcx,
-                SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
+                SaveIn(lldest) => datumblock.store_to(INIT, lldest)
             }
         }
         ty::RvalueDatumExpr => {
@@ -351,8 +345,9 @@ pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
             match dest {
                 Ignore => datumblock.drop_val(),
 
-                // NB: We always do `move_to()` regardless of the
-                // moves_map because we're processing an rvalue
+                // 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)
             }
         }
@@ -386,11 +381,11 @@ fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
 
 fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
     /*!
-     *
      * Translates an expression into a datum.  If this expression
      * is an rvalue, this will result in a temporary value being
-     * created.  If you already know where the result should be stored,
-     * you should use `trans_into()` instead. */
+     * created.  If you plan to store the value somewhere else,
+     * you should prefer `trans_into()` instead.
+     */
 
     let mut bcx = bcx;
 
@@ -535,7 +530,7 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
             let dst_datum = unpack_datum!(
                 bcx, trans_lvalue(bcx, dst));
             return src_datum.store_to_datum(
-                bcx, src.id, DROP_EXISTING, dst_datum);
+                bcx, DROP_EXISTING, dst_datum);
         }
         ast::expr_assign_op(callee_id, op, dst, src) => {
             return trans_assign_op(bcx, expr, callee_id, op, dst, src);
@@ -638,7 +633,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
             return trans_into(bcx, blk, dest);
         }
         ast::expr_copy(a) => {
-            return trans_into(bcx, a, dest);
+            // If we just called `trans_into(bcx, a, dest)`, then this
+            // might *move* the value into `dest` if the value is
+            // non-copyable. So first get a datum and then do an
+            // explicit copy.
+            let datumblk = trans_to_datum(bcx, a);
+            return match dest {
+                Ignore => datumblk.bcx,
+                SaveIn(llval) => datumblk.copy_to(INIT, llval)
+            };
         }
         ast::expr_call(f, ref args, _) => {
             return callee::trans_call(
@@ -1221,6 +1224,7 @@ fn trans_adt(bcx: block, repr: &adt::Repr, discr: int,
                 bcx = trans_into(bcx, e, Ignore);
             }
             for optbase.iter().advance |sbi| {
+                // FIXME #7261: this moves entire base, not just certain fields
                 bcx = trans_into(bcx, sbi.expr, Ignore);
             }
             return bcx;
@@ -1245,7 +1249,7 @@ fn trans_adt(bcx: block, repr: &adt::Repr, discr: int,
                 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, base.expr.id, INIT, dest);
+            bcx = datum.store_to(bcx, INIT, dest);
         }
     }