about summary refs log tree commit diff
diff options
context:
space:
mode:
-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);
         }
     }