rustc: Implement the Drop trait. r=brson
This commit is contained in:
parent
70886d314d
commit
2904095570
11 changed files with 200 additions and 23 deletions
|
@ -751,25 +751,6 @@ match mypoint {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Structs are the only type in Rust that may have user-defined
|
|
||||||
destructors, defined with `drop` blocks. Inside a `drop`, the name
|
|
||||||
`self` refers to the struct's value.
|
|
||||||
|
|
||||||
~~~
|
|
||||||
struct TimeBomb {
|
|
||||||
explosivity: uint,
|
|
||||||
|
|
||||||
drop {
|
|
||||||
for iter::repeat(self.explosivity) {
|
|
||||||
io::println(fmt!("blam!"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
~~~
|
|
||||||
|
|
||||||
> ***Note***: This destructor syntax is temporary. Eventually destructors
|
|
||||||
> will be defined for any type using [traits](#traits).
|
|
||||||
|
|
||||||
## Enums
|
## Enums
|
||||||
|
|
||||||
Enums are datatypes that have several alternate representations. For
|
Enums are datatypes that have several alternate representations. For
|
||||||
|
@ -1909,8 +1890,8 @@ traits are automatically derived and implemented for all applicable
|
||||||
types by the compiler, and may not be overridden:
|
types by the compiler, and may not be overridden:
|
||||||
|
|
||||||
* `Copy` - Types that can be copied: either implicitly, or explicitly with the
|
* `Copy` - Types that can be copied: either implicitly, or explicitly with the
|
||||||
`copy` operator. All types are copyable unless they are classes
|
`copy` operator. All types are copyable unless they have destructors or
|
||||||
with destructors or otherwise contain classes with destructors.
|
contain types with destructors.
|
||||||
|
|
||||||
* `Send` - Sendable (owned) types. All types are sendable unless they
|
* `Send` - Sendable (owned) types. All types are sendable unless they
|
||||||
contain managed boxes, managed closures, or otherwise managed
|
contain managed boxes, managed closures, or otherwise managed
|
||||||
|
@ -1922,6 +1903,28 @@ types by the compiler, and may not be overridden:
|
||||||
> ***Note:*** These three traits were referred to as 'kinds' in earlier
|
> ***Note:*** These three traits were referred to as 'kinds' in earlier
|
||||||
> iterations of the language, and often still are.
|
> iterations of the language, and often still are.
|
||||||
|
|
||||||
|
There is also a special trait known as `Drop`. This trait defines one method
|
||||||
|
called `finalize`, which is automatically called when value of the a type that
|
||||||
|
implements this trait is destroyed, either because the value went out of scope
|
||||||
|
or because the garbage collector reclaimed it.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
struct TimeBomb {
|
||||||
|
explosivity: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeBomb : Drop {
|
||||||
|
fn finalize() {
|
||||||
|
for iter::repeat(self.explosivity) {
|
||||||
|
io::println("blam!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
It is illegal to call `finalize` directly. Only code inserted by the compiler
|
||||||
|
may call it.
|
||||||
|
|
||||||
## Declaring and implementing traits
|
## Declaring and implementing traits
|
||||||
|
|
||||||
A trait consists of a set of methods, without bodies, or may be empty,
|
A trait consists of a set of methods, without bodies, or may be empty,
|
||||||
|
|
|
@ -28,6 +28,8 @@ pub use to_str::ToStr;
|
||||||
#[cfg(notest)]
|
#[cfg(notest)]
|
||||||
pub use ops::{Const, Copy, Send, Owned};
|
pub use ops::{Const, Copy, Send, Owned};
|
||||||
#[cfg(notest)]
|
#[cfg(notest)]
|
||||||
|
pub use ops::{Drop};
|
||||||
|
#[cfg(notest)]
|
||||||
pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, BitAnd, BitOr, BitXor};
|
pub use ops::{Add, Sub, Mul, Div, Modulo, Neg, BitAnd, BitOr, BitXor};
|
||||||
#[cfg(notest)]
|
#[cfg(notest)]
|
||||||
pub use ops::{Shl, Shr, Index};
|
pub use ops::{Shl, Shr, Index};
|
||||||
|
@ -38,6 +40,8 @@ extern mod coreops(name = "core", vers = "0.5");
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use coreops::ops::{Const, Copy, Send, Owned};
|
pub use coreops::ops::{Const, Copy, Send, Owned};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
pub use coreops::ops::{Drop};
|
||||||
|
#[cfg(test)]
|
||||||
pub use coreops::ops::{Add, Sub, Mul, Div, Modulo, Neg, BitAnd, BitOr};
|
pub use coreops::ops::{Add, Sub, Mul, Div, Modulo, Neg, BitAnd, BitOr};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use coreops::ops::{BitXor};
|
pub use coreops::ops::{BitXor};
|
||||||
|
|
|
@ -23,6 +23,11 @@ pub trait Owned {
|
||||||
// Empty.
|
// Empty.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[lang="drop"]
|
||||||
|
pub trait Drop {
|
||||||
|
fn finalize(); // XXX: Rename to "drop"? --pcwalton
|
||||||
|
}
|
||||||
|
|
||||||
#[lang="add"]
|
#[lang="add"]
|
||||||
pub trait Add<RHS,Result> {
|
pub trait Add<RHS,Result> {
|
||||||
pure fn add(rhs: &RHS) -> Result;
|
pure fn add(rhs: &RHS) -> Result;
|
||||||
|
|
|
@ -28,6 +28,8 @@ struct LanguageItems {
|
||||||
mut send_trait: Option<def_id>,
|
mut send_trait: Option<def_id>,
|
||||||
mut owned_trait: Option<def_id>,
|
mut owned_trait: Option<def_id>,
|
||||||
|
|
||||||
|
mut drop_trait: Option<def_id>,
|
||||||
|
|
||||||
mut add_trait: Option<def_id>,
|
mut add_trait: Option<def_id>,
|
||||||
mut sub_trait: Option<def_id>,
|
mut sub_trait: Option<def_id>,
|
||||||
mut mul_trait: Option<def_id>,
|
mut mul_trait: Option<def_id>,
|
||||||
|
@ -59,6 +61,8 @@ mod language_items {
|
||||||
send_trait: None,
|
send_trait: None,
|
||||||
owned_trait: None,
|
owned_trait: None,
|
||||||
|
|
||||||
|
drop_trait: None,
|
||||||
|
|
||||||
add_trait: None,
|
add_trait: None,
|
||||||
sub_trait: None,
|
sub_trait: None,
|
||||||
mul_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(~"send", &mut items.send_trait);
|
||||||
item_refs.insert(~"owned", &mut items.owned_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(~"add", &mut items.add_trait);
|
||||||
item_refs.insert(~"sub", &mut items.sub_trait);
|
item_refs.insert(~"sub", &mut items.sub_trait);
|
||||||
item_refs.insert(~"mul", &mut items.mul_trait);
|
item_refs.insert(~"mul", &mut items.mul_trait);
|
||||||
|
|
|
@ -404,7 +404,16 @@ type ctxt =
|
||||||
// A mapping from the def ID of an impl to the IDs of the derived
|
// A mapping from the def ID of an impl to the IDs of the derived
|
||||||
// methods within it.
|
// methods within it.
|
||||||
automatically_derived_methods_for_impl:
|
automatically_derived_methods_for_impl:
|
||||||
HashMap<ast::def_id, @~[ast::def_id]>
|
HashMap<ast::def_id, @~[ast::def_id]>,
|
||||||
|
|
||||||
|
// 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<ast::def_id, ast::def_id>,
|
||||||
|
|
||||||
|
// A method will be in this list if and only if it is a destructor.
|
||||||
|
destructors: HashMap<ast::def_id, ()>
|
||||||
};
|
};
|
||||||
|
|
||||||
enum tbox_flag {
|
enum tbox_flag {
|
||||||
|
@ -921,7 +930,9 @@ fn mk_ctxt(s: session::Session,
|
||||||
deriving_struct_methods: HashMap(),
|
deriving_struct_methods: HashMap(),
|
||||||
deriving_enum_methods: HashMap(),
|
deriving_enum_methods: HashMap(),
|
||||||
automatically_derived_methods: HashMap(),
|
automatically_derived_methods: HashMap(),
|
||||||
automatically_derived_methods_for_impl: HashMap()}
|
automatically_derived_methods_for_impl: HashMap(),
|
||||||
|
destructor_for_type: HashMap(),
|
||||||
|
destructors: HashMap()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3580,6 +3591,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).
|
/* If class_id names a class with a dtor, return Some(the dtor's id).
|
||||||
Otherwise return none. */
|
Otherwise return none. */
|
||||||
fn ty_dtor(cx: ctxt, class_id: def_id) -> Option<def_id> {
|
fn ty_dtor(cx: ctxt, class_id: def_id) -> Option<def_id> {
|
||||||
|
match cx.destructor_for_type.find(class_id) {
|
||||||
|
Some(method_def_id) => return Some(method_def_id),
|
||||||
|
None => {} // Continue.
|
||||||
|
}
|
||||||
|
|
||||||
if is_local(class_id) {
|
if is_local(class_id) {
|
||||||
match cx.items.find(class_id.node) {
|
match cx.items.find(class_id.node) {
|
||||||
Some(ast_map::node_item(@{
|
Some(ast_map::node_item(@{
|
||||||
|
|
|
@ -774,6 +774,7 @@ impl LookupContext {
|
||||||
let fty = self.fn_ty_from_origin(&candidate.origin);
|
let fty = self.fn_ty_from_origin(&candidate.origin);
|
||||||
|
|
||||||
self.enforce_trait_instance_limitations(fty, candidate);
|
self.enforce_trait_instance_limitations(fty, candidate);
|
||||||
|
self.enforce_drop_trait_limitations(candidate);
|
||||||
|
|
||||||
// before we only checked whether self_ty could be a subtype
|
// before we only checked whether self_ty could be a subtype
|
||||||
// of rcvr_ty; now we actually make it so (this may cause
|
// 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 {
|
fn is_relevant(&self, self_ty: ty::t, candidate: &Candidate) -> bool {
|
||||||
debug!("is_relevant(self_ty=%s, candidate=%s)",
|
debug!("is_relevant(self_ty=%s, candidate=%s)",
|
||||||
self.ty_to_str(self_ty), self.cand_to_str(candidate));
|
self.ty_to_str(self_ty), self.cand_to_str(candidate));
|
||||||
|
|
|
@ -228,6 +228,11 @@ impl CoherenceChecker {
|
||||||
// coherence checks, because we ensure by construction that no errors
|
// coherence checks, because we ensure by construction that no errors
|
||||||
// can happen at link time.
|
// can happen at link time.
|
||||||
self.add_external_crates();
|
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]) {
|
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) {
|
fn check_coherence(crate_context: @crate_ctxt, crate: @crate) {
|
||||||
|
|
12
src/test/compile-fail/drop-on-non-struct.rs
Normal file
12
src/test/compile-fail/drop-on-non-struct.rs
Normal file
|
@ -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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
15
src/test/compile-fail/explicit-call-to-dtor.rs
Normal file
15
src/test/compile-fail/explicit-call-to-dtor.rs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
|
25
src/test/compile-fail/explicit-call-to-supertrait-dtor.rs
Normal file
25
src/test/compile-fail/explicit-call-to-supertrait-dtor.rs
Normal file
|
@ -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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
14
src/test/run-pass/drop-trait.rs
Normal file
14
src/test/run-pass/drop-trait.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
struct Foo {
|
||||||
|
x: int
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo : Drop {
|
||||||
|
fn finalize() {
|
||||||
|
io::println("bye");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x: Foo = Foo { x: 3 };
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue