diff --git a/src/libcore/core.rs b/src/libcore/core.rs index 12a426261c49b..1be217dac9f9f 100644 --- a/src/libcore/core.rs +++ b/src/libcore/core.rs @@ -28,6 +28,8 @@ pub use to_str::ToStr; #[cfg(notest)] pub use ops::{Const, Copy, Send, Owned}; #[cfg(notest)] +pub use ops::{Drop}; +#[cfg(notest)] pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, BitAnd, BitOr, BitXor}; #[cfg(notest)] pub use ops::{Shl, Shr, Index}; @@ -38,6 +40,8 @@ extern mod coreops(name = "core", vers = "0.5"); #[cfg(test)] pub use coreops::ops::{Const, Copy, Send, Owned}; #[cfg(test)] +pub use coreops::ops::{Drop}; +#[cfg(test)] pub use coreops::ops::{Add, Sub, Mul, Div, Modulo, Neg, BitAnd, BitOr}; #[cfg(test)] pub use coreops::ops::{BitXor}; diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 7c6bcf5bd51fb..96b96d0f27ad1 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -23,6 +23,11 @@ pub trait Owned { // Empty. } +#[lang="drop"] +pub trait Drop { + fn finalize(); // XXX: Rename to "drop"? --pcwalton +} + #[lang="add"] pub trait Add { pure fn add(rhs: &RHS) -> Result; diff --git a/src/rustc/middle/lang_items.rs b/src/rustc/middle/lang_items.rs index 383fe2db3231c..b1d68bee2598f 100644 --- a/src/rustc/middle/lang_items.rs +++ b/src/rustc/middle/lang_items.rs @@ -28,6 +28,8 @@ struct LanguageItems { mut send_trait: Option, mut owned_trait: Option, + mut drop_trait: Option, + mut add_trait: Option, mut sub_trait: Option, mut mul_trait: Option, @@ -59,6 +61,8 @@ mod language_items { send_trait: None, owned_trait: None, + drop_trait: None, + add_trait: None, sub_trait: None, mul_trait: None, @@ -94,6 +98,8 @@ fn LanguageItemCollector(crate: @crate, session: Session, item_refs.insert(~"send", &mut items.send_trait); item_refs.insert(~"owned", &mut items.owned_trait); + item_refs.insert(~"drop", &mut items.drop_trait); + item_refs.insert(~"add", &mut items.add_trait); item_refs.insert(~"sub", &mut items.sub_trait); item_refs.insert(~"mul", &mut items.mul_trait); diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 69b4d985f6f6a..9d3e72898ea1b 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -406,7 +406,16 @@ type ctxt = // A mapping from the def ID of an impl to the IDs of the derived // methods within it. automatically_derived_methods_for_impl: - HashMap + HashMap, + + // A mapping from the def ID of an enum or struct type to the def ID + // of the method that implements its destructor. If the type is not + // present in this map, it does not have a destructor. This map is + // populated during the coherence phase of typechecking. + destructor_for_type: HashMap, + + // A method will be in this list if and only if it is a destructor. + destructors: HashMap }; enum tbox_flag { @@ -977,7 +986,9 @@ fn mk_ctxt(s: session::Session, deriving_struct_methods: HashMap(), deriving_enum_methods: HashMap(), automatically_derived_methods: HashMap(), - automatically_derived_methods_for_impl: HashMap()} + automatically_derived_methods_for_impl: HashMap(), + destructor_for_type: HashMap(), + destructors: HashMap()} } @@ -3627,6 +3638,11 @@ fn item_path_str(cx: ctxt, id: ast::def_id) -> ~str { /* 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 { + match cx.destructor_for_type.find(class_id) { + Some(method_def_id) => return Some(method_def_id), + None => {} // Continue. + } + if is_local(class_id) { match cx.items.find(class_id.node) { Some(ast_map::node_item(@{ diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index f1185181f554a..e314bde38a177 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -774,6 +774,7 @@ impl LookupContext { let fty = self.fn_ty_from_origin(&candidate.origin); self.enforce_trait_instance_limitations(fty, candidate); + self.enforce_drop_trait_limitations(candidate); // before we only checked whether self_ty could be a subtype // of rcvr_ty; now we actually make it so (this may cause @@ -858,6 +859,25 @@ impl LookupContext { } } + fn enforce_drop_trait_limitations(&self, candidate: &Candidate) { + // No code can call the finalize method explicitly. + let bad; + match candidate.origin { + method_static(method_id) | method_self(method_id, _) => { + bad = self.tcx().destructors.contains_key(method_id); + } + method_param({trait_id: trait_id, _}) | + method_trait(trait_id, _, _) => { + bad = self.tcx().destructor_for_type.contains_key(trait_id); + } + } + + if bad { + self.tcx().sess.span_err(self.expr.span, + ~"explicit call to destructor"); + } + } + fn is_relevant(&self, self_ty: ty::t, candidate: &Candidate) -> bool { debug!("is_relevant(self_ty=%s, candidate=%s)", self.ty_to_str(self_ty), self.cand_to_str(candidate)); diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs index d0ec2b3cfc554..26c559fd135c2 100644 --- a/src/rustc/middle/typeck/coherence.rs +++ b/src/rustc/middle/typeck/coherence.rs @@ -228,6 +228,11 @@ impl CoherenceChecker { // coherence checks, because we ensure by construction that no errors // can happen at link time. self.add_external_crates(); + + // Populate the table of destructors. It might seem a bit strange to + // do this here, but it's actually the most convenient place, since + // the coherence tables contain the trait -> type mappings. + self.populate_destructor_table(); } fn check_implementation(item: @item, associated_traits: ~[@trait_ref]) { @@ -913,6 +918,58 @@ impl CoherenceChecker { } } } + + // + // Destructors + // + + fn populate_destructor_table() { + let coherence_info = &self.crate_context.coherence_info; + let tcx = self.crate_context.tcx; + let drop_trait = tcx.lang_items.drop_trait.get(); + let impls_opt = coherence_info.extension_methods.find(drop_trait); + + let impls; + match impls_opt { + None => return, // No types with (new-style) destructors present. + Some(found_impls) => impls = found_impls + } + + for impls.each |impl_info| { + if impl_info.methods.len() < 1 { + // We'll error out later. For now, just don't ICE. + loop; + } + let method_def_id = impl_info.methods[0].did; + + let self_type = self.get_self_type_for_implementation(*impl_info); + match ty::get(self_type.ty).sty { + ty::ty_class(type_def_id, _) => { + tcx.destructor_for_type.insert(type_def_id, method_def_id); + tcx.destructors.insert(method_def_id, ()); + } + _ => { + // Destructors only work on nominal types. + if impl_info.did.crate == ast::local_crate { + match tcx.items.find(impl_info.did.node) { + Some(ast_map::node_item(@item, _)) => { + tcx.sess.span_err(item.span, + ~"the Drop trait may only \ + be implemented on \ + structures"); + } + _ => { + tcx.sess.bug(~"didn't find impl in ast map"); + } + } + } else { + tcx.sess.bug(~"found external impl of Drop trait on \ + something other than a struct"); + } + } + } + } + } } fn check_coherence(crate_context: @crate_ctxt, crate: @crate) { diff --git a/src/test/compile-fail/drop-on-non-struct.rs b/src/test/compile-fail/drop-on-non-struct.rs new file mode 100644 index 0000000000000..8ee72d9d2fdcc --- /dev/null +++ b/src/test/compile-fail/drop-on-non-struct.rs @@ -0,0 +1,12 @@ +type Foo = @[u8]; + +impl Foo : Drop { //~ ERROR the Drop trait may only be implemented + fn finalize() { + io::println("kaboom"); + } +} + +fn main() { +} + + diff --git a/src/test/compile-fail/explicit-call-to-dtor.rs b/src/test/compile-fail/explicit-call-to-dtor.rs new file mode 100644 index 0000000000000..56af671852b58 --- /dev/null +++ b/src/test/compile-fail/explicit-call-to-dtor.rs @@ -0,0 +1,15 @@ +struct Foo { + x: int +} + +impl Foo : Drop { + fn finalize() { + io::println("kaboom"); + } +} + +fn main() { + let x = Foo { x: 3 }; + x.finalize(); //~ ERROR explicit call to destructor +} + diff --git a/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs b/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs new file mode 100644 index 0000000000000..c7c2748235acc --- /dev/null +++ b/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs @@ -0,0 +1,25 @@ +struct Foo { + x: int +} + +trait Bar : Drop { + fn blah(); +} + +impl Foo : Drop { + fn finalize() { + io::println("kaboom"); + } +} + +impl Foo : Bar { + fn blah() { + self.finalize(); //~ ERROR explicit call to destructor + } +} + +fn main() { + let x = Foo { x: 3 }; +} + + diff --git a/src/test/run-pass/drop-trait.rs b/src/test/run-pass/drop-trait.rs new file mode 100644 index 0000000000000..3a379a4c9dc4f --- /dev/null +++ b/src/test/run-pass/drop-trait.rs @@ -0,0 +1,14 @@ +struct Foo { + x: int +} + +impl Foo : Drop { + fn finalize() { + io::println("bye"); + } +} + +fn main() { + let x: Foo = Foo { x: 3 }; +} +