Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Drop support for enums #15272

Merged
merged 1 commit into from Jul 23, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/librustc/middle/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down
144 changes: 110 additions & 34 deletions src/librustc/middle/trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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>
}

/**
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)))
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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() }
}
Expand All @@ -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, .. } |
Expand All @@ -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())
Expand All @@ -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")
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(""));

Expand Down Expand Up @@ -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);
Expand Down
Loading