about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-10-31 23:31:11 -0700
committerAlex Crichton <alex@alexcrichton.com>2013-10-31 23:31:11 -0700
commite35cd962a6971a98436da1281c91507a3db07104 (patch)
treec7c4ac5d998a04831fd6faa349081be21a6a7847 /src
parentd04a58cf2d224f6e0ae69334f90a9013ae2ba35a (diff)
downloadrust-e35cd962a6971a98436da1281c91507a3db07104.tar.gz
rust-e35cd962a6971a98436da1281c91507a3db07104.zip
Drop struct fields if the user destructor fails
This commit changes drop glue generated for structs to use the invoke LLVM
instruction instead of call. What this means is that if the user destructor
triggers an unwinding, then the fields of the struct will still ge dropped.

This is not an attempt to support failing while failing, as that's mostly a
problem of runtime support. This is more of an issue of soundness in making sure
that destructors are appropriately run. The test included fails before this
commit, and only has one call to fail!(), yet it doesn't destroy its struct
fields.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/trans/glue.rs57
-rw-r--r--src/test/run-pass/fail-in-dtor-drops-fields.rs42
2 files changed, 59 insertions, 40 deletions
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 2b0f13d2a7c..02b44907f33 100644
--- a/src/librustc/middle/trans/glue.rs
+++ b/src/librustc/middle/trans/glue.rs
@@ -395,39 +395,11 @@ pub fn trans_struct_drop_flag(bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did:
     let repr = adt::represent_type(bcx.ccx(), t);
     let drop_flag = adt::trans_drop_flag_ptr(bcx, repr, v0);
     do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| {
-        let mut bcx = cx;
-
-        // Find and call the actual destructor
-        let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did,
-                                     class_did, substs.tps.clone());
-
-        // The second argument is the "self" argument for drop
-        let params = unsafe {
-            let ty = Type::from_ref(llvm::LLVMTypeOf(dtor_addr));
-            ty.element_type().func_params()
-        };
-
-        // Class dtors have no explicit args, so the params should
-        // just consist of the environment (self)
-        assert_eq!(params.len(), 1);
-
-        let self_arg = PointerCast(bcx, v0, params[0]);
-        let args = ~[self_arg];
-
-        Call(bcx, dtor_addr, args, []);
-
-        // Drop the fields
-        let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
-        for (i, fld) in field_tys.iter().enumerate() {
-            let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
-            bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
-        }
-
-        bcx
+        trans_struct_drop(cx, t, v0, dtor_did, class_did, substs)
     }
 }
 
-pub fn trans_struct_drop(mut bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did: ast::DefId,
+pub fn trans_struct_drop(bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did: ast::DefId,
                          class_did: ast::DefId, substs: &ty::substs) -> @mut Block {
     let repr = adt::represent_type(bcx.ccx(), t);
 
@@ -445,19 +417,24 @@ pub fn trans_struct_drop(mut bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did:
     // just consist of the environment (self)
     assert_eq!(params.len(), 1);
 
-    let self_arg = PointerCast(bcx, v0, params[0]);
-    let args = ~[self_arg];
+    // Be sure to put all of the fields into a scope so we can use an invoke
+    // instruction to call the user destructor but still call the field
+    // destructors if the user destructor fails.
+    do with_scope(bcx, None, "field drops") |bcx| {
+        let self_arg = PointerCast(bcx, v0, params[0]);
+        let args = ~[self_arg];
 
-    Call(bcx, dtor_addr, args, []);
+        // Add all the fields as a value which needs to be cleaned at the end of
+        // this scope.
+        let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
+        for (i, fld) in field_tys.iter().enumerate() {
+            let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
+            add_clean(bcx, llfld_a, fld.mt.ty);
+        }
 
-    // Drop the fields
-    let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
-    for (i, fld) in field_tys.iter().enumerate() {
-        let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
-        bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
+        let (_, bcx) = invoke(bcx, dtor_addr, args, []);
+        bcx
     }
-
-    bcx
 }
 
 pub fn make_drop_glue(bcx: @mut Block, v0: ValueRef, t: ty::t) -> @mut Block {
diff --git a/src/test/run-pass/fail-in-dtor-drops-fields.rs b/src/test/run-pass/fail-in-dtor-drops-fields.rs
new file mode 100644
index 00000000000..7c6bb1fa379
--- /dev/null
+++ b/src/test/run-pass/fail-in-dtor-drops-fields.rs
@@ -0,0 +1,42 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::task;
+
+static mut dropped: bool = false;
+
+struct A {
+    b: B,
+}
+
+struct B {
+    foo: int,
+}
+
+impl Drop for A {
+    fn drop(&mut self) {
+        fail!()
+    }
+}
+
+impl Drop for B {
+    fn drop(&mut self) {
+        unsafe { dropped = true; }
+    }
+}
+
+pub fn main() {
+    let ret = do task::try {
+        let _a = A { b: B { foo: 3 } };
+    };
+    assert!(ret.is_err());
+    unsafe { assert!(dropped); }
+}
+