diff --git a/src/librustsyntax/ast_util.rs b/src/librustsyntax/ast_util.rs index eb442e1ec485..06821366f1cd 100644 --- a/src/librustsyntax/ast_util.rs +++ b/src/librustsyntax/ast_util.rs @@ -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: diff --git a/src/rustc/metadata/common.rs b/src/rustc/metadata/common.rs index 42331973baae..35373bffd846 100644 --- a/src/rustc/metadata/common.rs +++ b/src/rustc/metadata/common.rs @@ -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 diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index 333ad2806c40..5ebdc144ae7f 100644 --- a/src/rustc/metadata/csearch.rs +++ b/src/rustc/metadata/csearch.rs @@ -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 { + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::class_dtor(cdata, def.node) +} // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 1c8caa0f4aac..832dda817e3e 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -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 { + 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)); } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index c4f3225ebfc7..4638d8ea4e56 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -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(); diff --git a/src/rustc/middle/ast_map.rs b/src/rustc/middle/ast_map.rs index ef5851c3faf5..71ce361c626d 100644 --- a/src/rustc/middle/ast_map.rs +++ b/src/rustc/middle/ast_map.rs @@ -302,7 +302,6 @@ fn node_id_to_str(map: map, id: node_id) -> str { } } } - // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 43dfda534844..dcecd3f2251e 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -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); } diff --git a/src/rustc/middle/trans/shape.rs b/src/rustc/middle/trans/shape.rs index 14215efb199c..73897fd31756 100644 --- a/src/rustc/middle/trans/shape.rs +++ b/src/rustc/middle/trans/shape.rs @@ -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); } diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs index 16646472de87..b094a576cb1d 100644 --- a/src/rustc/middle/trans/type_of.rs +++ b/src/rustc/middle/trans/type_of.rs @@ -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"); } diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 09a1ef7c4c35..2d12870d25b4 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -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 { + 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) diff --git a/src/test/run-pass/resource-destruct-with-class.rs b/src/test/run-pass/resource-destruct-with-class.rs new file mode 100644 index 000000000000..0ae85a5af0b7 --- /dev/null +++ b/src/test/run-pass/resource-destruct-with-class.rs @@ -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); +}