From 59edfdd2ab3bad73086940afe1a57fa4706a4d2f Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Sat, 14 Jun 2014 15:55:55 +0200 Subject: [PATCH] Add Drop support for enums Fixes #13041. --- src/librustc/middle/kind.rs | 23 ++- src/librustc/middle/trans/adt.rs | 146 +++++++++++++----- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/debuginfo.rs | 4 +- src/librustc/middle/trans/glue.rs | 54 +++---- src/librustc/middle/ty.rs | 13 +- .../compile-fail/kindck-destructor-owned.rs | 2 +- src/test/run-pass/drop-trait-enum.rs | 94 +++++++++++ src/test/run-pass/drop-uninhabited-enum.rs | 19 +++ src/test/run-pass/issue-10802.rs | 16 +- src/test/run-pass/issue-6892.rs | 25 ++- 11 files changed, 313 insertions(+), 85 deletions(-) create mode 100644 src/test/run-pass/drop-trait-enum.rs create mode 100644 src/test/run-pass/drop-uninhabited-enum.rs 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), + General(IntType, Vec, 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, + pub fields: Vec } /** @@ -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 = 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::>(); 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::>(); 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]) } -/// Access the struct drop flag, if present. -pub fn trans_drop_flag_ptr(bcx: &Block, r: &Repr, val: ValueRef) -> ValueRef { +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, true) => GEPi(bcx, val, [0, st.fields.len() - 1]), + 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::>(); + 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<'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) => { + 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 { 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); + 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(); - // 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(); + // 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); - 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. + 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); + } - // Add all the fields as a value which needs to be cleaned at the end of - // this scope. - let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs); - for (i, fld) in field_tys.iter().enumerate() { - let llfld_a = adt::trans_field_ptr(bcx, &*repr, v0, 0, i); - bcx.fcx.schedule_drop_mem(cleanup::CustomScope(field_scope), - llfld_a, - fld.mt.ty); - } + let 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); - 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); - - 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 or the MIT license +// , 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 +} + +impl Drop for SendOnDrop { + fn drop(&mut self) { + self.sender.send(Dropped); + } +} + +enum Foo { + SimpleVariant(Sender), + NestedVariant(Box, SendOnDrop, Sender), + 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 or the MIT license +// , 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 {} +impl MyTrait for Box {} struct Whatever { w: Box } impl Whatever { @@ -35,4 +43,10 @@ fn main() { let _a = Whatever::new(box f as Box); } assert!(unsafe { DROPPED }); + unsafe { DROPPED = false; } + { + let f = box DroppableVariant1; + let _a = Whatever::new(box f as Box); + } + 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); - - assert_eq!(unsafe { NUM_DROPS }, 3); - { let _ = Foo; } - assert_eq!(unsafe { NUM_DROPS }, 4); - { let _ = Bar { x: 21 }; } + { let _x = _Foo(Foo); } assert_eq!(unsafe { NUM_DROPS }, 5); - { let _ = Baz(21); } + { let _x = _Bar(42u); } assert_eq!(unsafe { NUM_DROPS }, 6); + + { let _ = Foo; } + assert_eq!(unsafe { NUM_DROPS }, 7); + { let _ = Bar { x: 21 }; } + assert_eq!(unsafe { NUM_DROPS }, 8); + { let _ = Baz(21); } + assert_eq!(unsafe { NUM_DROPS }, 9); + { let _ = _Foo(Foo); } + assert_eq!(unsafe { NUM_DROPS }, 11); + { let _ = _Bar(42u); } + assert_eq!(unsafe { NUM_DROPS }, 12); }