about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-11-05 17:50:01 -0800
committerPatrick Walton <pcwalton@mimiga.net>2012-11-07 12:34:32 -0800
commit29040955709a145842f93fe11d4cd803415020be (patch)
treecd27b595087e924d76c4ae1fcb7a91806cb95a44
parent70886d314d0e49fe652f022139b03c2ddaca7b14 (diff)
downloadrust-29040955709a145842f93fe11d4cd803415020be.tar.gz
rust-29040955709a145842f93fe11d4cd803415020be.zip
rustc: Implement the Drop trait. r=brson
-rw-r--r--doc/tutorial.md45
-rw-r--r--src/libcore/core.rs4
-rw-r--r--src/libcore/ops.rs5
-rw-r--r--src/rustc/middle/lang_items.rs6
-rw-r--r--src/rustc/middle/ty.rs20
-rw-r--r--src/rustc/middle/typeck/check/method.rs20
-rw-r--r--src/rustc/middle/typeck/coherence.rs57
-rw-r--r--src/test/compile-fail/drop-on-non-struct.rs12
-rw-r--r--src/test/compile-fail/explicit-call-to-dtor.rs15
-rw-r--r--src/test/compile-fail/explicit-call-to-supertrait-dtor.rs25
-rw-r--r--src/test/run-pass/drop-trait.rs14
11 files changed, 200 insertions, 23 deletions
diff --git a/doc/tutorial.md b/doc/tutorial.md
index a818689d7e3..0d542441103 100644
--- a/doc/tutorial.md
+++ b/doc/tutorial.md
@@ -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 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:
 
 * `Copy` - Types that can be copied: either implicitly, or explicitly with the
-  `copy` operator. All types are copyable unless they are classes
-  with destructors or otherwise contain classes with destructors.
+  `copy` operator. All types are copyable unless they have destructors or
+  contain types with destructors.
 
 * `Send` - Sendable (owned) types. All types are sendable unless they
   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
 > 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
 
 A trait consists of a set of methods, without bodies, or may be empty,
diff --git a/src/libcore/core.rs b/src/libcore/core.rs
index 12a426261c4..1be217dac9f 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 7c6bcf5bd51..96b96d0f27a 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<RHS,Result> {
     pure fn add(rhs: &RHS) -> Result;
diff --git a/src/rustc/middle/lang_items.rs b/src/rustc/middle/lang_items.rs
index 383fe2db323..b1d68bee259 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<def_id>,
     mut owned_trait: Option<def_id>,
 
+    mut drop_trait: Option<def_id>,
+
     mut add_trait: Option<def_id>,
     mut sub_trait: Option<def_id>,
     mut mul_trait: Option<def_id>,
@@ -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 14c5ed5e71e..5e1bccb6c9d 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -404,7 +404,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<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 {
@@ -921,7 +930,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()}
 }
 
 
@@ -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).
    Otherwise return none. */
 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) {
        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 dd3f4069a9c..8afd28a5914 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 d0ec2b3cfc5..26c559fd135 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 00000000000..8ee72d9d2fd
--- /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 00000000000..56af671852b
--- /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 00000000000..c7c2748235a
--- /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 00000000000..3a379a4c9dc
--- /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 };
+}
+