about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_trans/trans/_match.rs72
-rw-r--r--src/librustc_trans/trans/adt.rs14
-rw-r--r--src/librustc_trans/trans/base.rs53
-rw-r--r--src/librustc_trans/trans/cleanup.rs48
-rw-r--r--src/librustc_trans/trans/closure.rs4
-rw-r--r--src/librustc_trans/trans/common.rs6
-rw-r--r--src/librustc_trans/trans/datum.rs70
-rw-r--r--src/librustc_trans/trans/expr.rs29
-rw-r--r--src/librustc_trans/trans/glue.rs29
-rw-r--r--src/librustc_trans/trans/tvec.rs4
10 files changed, 259 insertions, 70 deletions
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index 08be1fd2e4c..7a491a628e5 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -205,7 +205,7 @@ use trans::build::{AddCase, And, Br, CondBr, GEPi, InBoundsGEP, Load, PointerCas
 use trans::build::{Not, Store, Sub, add_comment};
 use trans::build;
 use trans::callee;
-use trans::cleanup::{self, CleanupMethods};
+use trans::cleanup::{self, CleanupMethods, DropHintMethods};
 use trans::common::*;
 use trans::consts;
 use trans::datum::*;
@@ -947,14 +947,14 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             TrByCopy(llbinding) |
             TrByMoveIntoCopy(llbinding) => {
                 let llval = Load(bcx, binding_info.llmatch);
-                let lval = match binding_info.trmode {
+                let lvalue = match binding_info.trmode {
                     TrByCopy(..) =>
                         Lvalue::new("_match::insert_lllocals"),
                     TrByMoveIntoCopy(..) =>
                         Lvalue::match_input("_match::insert_lllocals", bcx, binding_info.id),
                     _ => unreachable!(),
                 };
-                let datum = Datum::new(llval, binding_info.ty, lval);
+                let datum = Datum::new(llval, binding_info.ty, lvalue);
                 call_lifetime_start(bcx, llbinding);
                 bcx = datum.store_to(bcx, llbinding);
                 if let Some(cs) = cs {
@@ -971,14 +971,15 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             TrByRef => (binding_info.llmatch, true),
         };
 
-        let lval = Lvalue::local("_match::insert_lllocals",
-                                 bcx,
-                                 binding_info.id,
-                                 aliases_other_state);
-        let datum = Datum::new(llval, binding_info.ty, lval);
+        let lvalue = Lvalue::local("_match::insert_lllocals",
+                                   bcx,
+                                   binding_info.id,
+                                   aliases_other_state);
+        let datum = Datum::new(llval, binding_info.ty, lvalue);
         if let Some(cs) = cs {
+            let opt_datum = lvalue.dropflag_hint(bcx);
             bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch);
-            bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty);
+            bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty, opt_datum);
         }
 
         debug!("binding {} to {}", binding_info.id, bcx.val_to_string(llval));
@@ -1505,13 +1506,13 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &ast::Pat,
                 // but during matching we need to store a *T as explained
                 // above
                 llmatch = alloca_no_lifetime(bcx,
-                                 llvariable_ty.ptr_to(),
-                                 &bcx.name(name));
+                                             llvariable_ty.ptr_to(),
+                                             &bcx.name(name));
                 trmode = TrByMoveRef;
             }
             ast::BindByRef(_) => {
                 llmatch = alloca_no_lifetime(bcx,
-                                             llvariable_ty,
+                                 llvariable_ty,
                                  &bcx.name(name));
                 trmode = TrByRef;
             }
@@ -1631,7 +1632,25 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             bcx = mk_binding_alloca(
                 bcx, p_id, path1.node.name, scope, (),
                 "_match::store_local::create_dummy_locals",
-                |(), bcx, llval, ty| { drop_done_fill_mem(bcx, llval, ty); bcx });
+                |(), bcx, Datum { val: llval, ty, kind }| {
+                    // Dummy-locals start out uninitialized, so set their
+                    // drop-flag hints (if any) to "moved."
+                    if let Some(hint) = kind.dropflag_hint(bcx) {
+                        let moved_hint = adt::DTOR_MOVED_HINT as usize;
+                        debug!("store moved_hint={} for hint={:?}, uninitialized dummy",
+                               moved_hint, hint);
+                        Store(bcx, C_u8(bcx.fcx.ccx, moved_hint), hint.to_value().value());
+                    }
+
+                    if kind.drop_flag_info.must_zero() {
+                        // if no drop-flag hint, or the hint requires
+                        // we maintain the embedded drop-flag, then
+                        // mark embedded drop-flag(s) as moved
+                        // (i.e. "already dropped").
+                        drop_done_fill_mem(bcx, llval, ty);
+                    }
+                    bcx
+                });
         });
         bcx
     }
@@ -1654,8 +1673,8 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                     return mk_binding_alloca(
                         bcx, pat.id, ident.name, var_scope, (),
                         "_match::store_local",
-                        |(), bcx, v, _| expr::trans_into(bcx, &**init_expr,
-                                                         expr::SaveIn(v)));
+                        |(), bcx, Datum { val: v, .. }| expr::trans_into(bcx, &**init_expr,
+                                                                         expr::SaveIn(v)));
                 }
 
                 None => {}
@@ -1684,23 +1703,23 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
                                        caller_name: &'static str,
                                        populate: F)
                                        -> Block<'blk, 'tcx> where
-    F: FnOnce(A, Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>,
+    F: FnOnce(A, Block<'blk, 'tcx>, Datum<'tcx, Lvalue>) -> Block<'blk, 'tcx>,
 {
     let var_ty = node_id_type(bcx, p_id);
 
     // Allocate memory on stack for the binding.
     let llval = alloc_ty(bcx, var_ty, &bcx.name(name));
+    let lvalue = Lvalue::binding(caller_name, bcx, p_id, name);
+    let datum = Datum::new(llval, var_ty, lvalue);
 
     // Subtle: be sure that we *populate* the memory *before*
     // we schedule the cleanup.
-    let bcx = populate(arg, bcx, llval, var_ty);
+    let bcx = populate(arg, bcx, datum);
     bcx.fcx.schedule_lifetime_end(cleanup_scope, llval);
-    bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty);
+    bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty, lvalue.dropflag_hint(bcx));
 
     // Now that memory is initialized and has cleanup scheduled,
-    // create the datum and insert into the local variable map.
-    let lval = Lvalue::binding(caller_name, bcx, p_id, name);
-    let datum = Datum::new(llval, var_ty, lval);
+    // insert datum into the local variable map.
     bcx.fcx.lllocals.borrow_mut().insert(p_id, datum);
     bcx
 }
@@ -1746,7 +1765,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 bcx = mk_binding_alloca(
                     bcx, pat.id, path1.node.name, cleanup_scope, (),
                     "_match::bind_irrefutable_pat",
-                    |(), bcx, llval, ty| {
+                    |(), bcx, Datum { val: llval, ty, kind: _ }| {
                         match pat_binding_mode {
                             ast::BindByValue(_) => {
                                 // By value binding: move the value that `val`
@@ -1854,10 +1873,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         ast::PatBox(ref inner) => {
             let llbox = Load(bcx, val.val);
             bcx = bind_irrefutable_pat(
-                bcx,
-                &**inner,
-                MatchInput::from_val(llbox),
-                cleanup_scope);
+                bcx, &**inner, MatchInput::from_val(llbox), cleanup_scope);
         }
         ast::PatRegion(ref inner, _) => {
             let loaded_val = Load(bcx, val.val);
@@ -1884,13 +1900,13 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 .chain(slice.iter())
                 .chain(after.iter())
                 .zip(extracted.vals)
-                .fold(bcx, |bcx, (inner, elem)|
+                .fold(bcx, |bcx, (inner, elem)| {
                     bind_irrefutable_pat(
                         bcx,
                         &**inner,
                         MatchInput::from_val(elem),
                         cleanup_scope)
-                );
+                });
         }
         ast::PatMac(..) => {
             bcx.sess().span_bug(pat.span, "unexpanded macro");
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index d2753f5b78f..b47d2dd4112 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -163,6 +163,20 @@ macro_rules! repeat_u8_as_u64 {
                        (repeat_u8_as_u32!($name) as u64)) }
 }
 
+/// `DTOR_NEEDED_HINT` is a stack-local hint that just means
+/// "we do not know whether the destructor has run or not; check the
+/// drop-flag embedded in the value itself."
+pub const DTOR_NEEDED_HINT: u8 = 0x3d;
+
+/// `DTOR_MOVED_HINT` is a stack-local hint that means "this value has
+/// definitely been moved; you do not need to run its destructor."
+///
+/// (However, for now, such values may still end up being explicitly
+/// zeroed by the generated code; this is the distinction between
+/// `datum::DropFlagInfo::ZeroAndMaintain` versus
+/// `datum::DropFlagInfo::DontZeroJustUse`.)
+pub const DTOR_MOVED_HINT: u8 = 0x2d;
+
 pub const DTOR_NEEDED: u8 = 0xd4;
 pub const DTOR_NEEDED_U32: u32 = repeat_u8_as_u32!(DTOR_NEEDED);
 pub const DTOR_NEEDED_U64: u64 = repeat_u8_as_u64!(DTOR_NEEDED);
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index ec7ff4fa748..61e81d75607 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -51,8 +51,7 @@ use trans::attributes;
 use trans::build::*;
 use trans::builder::{Builder, noname};
 use trans::callee;
-use trans::cleanup::CleanupMethods;
-use trans::cleanup;
+use trans::cleanup::{self, CleanupMethods, DropHint};
 use trans::closure;
 use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
 use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
@@ -88,7 +87,7 @@ use arena::TypedArena;
 use libc::c_uint;
 use std::ffi::{CStr, CString};
 use std::cell::{Cell, RefCell};
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
 use std::mem;
 use std::str;
 use std::{i8, i16, i32, i64};
@@ -1284,6 +1283,54 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>,
         }
     }
 
+    // Create the drop-flag hints for every unfragmented path in the function.
+    let tcx = fcx.ccx.tcx();
+    let fn_did = ast::DefId { krate: ast::LOCAL_CRATE, node: fcx.id };
+    let mut hints = fcx.lldropflag_hints.borrow_mut();
+    let fragment_infos = tcx.fragment_infos.borrow();
+
+    // Intern table for drop-flag hint datums.
+    let mut seen = HashMap::new();
+
+    if let Some(fragment_infos) = fragment_infos.get(&fn_did) {
+        for &info in fragment_infos {
+
+            let make_datum = |id| {
+                let init_val = C_u8(fcx.ccx, adt::DTOR_NEEDED_HINT as usize);
+                let llname = &format!("dropflag_hint_{}", id);
+                debug!("adding hint {}", llname);
+                let ptr = alloc_ty(entry_bcx, tcx.types.u8, llname);
+                Store(entry_bcx, init_val, ptr);
+                let ty = tcx.mk_ptr(ty::TypeAndMut { ty: tcx.types.u8, mutbl: ast::MutMutable });
+                let flag = datum::Lvalue::new_dropflag_hint("base::init_function");
+                let datum = datum::Datum::new(ptr, ty, flag);
+                datum
+            };
+
+            let (var, datum) = match info {
+                ty::FragmentInfo::Moved { var, .. } |
+                ty::FragmentInfo::Assigned { var, .. } => {
+                    let datum = seen.get(&var).cloned().unwrap_or_else(|| {
+                        let datum = make_datum(var);
+                        seen.insert(var, datum.clone());
+                        datum
+                    });
+                    (var, datum)
+                }
+            };
+            match info {
+                ty::FragmentInfo::Moved { move_expr: expr_id, .. } => {
+                    debug!("FragmentInfo::Moved insert drop hint for {}", expr_id);
+                    hints.insert(expr_id, DropHint::new(var, datum));
+                }
+                ty::FragmentInfo::Assigned { assignee_id: expr_id, .. } => {
+                    debug!("FragmentInfo::Assigned insert drop hint for {}", expr_id);
+                    hints.insert(expr_id, DropHint::new(var, datum));
+                }
+            }
+        }
+    }
+
     entry_bcx
 }
 
diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs
index fe996020267..b4b0472512e 100644
--- a/src/librustc_trans/trans/cleanup.rs
+++ b/src/librustc_trans/trans/cleanup.rs
@@ -219,6 +219,23 @@ pub struct DropHint<K>(pub ast::NodeId, pub K);
 pub type DropHintDatum<'tcx> = DropHint<Datum<'tcx, Lvalue>>;
 pub type DropHintValue = DropHint<ValueRef>;
 
+impl<K> DropHint<K> {
+    pub fn new(id: ast::NodeId, k: K) -> DropHint<K> { DropHint(id, k) }
+}
+
+impl DropHint<ValueRef> {
+    pub fn value(&self) -> ValueRef { self.1 }
+}
+
+pub trait DropHintMethods {
+    type ValueKind;
+    fn to_value(&self) -> Self::ValueKind;
+}
+impl<'tcx> DropHintMethods for DropHintDatum<'tcx> {
+    type ValueKind = DropHintValue;
+    fn to_value(&self) -> DropHintValue { DropHint(self.0, self.1.val) }
+}
+
 impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
     /// Invoked when we start to trans the code contained within a new cleanup scope.
     fn push_ast_cleanup_scope(&self, debug_loc: NodeIdAndSpan) {
@@ -389,14 +406,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
     fn schedule_drop_mem(&self,
                          cleanup_scope: ScopeId,
                          val: ValueRef,
-                         ty: Ty<'tcx>) {
+                         ty: Ty<'tcx>,
+                         drop_hint: Option<DropHintDatum<'tcx>>) {
         if !self.type_needs_drop(ty) { return; }
+        let drop_hint = drop_hint.map(|hint|hint.to_value());
         let drop = box DropValue {
             is_immediate: false,
             val: val,
             ty: ty,
             fill_on_drop: false,
             skip_dtor: false,
+            drop_hint: drop_hint,
         };
 
         debug!("schedule_drop_mem({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
@@ -413,23 +433,28 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
     fn schedule_drop_and_fill_mem(&self,
                                   cleanup_scope: ScopeId,
                                   val: ValueRef,
-                                  ty: Ty<'tcx>) {
+                                  ty: Ty<'tcx>,
+                                  drop_hint: Option<DropHintDatum<'tcx>>) {
         if !self.type_needs_drop(ty) { return; }
 
+        let drop_hint = drop_hint.map(|datum|datum.to_value());
         let drop = box DropValue {
             is_immediate: false,
             val: val,
             ty: ty,
             fill_on_drop: true,
             skip_dtor: false,
+            drop_hint: drop_hint,
         };
 
-        debug!("schedule_drop_and_fill_mem({:?}, val={}, ty={:?}, fill_on_drop={}, skip_dtor={})",
+        debug!("schedule_drop_and_fill_mem({:?}, val={}, ty={:?},
+                fill_on_drop={}, skip_dtor={}, has_drop_hint={})",
                cleanup_scope,
                self.ccx.tn().val_to_string(val),
                ty,
                drop.fill_on_drop,
-               drop.skip_dtor);
+               drop.skip_dtor,
+               drop_hint.is_some());
 
         self.schedule_clean(cleanup_scope, drop as CleanupObj);
     }
@@ -453,6 +478,7 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
             ty: ty,
             fill_on_drop: false,
             skip_dtor: true,
+            drop_hint: None,
         };
 
         debug!("schedule_drop_adt_contents({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
@@ -472,13 +498,14 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
                                ty: Ty<'tcx>) {
 
         if !self.type_needs_drop(ty) { return; }
-        let drop = box DropValue {
+        let drop = Box::new(DropValue {
             is_immediate: true,
             val: val,
             ty: ty,
             fill_on_drop: false,
             skip_dtor: false,
-        };
+            drop_hint: None,
+        });
 
         debug!("schedule_drop_immediate({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
                cleanup_scope,
@@ -983,6 +1010,7 @@ pub struct DropValue<'tcx> {
     ty: Ty<'tcx>,
     fill_on_drop: bool,
     skip_dtor: bool,
+    drop_hint: Option<DropHintValue>,
 }
 
 impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> {
@@ -1007,7 +1035,7 @@ impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> {
         let bcx = if self.is_immediate {
             glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc, self.skip_dtor)
         } else {
-            glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor)
+            glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor, self.drop_hint)
         };
         if self.fill_on_drop {
             base::drop_done_fill_mem(bcx, self.val, self.ty);
@@ -1135,11 +1163,13 @@ pub trait CleanupMethods<'blk, 'tcx> {
     fn schedule_drop_mem(&self,
                          cleanup_scope: ScopeId,
                          val: ValueRef,
-                         ty: Ty<'tcx>);
+                         ty: Ty<'tcx>,
+                         drop_hint: Option<DropHintDatum<'tcx>>);
     fn schedule_drop_and_fill_mem(&self,
                                   cleanup_scope: ScopeId,
                                   val: ValueRef,
-                                  ty: Ty<'tcx>);
+                                  ty: Ty<'tcx>,
+                                  drop_hint: Option<DropHintDatum<'tcx>>);
     fn schedule_drop_adt_contents(&self,
                                   cleanup_scope: ScopeId,
                                   val: ValueRef,
diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs
index ef5da3e40df..d9787231096 100644
--- a/src/librustc_trans/trans/closure.rs
+++ b/src/librustc_trans/trans/closure.rs
@@ -82,9 +82,11 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr);
 
         if kind == ty::FnOnceClosureKind && !captured_by_ref {
+            let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id);
             bcx.fcx.schedule_drop_mem(arg_scope_id,
                                       upvar_ptr,
-                                      node_id_type(bcx, def_id.node))
+                                      node_id_type(bcx, def_id.node),
+                                      hint)
         }
 
         if let Some(env_pointer_alloca) = env_pointer_alloca {
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 82c835e8c34..9478c197a44 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -318,6 +318,12 @@ pub struct DropFlagHintsMap<'tcx> {
 impl<'tcx> DropFlagHintsMap<'tcx> {
     pub fn new() -> DropFlagHintsMap<'tcx> { DropFlagHintsMap { node_map: NodeMap() } }
     pub fn has_hint(&self, id: ast::NodeId) -> bool { self.node_map.contains_key(&id) }
+    pub fn insert(&mut self, id: ast::NodeId, datum: cleanup::DropHintDatum<'tcx>) {
+        self.node_map.insert(id, HintEntry { datum: datum });
+    }
+    pub fn hint_datum(&self, id: ast::NodeId) -> Option<cleanup::DropHintDatum<'tcx>> {
+        self.node_map.get(&id).map(|t|t.datum)
+    }
 }
 
 // Function context.  Every LLVM function we create will have one of
diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs
index f14b6f368f4..28fbc9fc8d5 100644
--- a/src/librustc_trans/trans/datum.rs
+++ b/src/librustc_trans/trans/datum.rs
@@ -93,11 +93,12 @@ pub use self::Expr::*;
 pub use self::RvalueMode::*;
 
 use llvm::ValueRef;
+use trans::adt;
 use trans::base::*;
-use trans::build::Load;
+use trans::build::{Load, Store};
 use trans::common::*;
 use trans::cleanup;
-use trans::cleanup::CleanupMethods;
+use trans::cleanup::{CleanupMethods, DropHintDatum, DropHintMethods};
 use trans::expr;
 use trans::tvec;
 use trans::type_of;
@@ -111,7 +112,7 @@ use syntax::codemap::DUMMY_SP;
 /// 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 `README.md` for more details.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 pub struct Datum<'tcx, K> {
     /// The llvm value.  This is either a pointer to the Rust value or
     /// the value itself, depending on `kind` below.
@@ -141,7 +142,7 @@ pub enum Expr {
     LvalueExpr(Lvalue),
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum DropFlagInfo {
     DontZeroJustUse(ast::NodeId),
     ZeroAndMaintain(ast::NodeId),
@@ -157,12 +158,18 @@ impl DropFlagInfo {
         }
     }
 
-    pub fn hint_to_maintain(&self) -> Option<ast::NodeId> {
-        match *self {
-            DropFlagInfo::DontZeroJustUse(id) => Some(id),
-            DropFlagInfo::ZeroAndMaintain(id) => Some(id),
-            DropFlagInfo::None => None,
-        }
+    pub fn hint_datum<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>)
+                              -> Option<DropHintDatum<'tcx>> {
+        let id = match *self {
+            DropFlagInfo::None => return None,
+            DropFlagInfo::DontZeroJustUse(id) |
+            DropFlagInfo::ZeroAndMaintain(id) => id,
+        };
+
+        let hints = bcx.fcx.lldropflag_hints.borrow();
+        let retval = hints.hint_datum(id);
+        assert!(retval.is_some(), "An id (={}) means must have a hint", id);
+        retval
     }
 }
 
@@ -182,7 +189,8 @@ pub struct Rvalue {
     pub mode: RvalueMode
 }
 
-impl Lvalue {
+// XXX: reduce this to a smaller kernel of constructors.
+impl Lvalue { // These are all constructors for various Lvalues.
     pub fn new(source: &'static str) -> Lvalue {
         Lvalue { source: source, drop_flag_info: DropFlagInfo::None }
     }
@@ -267,9 +275,21 @@ impl Lvalue {
         Lvalue { source: source, drop_flag_info: info }
     }
 
+    pub fn new_dropflag_hint(source: &'static str) -> Lvalue {
+        debug!("dropflag hint Lvalue at {}", source);
+        Lvalue { source: source, drop_flag_info: DropFlagInfo::None }
+    }
+} // end Lvalue constructor methods.
+
+impl Lvalue {
     fn has_dropflag_hint<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                      id: ast::NodeId) -> bool {
-        bcx.fcx.lldropflag_hints.borrow().has_hint(id)
+        let hints = bcx.fcx.lldropflag_hints.borrow();
+        hints.has_hint(id)
+    }
+    pub fn dropflag_hint<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>)
+                                 -> Option<DropHintDatum<'tcx>> {
+        self.drop_flag_info.hint_datum(bcx)
     }
 }
 
@@ -323,7 +343,7 @@ pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>,
     // Subtle. Populate the scratch memory *before* scheduling cleanup.
     let bcx = populate(arg, bcx, scratch);
     bcx.fcx.schedule_lifetime_end(scope, scratch);
-    bcx.fcx.schedule_drop_mem(scope, scratch, ty);
+    bcx.fcx.schedule_drop_mem(scope, scratch, ty, None);
 
     DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum")))
 }
@@ -362,7 +382,7 @@ fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode,
         ByValue => { fcx.schedule_drop_immediate(scope, val, ty); }
         ByRef => {
             fcx.schedule_lifetime_end(scope, val);
-            fcx.schedule_drop_mem(scope, val, ty);
+            fcx.schedule_drop_mem(scope, val, ty, None);
         }
     }
 }
@@ -419,10 +439,28 @@ impl KindOps for Lvalue {
                               -> Block<'blk, 'tcx> {
         let _icx = push_ctxt("<Lvalue as KindOps>::post_store");
         if bcx.fcx.type_needs_drop(ty) {
-            // cancel cleanup of affine values by drop-filling the memory
-            let () = drop_done_fill_mem(bcx, val, ty);
+            // cancel cleanup of affine values:
+            // 1. if it has drop-hint, mark as moved; then code
+            //    aware of drop-hint won't bother calling the
+            //    drop-glue itself.
+            if let Some(hint_datum) = self.drop_flag_info.hint_datum(bcx) {
+                let moved_hint_byte = adt::DTOR_MOVED_HINT as usize;
+                let hint_llval = hint_datum.to_value().value();
+                Store(bcx, C_u8(bcx.fcx.ccx, moved_hint_byte), hint_llval);
+            }
+            // 2. if the drop info says its necessary, drop-fill the memory.
+            if self.drop_flag_info.must_zero() {
+                let () = drop_done_fill_mem(bcx, val, ty);
+            }
             bcx
         } else {
+            // XXX would be nice to assert this, but we currently are
+            // adding e.g.  DontZeroJustUse flags. (The dropflag hint
+            // construction should be taking !type_needs_drop into
+            // account; earlier analysis phases may not have all the
+            // info they need to do it properly, I think...)
+            //
+            // assert_eq!(self.drop_flag_info, DropFlagInfo::None);
             bcx
         }
     }
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 1582a43d94d..ba0ae27a005 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -61,7 +61,7 @@ use middle::traits;
 use trans::{_match, adt, asm, base, callee, closure, consts, controlflow};
 use trans::base::*;
 use trans::build::*;
-use trans::cleanup::{self, CleanupMethods};
+use trans::cleanup::{self, CleanupMethods, DropHintMethods};
 use trans::common::*;
 use trans::datum::*;
 use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
@@ -1004,10 +1004,26 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
                 let src_datum = unpack_datum!(
                     bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign"));
-                bcx = glue::drop_ty(bcx,
-                                    dst_datum.val,
-                                    dst_datum.ty,
-                                    expr.debug_loc());
+                if let Some(hint_datum) = dst_datum.kind.drop_flag_info.hint_datum(bcx) {
+                    let hint_val = hint_datum.to_value();
+                    // XXX the checkpointed branch only does the
+                    // drop_ty call within this branch (and I claim
+                    // that seems like a bug). At this point I have
+                    // moved it into the branch solely to see if it
+                    // makes my plague of bugs go away.
+                    bcx = glue::drop_ty_core(bcx,
+                                             dst_datum.val,
+                                             dst_datum.ty,
+                                             expr.debug_loc(),
+                                             false,
+                                             Some(hint_val));
+                    // We are initializing or overwriting the
+                    // destination, so we need to write "drop needed"
+                    // into the hint.
+                    let hint_llval = hint_val.value();
+                    let drop_needed = C_u8(bcx.fcx.ccx, adt::DTOR_NEEDED_HINT as usize);
+                    Store(bcx, drop_needed, hint_llval);
+                }
                 src_datum.store_to(bcx, dst_datum.val)
             } else {
                 src_datum.store_to(bcx, dst_datum.val)
@@ -1578,7 +1594,8 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
             bcx = trans_into(bcx, &**e, SaveIn(dest));
             let scope = cleanup::CustomScope(custom_cleanup_scope);
             fcx.schedule_lifetime_end(scope, dest);
-            fcx.schedule_drop_mem(scope, dest, e_ty);
+            // FIXME: nonzeroing move should generalize to fields
+            fcx.schedule_drop_mem(scope, dest, e_ty, None);
         }
     }
 
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index e530eb0de59..6019099c058 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -130,17 +130,20 @@ pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                            v: ValueRef,
                            t: Ty<'tcx>,
                            debug_loc: DebugLoc) -> Block<'blk, 'tcx> {
-    drop_ty_core(bcx, v, t, debug_loc, false)
+    drop_ty_core(bcx, v, t, debug_loc, false, None)
 }
 
 pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                 v: ValueRef,
                                 t: Ty<'tcx>,
                                 debug_loc: DebugLoc,
-                                skip_dtor: bool) -> Block<'blk, 'tcx> {
+                                skip_dtor: bool,
+                                drop_hint: Option<cleanup::DropHintValue>)
+                                -> Block<'blk, 'tcx> {
     // NB: v is an *alias* of type t here, not a direct value.
-    debug!("drop_ty_core(t={:?}, skip_dtor={})", t, skip_dtor);
+    debug!("drop_ty_core(t={:?}, skip_dtor={} drop_hint={:?})", t, skip_dtor, drop_hint);
     let _icx = push_ctxt("drop_ty");
+    let mut bcx = bcx;
     if bcx.fcx.type_needs_drop(t) {
         let ccx = bcx.ccx();
         let g = if skip_dtor {
@@ -156,7 +159,23 @@ pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             v
         };
 
-        Call(bcx, glue, &[ptr], None, debug_loc);
+        match drop_hint {
+            Some(drop_hint) => {
+                let hint_val = load_ty(bcx, drop_hint.value(), bcx.tcx().types.u8);
+                let moved_val =
+                    C_integral(Type::i8(bcx.ccx()), adt::DTOR_MOVED_HINT as u64, false);
+                let may_need_drop =
+                    ICmp(bcx, llvm::IntNE, hint_val, moved_val, DebugLoc::None);
+                bcx = with_cond(bcx, may_need_drop, |cx| {
+                    Call(cx, glue, &[ptr], None, debug_loc);
+                    cx
+                })
+            }
+            None => {
+                // No drop-hint ==> call standard drop glue
+                Call(bcx, glue, &[ptr], None, debug_loc);
+            }
+        }
     }
     bcx
 }
@@ -170,7 +189,7 @@ pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _icx = push_ctxt("drop_ty_immediate");
     let vp = alloca(bcx, type_of(bcx.ccx(), t), "");
     store_ty(bcx, v, vp, t);
-    drop_ty_core(bcx, vp, t, debug_loc, skip_dtor)
+    drop_ty_core(bcx, vp, t, debug_loc, skip_dtor, None)
 }
 
 pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef {
diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs
index 0e05ca52959..a9abc61c274 100644
--- a/src/librustc_trans/trans/tvec.rs
+++ b/src/librustc_trans/trans/tvec.rs
@@ -117,7 +117,7 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         // Arrange for the backing array to be cleaned up.
         let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id);
         fcx.schedule_lifetime_end(cleanup_scope, llfixed);
-        fcx.schedule_drop_mem(cleanup_scope, llfixed, fixed_ty);
+        fcx.schedule_drop_mem(cleanup_scope, llfixed, fixed_ty, None);
 
         // Generate the content into the backing array.
         // llfixed has type *[T x N], but we want the type *T,
@@ -212,7 +212,7 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                                SaveIn(lleltptr));
                         let scope = cleanup::CustomScope(temp_scope);
                         fcx.schedule_lifetime_end(scope, lleltptr);
-                        fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty);
+                        fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty, None);
                     }
                     fcx.pop_custom_cleanup_scope(temp_scope);
                 }