about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-07-22 23:11:12 +0000
committerbors <bors@rust-lang.org>2014-07-22 23:11:12 +0000
commit217f1fbfc861cd6da41746f899bad79b37f471ab (patch)
tree05a42b3d59a43879c11416936d7e85dbf9cf67d7
parent43d84bf32ef8437b39cbb89cc30c6fd293df7b88 (diff)
parent59edfdd2ab3bad73086940afe1a57fa4706a4d2f (diff)
downloadrust-217f1fbfc861cd6da41746f899bad79b37f471ab.tar.gz
rust-217f1fbfc861cd6da41746f899bad79b37f471ab.zip
auto merge of #15272 : jakub-/rust/issue-13041, r=pcwalton
Fixes #13041.
-rw-r--r--src/librustc/middle/kind.rs23
-rw-r--r--src/librustc/middle/trans/adt.rs144
-rw-r--r--src/librustc/middle/trans/base.rs2
-rw-r--r--src/librustc/middle/trans/debuginfo.rs4
-rw-r--r--src/librustc/middle/trans/glue.rs58
-rw-r--r--src/librustc/middle/ty.rs13
-rw-r--r--src/test/compile-fail/kindck-destructor-owned.rs2
-rw-r--r--src/test/run-pass/drop-trait-enum.rs94
-rw-r--r--src/test/run-pass/drop-uninhabited-enum.rs19
-rw-r--r--src/test/run-pass/issue-10802.rs16
-rw-r--r--src/test/run-pass/issue-6892.rs21
11 files changed, 312 insertions, 84 deletions
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index d2a5342c17e..517bdcbff96 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -13,8 +13,10 @@ use middle::freevars::freevar_entry;
 use middle::freevars;
 use middle::subst;
 use middle::ty;
-use middle::typeck::{MethodCall, NoAdjustment};
+use middle::ty_fold;
+use middle::ty_fold::TypeFoldable;
 use middle::typeck;
+use middle::typeck::{MethodCall, NoAdjustment};
 use util::ppaux::{Repr, ty_to_string};
 use util::ppaux::UserString;
 
@@ -83,18 +85,29 @@ pub fn check_crate(tcx: &ty::ctxt,
     tcx.sess.abort_if_errors();
 }
 
+struct EmptySubstsFolder<'a> {
+    tcx: &'a ty::ctxt
+}
+impl<'a> ty_fold::TypeFolder for EmptySubstsFolder<'a> {
+    fn tcx<'a>(&'a self) -> &'a ty::ctxt {
+        self.tcx
+    }
+    fn fold_substs(&mut self, _: &subst::Substs) -> subst::Substs {
+        subst::Substs::empty()
+    }
+}
+
 fn check_struct_safe_for_destructor(cx: &mut Context,
                                     span: Span,
                                     struct_did: DefId) {
     let struct_tpt = ty::lookup_item_type(cx.tcx, struct_did);
     if !struct_tpt.generics.has_type_params(subst::TypeSpace)
       && !struct_tpt.generics.has_region_params(subst::TypeSpace) {
-        let struct_ty = ty::mk_struct(cx.tcx, struct_did,
-                                      subst::Substs::empty());
-        if !ty::type_is_sendable(cx.tcx, struct_ty) {
+        let mut folder = EmptySubstsFolder { tcx: cx.tcx };
+        if !ty::type_is_sendable(cx.tcx, struct_tpt.ty.fold_with(&mut folder)) {
             span_err!(cx.tcx.sess, span, E0125,
                       "cannot implement a destructor on a \
-                       structure that does not satisfy Send");
+                       structure or enumeration that does not satisfy Send");
             span_note!(cx.tcx.sess, span,
                        "use \"#[unsafe_destructor]\" on the implementation \
                         to force the compiler to allow this");
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 54c30e72154..9ec0407b5c3 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -53,7 +53,10 @@ use middle::subst;
 use middle::subst::Subst;
 use middle::trans::_match;
 use middle::trans::build::*;
+use middle::trans::cleanup;
+use middle::trans::cleanup::CleanupMethods;
 use middle::trans::common::*;
+use middle::trans::datum;
 use middle::trans::machine;
 use middle::trans::type_::Type;
 use middle::trans::type_of;
@@ -83,8 +86,12 @@ pub enum Repr {
     /**
      * General-case enums: for each case there is a struct, and they
      * all start with a field for the discriminant.
+     *
+     * Types with destructors need a dynamic destroyedness flag to
+     * avoid running the destructor too many times; the last argument
+     * indicates whether such a flag is present.
      */
-    General(IntType, Vec<Struct>),
+    General(IntType, Vec<Struct>, bool),
     /**
      * Two cases distinguished by a nullable pointer: the case with discriminant
      * `nndiscr` must have single field which is known to be nonnull due to its type.
@@ -121,7 +128,7 @@ pub struct Struct {
     pub size: u64,
     pub align: u64,
     pub packed: bool,
-    pub fields: Vec<ty::t>,
+    pub fields: Vec<ty::t>
 }
 
 /**
@@ -173,14 +180,17 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
             let cases = get_cases(cx.tcx(), def_id, substs);
             let hint = ty::lookup_repr_hint(cx.tcx(), def_id);
 
+            let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag();
+
             if cases.len() == 0 {
                 // Uninhabitable; represent as unit
                 // (Typechecking will reject discriminant-sizing attrs.)
                 assert_eq!(hint, attr::ReprAny);
-                return Univariant(mk_struct(cx, [], false), false);
+                let ftys = if dtor { vec!(ty::mk_bool()) } else { vec!() };
+                return Univariant(mk_struct(cx, ftys.as_slice(), false), dtor);
             }
 
-            if cases.iter().all(|c| c.tys.len() == 0) {
+            if !dtor && cases.iter().all(|c| c.tys.len() == 0) {
                 // All bodies empty -> intlike
                 let discrs: Vec<u64> = cases.iter().map(|c| c.discr).collect();
                 let bounds = IntBounds {
@@ -199,20 +209,19 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
                 cx.sess().bug(format!("non-C-like enum {} with specified \
                                       discriminants",
                                       ty::item_path_str(cx.tcx(),
-                                                        def_id)).as_slice())
+                                                        def_id)).as_slice());
             }
 
             if cases.len() == 1 {
                 // Equivalent to a struct/tuple/newtype.
                 // (Typechecking will reject discriminant-sizing attrs.)
                 assert_eq!(hint, attr::ReprAny);
-                return Univariant(mk_struct(cx,
-                                            cases.get(0).tys.as_slice(),
-                                            false),
-                                  false)
+                let mut ftys = cases.get(0).tys.clone();
+                if dtor { ftys.push(ty::mk_bool()); }
+                return Univariant(mk_struct(cx, ftys.as_slice(), false), dtor);
             }
 
-            if cases.len() == 2 && hint == attr::ReprAny {
+            if !dtor && cases.len() == 2 && hint == attr::ReprAny {
                 // Nullable pointer optimization
                 let mut discr = 0;
                 while discr < 2 {
@@ -246,10 +255,12 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
             let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64,
                                      slo: 0, shi: (cases.len() - 1) as i64 };
             let ity = range_to_inttype(cx, hint, &bounds);
+
             return General(ity, cases.iter().map(|c| {
-                let discr = vec!(ty_of_inttype(ity));
-                mk_struct(cx, discr.append(c.tys.as_slice()).as_slice(), false)
-            }).collect())
+                let mut ftys = vec!(ty_of_inttype(ity)).append(c.tys.as_slice());
+                if dtor { ftys.push(ty::mk_bool()); }
+                mk_struct(cx, ftys.as_slice(), false)
+            }).collect(), dtor);
         }
         _ => cx.sess().bug("adt::represent_type called on non-ADT type")
     }
@@ -359,7 +370,6 @@ fn get_cases(tcx: &ty::ctxt, def_id: ast::DefId, substs: &subst::Substs) -> Vec<
     }).collect()
 }
 
-
 fn mk_struct(cx: &CrateContext, tys: &[ty::t], packed: bool) -> Struct {
     let lltys = tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)).collect::<Vec<_>>();
     let llty_rec = Type::struct_(cx, lltys.as_slice(), packed);
@@ -499,7 +509,7 @@ fn generic_type_of(cx: &CrateContext, r: &Repr, name: Option<&str>, sizing: bool
                 Some(name) => { assert_eq!(sizing, false); Type::named_struct(cx, name) }
             }
         }
-        General(ity, ref sts) => {
+        General(ity, ref sts, _) => {
             // We need a representation that has:
             // * The alignment of the most-aligned field
             // * The size of the largest variant (rounded up to that alignment)
@@ -584,7 +594,7 @@ pub fn trans_get_discr(bcx: &Block, r: &Repr, scrutinee: ValueRef, cast_to: Opti
             val = load_discr(bcx, ity, scrutinee, min, max);
             signed = ity.is_signed();
         }
-        General(ity, ref cases) => {
+        General(ity, ref cases, _) => {
             let ptr = GEPi(bcx, scrutinee, [0, 0]);
             val = load_discr(bcx, ity, ptr, 0, (cases.len() - 1) as Disr);
             signed = ity.is_signed();
@@ -658,7 +668,7 @@ pub fn trans_case<'a>(bcx: &'a Block<'a>, r: &Repr, discr: Disr)
             _match::single_result(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
                                                               discr as u64, true)))
         }
-        General(ity, _) => {
+        General(ity, _, _) => {
             _match::single_result(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
                                                               discr as u64, true)))
         }
@@ -684,17 +694,21 @@ pub fn trans_set_discr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr) {
             Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
                   val)
         }
-        General(ity, _) => {
+        General(ity, ref cases, dtor) => {
+            if dtor {
+                let ptr = trans_field_ptr(bcx, r, val, discr,
+                                          cases.get(discr as uint).fields.len() - 2);
+                Store(bcx, C_u8(bcx.ccx(), 1), ptr);
+            }
             Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
                   GEPi(bcx, val, [0, 0]))
         }
-        Univariant(ref st, true) => {
-            assert_eq!(discr, 0);
-            Store(bcx, C_u8(bcx.ccx(), 1),
-                  GEPi(bcx, val, [0, st.fields.len() - 1]))
-        }
-        Univariant(..) => {
+        Univariant(ref st, dtor) => {
             assert_eq!(discr, 0);
+            if dtor {
+                Store(bcx, C_u8(bcx.ccx(), 1),
+                    GEPi(bcx, val, [0, st.fields.len() - 1]));
+            }
         }
         RawNullablePointer { nndiscr, nnty, ..} => {
             if discr != nndiscr {
@@ -737,7 +751,9 @@ pub fn num_args(r: &Repr, discr: Disr) -> uint {
             assert_eq!(discr, 0);
             st.fields.len() - (if dtor { 1 } else { 0 })
         }
-        General(_, ref cases) => cases.get(discr as uint).fields.len() - 1,
+        General(_, ref cases, dtor) => {
+            cases.get(discr as uint).fields.len() - 1 - (if dtor { 1 } else { 0 })
+        }
         RawNullablePointer { nndiscr, ref nullfields, .. } => {
             if discr == nndiscr { 1 } else { nullfields.len() }
         }
@@ -762,7 +778,7 @@ pub fn trans_field_ptr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr,
             assert_eq!(discr, 0);
             struct_field_ptr(bcx, st, val, ix, false)
         }
-        General(_, ref cases) => {
+        General(_, ref cases, _) => {
             struct_field_ptr(bcx, cases.get(discr as uint), val, ix + 1, true)
         }
         RawNullablePointer { nndiscr, ref nullfields, .. } |
@@ -788,11 +804,10 @@ pub fn trans_field_ptr(bcx: &Block, r: &Repr, val: ValueRef, discr: Disr,
     }
 }
 
-fn struct_field_ptr(bcx: &Block, st: &Struct, val: ValueRef, ix: uint,
-              needs_cast: bool) -> ValueRef {
-    let ccx = bcx.ccx();
-
+pub fn struct_field_ptr(bcx: &Block, st: &Struct, val: ValueRef,
+                        ix: uint, needs_cast: bool) -> ValueRef {
     let val = if needs_cast {
+        let ccx = bcx.ccx();
         let fields = st.fields.iter().map(|&ty| type_of::type_of(ccx, ty)).collect::<Vec<_>>();
         let real_ty = Type::struct_(ccx, fields.as_slice(), st.packed);
         PointerCast(bcx, val, real_ty.ptr_to())
@@ -803,10 +818,71 @@ fn struct_field_ptr(bcx: &Block, st: &Struct, val: ValueRef, ix: uint,
     GEPi(bcx, val, [0, ix])
 }
 
+pub fn fold_variants<'r, 'b>(
+    bcx: &'b Block<'b>, r: &Repr, value: ValueRef,
+    f: |&'b Block<'b>, &Struct, ValueRef|: 'r -> &'b Block<'b>
+) -> &'b Block<'b> {
+    let fcx = bcx.fcx;
+    match *r {
+        Univariant(ref st, _) => {
+            f(bcx, st, value)
+        }
+        General(ity, ref cases, _) => {
+            let ccx = bcx.ccx();
+            let unr_cx = fcx.new_temp_block("enum-variant-iter-unr");
+            Unreachable(unr_cx);
+
+            let discr_val = trans_get_discr(bcx, r, value, None);
+            let llswitch = Switch(bcx, discr_val, unr_cx.llbb, cases.len());
+            let bcx_next = fcx.new_temp_block("enum-variant-iter-next");
+
+            for (discr, case) in cases.iter().enumerate() {
+                let mut variant_cx = fcx.new_temp_block(
+                    format!("enum-variant-iter-{}", discr.to_string()).as_slice()
+                );
+                let rhs_val = C_integral(ll_inttype(ccx, ity), discr as u64, true);
+                AddCase(llswitch, rhs_val, variant_cx.llbb);
+
+                let fields = case.fields.iter().map(|&ty|
+                    type_of::type_of(bcx.ccx(), ty)).collect::<Vec<_>>();
+                let real_ty = Type::struct_(ccx, fields.as_slice(), case.packed);
+                let variant_value = PointerCast(variant_cx, value, real_ty.ptr_to());
+
+                variant_cx = f(variant_cx, case, variant_value);
+                Br(variant_cx, bcx_next.llbb);
+            }
+
+            bcx_next
+        }
+        _ => unreachable!()
+    }
+}
+
 /// Access the struct drop flag, if present.
-pub fn trans_drop_flag_ptr(bcx: &Block, r: &Repr, val: ValueRef) -> ValueRef {
+pub fn trans_drop_flag_ptr<'b>(mut bcx: &'b Block<'b>, r: &Repr,
+                               val: ValueRef) -> datum::DatumBlock<'b, datum::Expr> {
+    let ptr_ty = ty::mk_imm_ptr(bcx.tcx(), ty::mk_bool());
     match *r {
-        Univariant(ref st, true) => GEPi(bcx, val, [0, st.fields.len() - 1]),
+        Univariant(ref st, true) => {
+            let flag_ptr = GEPi(bcx, val, [0, st.fields.len() - 1]);
+            datum::immediate_rvalue_bcx(bcx, flag_ptr, ptr_ty).to_expr_datumblock()
+        }
+        General(_, _, true) => {
+            let fcx = bcx.fcx;
+            let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
+            let scratch = unpack_datum!(bcx, datum::lvalue_scratch_datum(
+                bcx, ty::mk_bool(), "drop_flag", false,
+                cleanup::CustomScope(custom_cleanup_scope), (), |_, bcx, _| bcx
+            ));
+            bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
+                let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false);
+                datum::Datum::new(ptr, ptr_ty, datum::Rvalue::new(datum::ByRef))
+                    .store_to(variant_cx, scratch.val)
+            });
+            let expr_datum = scratch.to_expr_datum();
+            fcx.pop_custom_cleanup_scope(custom_cleanup_scope);
+            datum::DatumBlock::new(bcx, expr_datum)
+        }
         _ => bcx.ccx().sess().bug("tried to get drop flag of non-droppable type")
     }
 }
@@ -840,7 +916,7 @@ pub fn trans_const(ccx: &CrateContext, r: &Repr, discr: Disr,
             assert_discr_in_range(ity, min, max, discr);
             C_integral(ll_inttype(ccx, ity), discr as u64, true)
         }
-        General(ity, ref cases) => {
+        General(ity, ref cases, _) => {
             let case = cases.get(discr as uint);
             let max_sz = cases.iter().map(|x| x.size).max().unwrap();
             let lldiscr = C_integral(ll_inttype(ccx, ity), discr as u64, true);
@@ -964,7 +1040,7 @@ pub fn const_get_discrim(ccx: &CrateContext, r: &Repr, val: ValueRef)
                 attr::UnsignedInt(..) => const_to_uint(val) as Disr
             }
         }
-        General(ity, _) => {
+        General(ity, _, _) => {
             match ity {
                 attr::SignedInt(..) => const_to_int(const_get_elt(ccx, val, [0])) as Disr,
                 attr::UnsignedInt(..) => const_to_uint(const_get_elt(ccx, val, [0])) as Disr
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index a9d5b30b9c7..dbdb99d3ccd 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -1856,7 +1856,7 @@ fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span,
 
     let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id));
     match *avar {
-        adt::General(_, ref variants) => {
+        adt::General(_, ref variants, _) => {
             for var in variants.iter() {
                 let mut size = 0;
                 for field in var.fields.iter().skip(1) {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 47e94175546..66722b2c4db 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -1993,7 +1993,7 @@ struct EnumMemberDescriptionFactory {
 impl EnumMemberDescriptionFactory {
     fn create_member_descriptions(&self, cx: &CrateContext) -> Vec<MemberDescription> {
         match *self.type_rep {
-            adt::General(_, ref struct_defs) => {
+            adt::General(_, ref struct_defs, _) => {
                 let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata
                     .expect(""));
 
@@ -2362,7 +2362,7 @@ fn prepare_enum_metadata(cx: &CrateContext,
         adt::RawNullablePointer { .. }           |
         adt::StructWrappedNullablePointer { .. } |
         adt::Univariant(..)                      => None,
-        adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),
+        adt::General(inttype, _, _) => Some(discriminant_type_metadata(inttype)),
     };
 
     let enum_llvm_type = type_of::type_of(cx, enum_type);
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 7afba95a98b..2d2e5f141a5 100644
--- a/src/librustc/middle/trans/glue.rs
+++ b/src/librustc/middle/trans/glue.rs
@@ -204,7 +204,7 @@ fn make_visit_glue<'a>(bcx: &'a Block<'a>, v: ValueRef, t: ty::t)
     bcx
 }
 
-fn trans_struct_drop_flag<'a>(bcx: &'a Block<'a>,
+fn trans_struct_drop_flag<'a>(mut bcx: &'a Block<'a>,
                               t: ty::t,
                               v0: ValueRef,
                               dtor_did: ast::DefId,
@@ -212,8 +212,8 @@ fn trans_struct_drop_flag<'a>(bcx: &'a Block<'a>,
                               substs: &subst::Substs)
                               -> &'a Block<'a> {
     let repr = adt::represent_type(bcx.ccx(), t);
-    let drop_flag = adt::trans_drop_flag_ptr(bcx, &*repr, v0);
-    with_cond(bcx, load_ty(bcx, drop_flag, ty::mk_bool()), |cx| {
+    let drop_flag = unpack_datum!(bcx, adt::trans_drop_flag_ptr(bcx, &*repr, v0));
+    with_cond(bcx, load_ty(bcx, drop_flag.val, ty::mk_bool()), |cx| {
         trans_struct_drop(cx, t, v0, dtor_did, class_did, substs)
     })
 }
@@ -237,33 +237,33 @@ fn trans_struct_drop<'a>(bcx: &'a Block<'a>,
         ty.element_type().func_params()
     };
 
-    // Class dtors have no explicit args, so the params should
-    // just consist of the environment (self)
-    assert_eq!(params.len(), 1);
-
-    // Be sure to put all of the fields into a scope so we can use an invoke
-    // instruction to call the user destructor but still call the field
-    // destructors if the user destructor fails.
-    let field_scope = bcx.fcx.push_custom_cleanup_scope();
-
-    let self_arg = PointerCast(bcx, v0, *params.get(0));
-    let args = vec!(self_arg);
-
-    // Add all the fields as a value which needs to be cleaned at the end of
-    // this scope.
-    let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
-    for (i, fld) in field_tys.iter().enumerate() {
-        let llfld_a = adt::trans_field_ptr(bcx, &*repr, v0, 0, i);
-        bcx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope),
-                                  llfld_a,
-                                  fld.mt.ty);
-    }
+    adt::fold_variants(bcx, &*repr, v0, |variant_cx, st, value| {
+        // Be sure to put all of the fields into a scope so we can use an invoke
+        // instruction to call the user destructor but still call the field
+        // destructors if the user destructor fails.
+        let field_scope = variant_cx.fcx.push_custom_cleanup_scope();
+
+        // Class dtors have no explicit args, so the params should
+        // just consist of the environment (self).
+        assert_eq!(params.len(), 1);
+        let self_arg = PointerCast(variant_cx, value, *params.get(0));
+        let args = vec!(self_arg);
+
+        // Add all the fields as a value which needs to be cleaned at the end of
+        // this scope.
+        for (i, ty) in st.fields.iter().enumerate() {
+            let llfld_a = adt::struct_field_ptr(variant_cx, &*st, value, i, false);
+            variant_cx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope),
+                                             llfld_a, *ty);
+        }
 
-    let dtor_ty = ty::mk_ctor_fn(bcx.tcx(), ast::DUMMY_NODE_ID,
-                                 [get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil());
-    let (_, bcx) = invoke(bcx, dtor_addr, args, dtor_ty, None);
+        let dtor_ty = ty::mk_ctor_fn(variant_cx.tcx(), ast::DUMMY_NODE_ID,
+                                     [get_drop_glue_type(bcx.ccx(), t)], ty::mk_nil());
+        let (_, variant_cx) = invoke(variant_cx, dtor_addr, args, dtor_ty, None);
 
-    bcx.fcx.pop_and_trans_custom_cleanup_scope(bcx, field_scope)
+        variant_cx.fcx.pop_and_trans_custom_cleanup_scope(variant_cx, field_scope);
+        variant_cx
+    })
 }
 
 fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<'a> {
@@ -317,7 +317,7 @@ fn make_drop_glue<'a>(bcx: &'a Block<'a>, v0: ValueRef, t: ty::t) -> &'a Block<'
                 }
             }
         }
-        ty::ty_struct(did, ref substs) => {
+        ty::ty_struct(did, ref substs) | ty::ty_enum(did, ref substs) => {
             let tcx = bcx.tcx();
             match ty::ty_dtor(tcx, did) {
                 ty::TraitDtor(dtor, true) => {
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 9b55ee657fc..4dfd15fe136 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -2106,13 +2106,16 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
 
             ty_enum(did, ref substs) => {
                 let variants = substd_enum_variants(cx, did, substs);
-                let res =
+                let mut res =
                     TypeContents::union(variants.as_slice(), |variant| {
                         TypeContents::union(variant.args.as_slice(),
                                             |arg_ty| {
                             tc_ty(cx, *arg_ty, cache)
                         })
                     });
+                if ty::has_dtor(cx, did) {
+                    res = res | TC::OwnsDtor;
+                }
                 apply_lang_items(cx, did, res)
             }
 
@@ -3778,17 +3781,13 @@ pub enum DtorKind {
 }
 
 impl DtorKind {
-    pub fn is_not_present(&self) -> bool {
+    pub fn is_present(&self) -> bool {
         match *self {
-            NoDtor => true,
+            TraitDtor(..) => true,
             _ => false
         }
     }
 
-    pub fn is_present(&self) -> bool {
-        !self.is_not_present()
-    }
-
     pub fn has_drop_flag(&self) -> bool {
         match self {
             &NoDtor => false,
diff --git a/src/test/compile-fail/kindck-destructor-owned.rs b/src/test/compile-fail/kindck-destructor-owned.rs
index fa5f1531637..44fe2607fcc 100644
--- a/src/test/compile-fail/kindck-destructor-owned.rs
+++ b/src/test/compile-fail/kindck-destructor-owned.rs
@@ -17,7 +17,7 @@ struct Foo {
 }
 
 impl Drop for Foo {
-    //~^ ERROR cannot implement a destructor on a structure that does not satisfy Send
+//~^ ERROR cannot implement a destructor on a structure or enumeration that does not satisfy Send
     fn drop(&mut self) {
     }
 }
diff --git a/src/test/run-pass/drop-trait-enum.rs b/src/test/run-pass/drop-trait-enum.rs
new file mode 100644
index 00000000000..977eaa13fc1
--- /dev/null
+++ b/src/test/run-pass/drop-trait-enum.rs
@@ -0,0 +1,94 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(struct_variant)]
+
+use std::task;
+
+#[deriving(PartialEq, Show)]
+enum Message {
+    Dropped,
+    DestructorRan
+}
+
+struct SendOnDrop {
+    sender: Sender<Message>
+}
+
+impl Drop for SendOnDrop {
+    fn drop(&mut self) {
+        self.sender.send(Dropped);
+    }
+}
+
+enum Foo {
+    SimpleVariant(Sender<Message>),
+    NestedVariant(Box<uint>, SendOnDrop, Sender<Message>),
+    FailingVariant { on_drop: SendOnDrop }
+}
+
+impl Drop for Foo {
+    fn drop(&mut self) {
+        match self {
+            &SimpleVariant(ref mut sender) => {
+                sender.send(DestructorRan);
+            }
+            &NestedVariant(_, _, ref mut sender) => {
+                sender.send(DestructorRan);
+            }
+            &FailingVariant { .. } => {
+                fail!("Failed");
+            }
+        }
+    }
+}
+
+pub fn main() {
+    let (sender, receiver) = channel();
+    {
+        let v = SimpleVariant(sender);
+    }
+    assert_eq!(receiver.recv(), DestructorRan);
+    assert_eq!(receiver.recv_opt().ok(), None);
+
+    let (sender, receiver) = channel();
+    {
+        let v = NestedVariant(box 42u, SendOnDrop { sender: sender.clone() }, sender);
+    }
+    assert_eq!(receiver.recv(), DestructorRan);
+    assert_eq!(receiver.recv(), Dropped);
+    assert_eq!(receiver.recv_opt().ok(), None);
+
+    let (sender, receiver) = channel();
+    task::spawn(proc() {
+        let v = FailingVariant { on_drop: SendOnDrop { sender: sender } };
+    });
+    assert_eq!(receiver.recv(), Dropped);
+    assert_eq!(receiver.recv_opt().ok(), None);
+
+    let (sender, receiver) = channel();
+    {
+        task::spawn(proc() {
+            let mut v = NestedVariant(box 42u, SendOnDrop {
+                sender: sender.clone()
+            }, sender.clone());
+            v = NestedVariant(box 42u, SendOnDrop { sender: sender.clone() }, sender.clone());
+            v = SimpleVariant(sender.clone());
+            v = FailingVariant { on_drop: SendOnDrop { sender: sender } };
+        });
+    }
+    assert_eq!(receiver.recv(), DestructorRan);
+    assert_eq!(receiver.recv(), Dropped);
+    assert_eq!(receiver.recv(), DestructorRan);
+    assert_eq!(receiver.recv(), Dropped);
+    assert_eq!(receiver.recv(), DestructorRan);
+    assert_eq!(receiver.recv(), Dropped);
+    assert_eq!(receiver.recv_opt().ok(), None);
+}
diff --git a/src/test/run-pass/drop-uninhabited-enum.rs b/src/test/run-pass/drop-uninhabited-enum.rs
new file mode 100644
index 00000000000..f8c54fbab8a
--- /dev/null
+++ b/src/test/run-pass/drop-uninhabited-enum.rs
@@ -0,0 +1,19 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum Foo { }
+
+impl Drop for Foo {
+    fn drop(&mut self) { }
+}
+
+fn foo(x: Foo) { }
+
+fn main() { }
diff --git a/src/test/run-pass/issue-10802.rs b/src/test/run-pass/issue-10802.rs
index 6c4f0cc7f5f..4fda506ae64 100644
--- a/src/test/run-pass/issue-10802.rs
+++ b/src/test/run-pass/issue-10802.rs
@@ -8,8 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
 struct DroppableStruct;
+enum DroppableEnum {
+    DroppableVariant1, DroppableVariant2
+}
 
 static mut DROPPED: bool = false;
 
@@ -18,9 +20,15 @@ impl Drop for DroppableStruct {
         unsafe { DROPPED = true; }
     }
 }
+impl Drop for DroppableEnum {
+    fn drop(&mut self) {
+        unsafe { DROPPED = true; }
+    }
+}
 
 trait MyTrait { }
 impl MyTrait for Box<DroppableStruct> {}
+impl MyTrait for Box<DroppableEnum> {}
 
 struct Whatever { w: Box<MyTrait> }
 impl  Whatever {
@@ -35,4 +43,10 @@ fn main() {
         let _a = Whatever::new(box f as Box<MyTrait>);
     }
     assert!(unsafe { DROPPED });
+    unsafe { DROPPED = false; }
+    {
+        let f = box DroppableVariant1;
+        let _a = Whatever::new(box f as Box<MyTrait>);
+    }
+    assert!(unsafe { DROPPED });
 }
diff --git a/src/test/run-pass/issue-6892.rs b/src/test/run-pass/issue-6892.rs
index 891892ff9cb..462a78a5368 100644
--- a/src/test/run-pass/issue-6892.rs
+++ b/src/test/run-pass/issue-6892.rs
@@ -14,6 +14,7 @@
 struct Foo;
 struct Bar { x: int }
 struct Baz(int);
+enum FooBar { _Foo(Foo), _Bar(uint) }
 
 static mut NUM_DROPS: uint = 0;
 
@@ -32,6 +33,11 @@ impl Drop for Baz {
         unsafe { NUM_DROPS += 1; }
     }
 }
+impl Drop for FooBar {
+    fn drop(&mut self) {
+        unsafe { NUM_DROPS += 1; }
+    }
+}
 
 fn main() {
     assert_eq!(unsafe { NUM_DROPS }, 0);
@@ -41,12 +47,19 @@ fn main() {
     assert_eq!(unsafe { NUM_DROPS }, 2);
     { let _x = Baz(21); }
     assert_eq!(unsafe { NUM_DROPS }, 3);
+    { let _x = _Foo(Foo); }
+    assert_eq!(unsafe { NUM_DROPS }, 5);
+    { let _x = _Bar(42u); }
+    assert_eq!(unsafe { NUM_DROPS }, 6);
 
-    assert_eq!(unsafe { NUM_DROPS }, 3);
     { let _ = Foo; }
-    assert_eq!(unsafe { NUM_DROPS }, 4);
+    assert_eq!(unsafe { NUM_DROPS }, 7);
     { let _ = Bar { x: 21 }; }
-    assert_eq!(unsafe { NUM_DROPS }, 5);
+    assert_eq!(unsafe { NUM_DROPS }, 8);
     { let _ = Baz(21); }
-    assert_eq!(unsafe { NUM_DROPS }, 6);
+    assert_eq!(unsafe { NUM_DROPS }, 9);
+    { let _ = _Foo(Foo); }
+    assert_eq!(unsafe { NUM_DROPS }, 11);
+    { let _ = _Bar(42u); }
+    assert_eq!(unsafe { NUM_DROPS }, 12);
 }