Generate drop glue correctly for classes with destructors

This commit is contained in:
Tim Chevalier
2012-05-15 17:59:55 -07:00
parent 5d625af9f9
commit fa5cc5bcd0
11 changed files with 171 additions and 30 deletions

View File

@@ -363,8 +363,11 @@ fn operator_prec(op: ast::binop) -> uint {
}
fn dtor_dec() -> fn_decl {
{inputs: [], output: @{id: 0, node: ty_nil, span: dummy_sp()},
purity: impure_fn, cf: return_val, constraints: []}
let nil_t = @{id: 0, node: ty_nil, span: dummy_sp()};
// dtor has one argument, of type ()
{inputs: [{mode: ast::expl(ast::by_ref),
ty: nil_t, ident: "_", id: 0}],
output: nil_t, purity: impure_fn, cf: return_val, constraints: []}
}
// Local Variables:

View File

@@ -95,6 +95,7 @@ const tag_mod_impl_iface: uint = 0x47u;
different tags.
*/
const tag_item_impl_method: uint = 0x48u;
const tag_item_dtor: uint = 0x49u;
// used to encode crate_ctxt side tables
enum astencode_tag { // Reserves 0x50 -- 0x6f

View File

@@ -10,6 +10,7 @@ import driver::session::expect;
import common::*;
import std::map::hashmap;
export class_dtor;
export get_symbol;
export get_class_fields;
export get_class_method;
@@ -185,6 +186,12 @@ fn get_class_method(cstore: cstore::cstore, def: ast::def_id, mname: str)
decoder::get_class_method(cdata, def.node, mname)
}
/* If def names a class with a dtor, return it. Otherwise, return none. */
fn class_dtor(cstore: cstore::cstore, def: ast::def_id)
-> option<ast::def_id> {
let cdata = cstore::get_crate_data(cstore, def.crate);
decoder::class_dtor(cdata, def.node)
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@@ -16,6 +16,7 @@ import util::ppaux::ty_to_str;
import ebml::deserializer;
import syntax::diagnostic::span_handler;
export class_dtor;
export get_class_fields;
export get_symbol;
export get_enum_variants;
@@ -331,6 +332,19 @@ fn get_class_method(cdata: cmd, id: ast::node_id, name: str) -> ast::def_id {
}
}
fn class_dtor(cdata: cmd, id: ast::node_id) -> option<ast::def_id> {
let items = ebml::get_doc(ebml::doc(cdata.data), tag_items);
let cls_items = alt maybe_find_item(id, items) {
some(it) { it }
none { ret none; }};
let mut rslt = none;
ebml::tagged_docs(cls_items, tag_item_dtor) {|f|
let did = parse_def_id(ebml::doc_data(f));
rslt = some(translate_def_id(cdata, did));
}
rslt
}
fn get_symbol(data: @[u8], id: ast::node_id) -> str {
ret item_symbol(lookup_item(id, data));
}

View File

@@ -201,8 +201,7 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt,
encode_def_id(ebml_w, local_def(it.id));
ebml_w.end_tag();
}
// FIXME: I don't *think* dtor needs to be serialized?
item_class(_, _, items, ctor, _dtor, _) {
item_class(_, _, items, ctor, m_dtor, _) {
add_to_index(ebml_w, path, index, it.ident);
ebml_w.start_tag(tag_paths_data_item);
encode_name(ebml_w, it.ident);
@@ -212,6 +211,12 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt,
add_to_index(ebml_w, path, index, it.ident);
#debug("ctor id: %d", ctor.node.id);
encode_named_def_id(ebml_w, it.ident, local_def(ctor.node.id));
/* Encode id for dtor */
option::iter(m_dtor) {|dtor|
ebml_w.start_tag(tag_item_dtor);
encode_def_id(ebml_w, local_def(dtor.node.id));
ebml_w.end_tag();
};
encode_class_item_paths(ebml_w, items, path + [it.ident],
index);
ebml_w.end_tag();

View File

@@ -302,7 +302,6 @@ fn node_id_to_str(map: map, id: node_id) -> str {
}
}
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@@ -717,6 +717,48 @@ fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
ty::ty_res(did, inner, substs) {
trans_res_drop(bcx, v0, did, inner, substs.tps)
}
ty::ty_class(did, substs) {
let tcx = bcx.tcx();
alt ty::ty_dtor(tcx, did) {
some(dtor) {
let drop_flag = GEPi(bcx, v0, [0u, 0u]);
with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) {|cx|
let mut bcx = cx;
// we have to cast v0
let classptr = GEPi(bcx, v0, [0u, 1u]);
// Find and call the actual destructor
let dtor_addr = get_res_dtor(bcx.ccx(), dtor, substs.tps);
// The second argument is the "self" argument for drop
let params = lib::llvm::fn_ty_param_tys
(llvm::LLVMGetElementType
(llvm::LLVMTypeOf(dtor_addr)));
let self_arg = PointerCast(bcx, v0, params[1u]);
let args = [bcx.fcx.llretptr, self_arg];
let val_llty = lib::llvm::fn_ty_param_tys
(llvm::LLVMGetElementType
(llvm::LLVMTypeOf(dtor_addr)))[args.len()];
let val_cast = BitCast(bcx, classptr, val_llty);
#debug("fn_ty: %s", ty_str(bcx.ccx().tn,
(llvm::LLVMGetElementType
(llvm::LLVMTypeOf(dtor_addr)))));
#debug("self's ty: %s", val_str(bcx.ccx().tn, v0));
Call(bcx, dtor_addr, args + [val_cast]);
// Drop the fields
for vec::eachi(ty::class_items_as_fields(tcx, did, substs))
{|i, fld|
let llfld_a = GEPi(bcx, classptr, [0u, i]);
bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
}
Store(bcx, C_u8(0u), drop_flag);
bcx
}
}
none {
// No dtor? Just the default case
iter_structural_ty(bcx, v0, t, drop_ty)
}
}
}
ty::ty_fn(_) {
closure::make_fn_glue(bcx, v0, t, drop_ty)
}
@@ -1015,11 +1057,12 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
ret next_cx;
}
ty::ty_class(did, substs) {
// a class is like a record type
for vec::eachi(ty::class_items_as_fields(cx.tcx(), did, substs))
assert(ty::ty_dtor(cx.tcx(), did) == none);
// a class w/ no dtor is like a record type
for vec::eachi(ty::class_items_as_fields(cx.tcx(), did, substs))
{|i, fld|
let llfld_a = GEPi(cx, av, [0u, i]);
cx = f(cx, llfld_a, fld.mt.ty);
let llfld_a = GEPi(cx, av, [0u, i]);
cx = f(cx, llfld_a, fld.mt.ty);
}
}
_ { cx.sess().unimpl("type in iter_structural_ty"); }
@@ -2340,16 +2383,24 @@ fn trans_rec_field(bcx: block, base: @ast::expr,
fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t,
field: ast::ident, sp: span) -> lval_result {
let mut is_class_with_dtor = false;
let fields = alt ty::get(ty).struct {
ty::ty_rec(fs) { fs }
ty::ty_class(did, substs) {
ty::class_items_as_fields(bcx.tcx(), did, substs) }
// Constraint?
_ { bcx.tcx().sess.span_bug(sp, "trans_rec_field:\
ty::ty_rec(fs) { fs }
ty::ty_class(did, substs) {
if option::is_some(ty::ty_dtor(bcx.tcx(), did)) {
is_class_with_dtor = true;
}
ty::class_items_as_fields(bcx.tcx(), did, substs)
}
// Constraint?
_ { bcx.tcx().sess.span_bug(sp, "trans_rec_field:\
base expr has non-record type"); }
};
};
let ix = field_idx_strict(bcx.tcx(), sp, field, fields);
let val = GEPi(bcx, val, [0u, ix]);
let val = GEPi(bcx, if is_class_with_dtor {
GEPi(bcx, val, [0u, 1u])
}
else { val }, [0u, ix]);
ret {bcx: bcx, val: val, kind: owned};
}
@@ -4596,6 +4647,19 @@ fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
// So we initialize it here
let selfptr = alloc_ty(bcx_top, rslt_ty);
// If we have a dtor, we have a two-word representation with a drop
// flag, then a pointer to the class itself
let valptr = if option::is_some(ty::ty_dtor(bcx_top.tcx(),
parent_id)) {
// Initialize the drop flag
let one = C_u8(1u);
let flag = GEPi(bcx_top, selfptr, [0u, 0u]);
Store(bcx_top, one, flag);
// Select the pointer to the class itself
GEPi(bcx_top, selfptr, [0u, 1u])
}
else { selfptr };
// initialize fields to zero
let fields = ty::class_items_as_fields(bcx_top.tcx(), parent_id,
dummy_substs(psubsts.tys));
@@ -4604,7 +4668,7 @@ fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
// drop their LHS
for fields.each {|field|
let ix = field_idx_strict(bcx.tcx(), sp, field.ident, fields);
bcx = zero_alloca(bcx, GEPi(bcx, selfptr, [0u, ix]),
bcx = zero_alloca(bcx, GEPi(bcx, valptr, [0u, ix]),
field.mt.ty);
}
@@ -4626,7 +4690,7 @@ fn trans_class_dtor(ccx: @crate_ctxt, path: path,
body: ast::blk, lldtor_decl: ValueRef,
dtor_id: ast::node_id,
parent_id: ast::def_id) {
let class_ty = ty::lookup_item_type(ccx.tcx, parent_id).ty;
let class_ty = ty::lookup_item_type(ccx.tcx, parent_id).ty;
trans_fn(ccx, path, ast_util::dtor_dec(),
body, lldtor_decl, impl_self(class_ty), none, dtor_id);
}

View File

@@ -414,10 +414,25 @@ fn shape_of(ccx: @crate_ctxt, t: ty::t, ty_param_map: [uint]) -> [u8] {
s
}
ty::ty_iface(_, _) { [shape_box_fn] }
ty::ty_class(did, ts) {
// same as records
let mut s = [shape_struct], sub = [];
for ty::class_items_as_fields(ccx.tcx, did, ts).each {|f|
ty::ty_class(did, substs) {
// same as records, unless there's a dtor
let tps = substs.tps;
let m_dtor_did = ty::ty_dtor(ccx.tcx, did);
let mut s = if option::is_some(m_dtor_did) {
[shape_res]
}
else { [shape_struct] };
let mut sub = [];
option::iter(m_dtor_did) {|dtor_did|
let ri = {did: dtor_did, tps: tps};
let id = interner::intern(ccx.shape_cx.resources, ri);
add_u16(s, id as u16);
add_u16(s, vec::len(tps) as u16);
for vec::each(tps) {|tp|
add_substr(s, shape_of(ccx, tp, ty_param_map));
}
};
for ty::class_items_as_fields(ccx.tcx, did, substs).each {|f|
sub += shape_of(ccx, f.mt.ty, ty_param_map);
}
add_substr(s, sub);
@@ -571,14 +586,11 @@ fn gen_enum_shapes(ccx: @crate_ctxt) -> ValueRef {
fn gen_resource_shapes(ccx: @crate_ctxt) -> ValueRef {
let mut dtors = [];
let mut i = 0u;
let len = interner::len(ccx.shape_cx.resources);
while i < len {
let ri = interner::get(ccx.shape_cx.resources, i);
dtors += [trans::base::get_res_dtor(ccx, ri.did, ri.tps)];
i += 1u;
uint::range(0u, len) {|i|
let ri = interner::get(ccx.shape_cx.resources, i);
dtors += [trans::base::get_res_dtor(ccx, ri.did, ri.tps)];
}
ret mk_global(ccx, "resource_shapes", C_struct(dtors), true);
}

View File

@@ -147,7 +147,13 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
let t = ty::lookup_field_type(cx.tcx, did, f.id, ts);
type_of(cx, t)
};
T_struct(tys)
if ty::ty_dtor(cx.tcx, did) == none {
T_struct(tys)
}
else {
// resource type
T_struct([T_i8(), T_struct(tys)])
}
}
ty::ty_self { cx.tcx.sess.unimpl("type_of: ty_self"); }
ty::ty_var(_) { cx.tcx.sess.bug("type_of shouldn't see a ty_var"); }

View File

@@ -6,7 +6,7 @@ import session::session;
import syntax::ast;
import syntax::ast::*;
import syntax::ast_util;
import syntax::ast_util::{is_local, split_class_items};
import syntax::ast_util::{is_local, local_def, split_class_items};
import syntax::codemap::span;
import metadata::csearch;
import util::common::*;
@@ -69,6 +69,7 @@ export new_ty_hash;
export enum_variants, substd_enum_variants;
export iface_methods, store_iface_methods, impl_iface;
export enum_variant_with_id;
export ty_dtor;
export ty_param_bounds_and_ty;
export ty_bool, mk_bool, type_is_bool;
export ty_bot, mk_bot, type_is_bot;
@@ -2377,6 +2378,22 @@ fn item_path_str(cx: ctxt, id: ast::def_id) -> str {
ast_map::path_to_str(item_path(cx, id))
}
/* If class_id names a class with a dtor, return some(the dtor's id).
Otherwise return none. */
fn ty_dtor(cx: ctxt, class_id: def_id) -> option<def_id> {
if is_local(class_id) {
alt cx.items.find(class_id.node) {
some(ast_map::node_item(@{node: ast::item_class(_, _, _, _,
some(dtor), _), _}, _))
{ some(local_def(dtor.node.id)) }
_ { none }
}
}
else {
csearch::class_dtor(cx.sess.cstore, class_id)
}
}
fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path {
if id.crate != ast::local_crate {
csearch::get_item_path(cx, id)

View File

@@ -0,0 +1,13 @@
class shrinky_pointer {
let i: @@mut int;
fn look_at() -> int { ret **(self.i); }
new(i: @@mut int) { self.i = i; }
drop { log(error, "Hello!"); **(self.i) -= 1; }
}
fn main() {
let my_total = @@mut 10;
{ let pt <- shrinky_pointer(my_total); assert (pt.look_at() == 10); }
log(error, #fmt("my_total = %d", **my_total));
assert (**my_total == 9);
}