Skip to content

Commit 702767d

Browse files
committed
auto merge of #10219 : alexcrichton/rust/drop-invoke, r=pcwalton
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.
2 parents dc079e1 + e35cd96 commit 702767d

File tree

2 files changed

+59
-40
lines changed

2 files changed

+59
-40
lines changed

Diff for: src/librustc/middle/trans/glue.rs

+17-40
Original file line numberDiff line numberDiff line change
@@ -395,39 +395,11 @@ pub fn trans_struct_drop_flag(bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did:
395395
let repr = adt::represent_type(bcx.ccx(), t);
396396
let drop_flag = adt::trans_drop_flag_ptr(bcx, repr, v0);
397397
do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| {
398-
let mut bcx = cx;
399-
400-
// Find and call the actual destructor
401-
let dtor_addr = get_res_dtor(bcx.ccx(), dtor_did,
402-
class_did, substs.tps.clone());
403-
404-
// The second argument is the "self" argument for drop
405-
let params = unsafe {
406-
let ty = Type::from_ref(llvm::LLVMTypeOf(dtor_addr));
407-
ty.element_type().func_params()
408-
};
409-
410-
// Class dtors have no explicit args, so the params should
411-
// just consist of the environment (self)
412-
assert_eq!(params.len(), 1);
413-
414-
let self_arg = PointerCast(bcx, v0, params[0]);
415-
let args = ~[self_arg];
416-
417-
Call(bcx, dtor_addr, args, []);
418-
419-
// Drop the fields
420-
let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
421-
for (i, fld) in field_tys.iter().enumerate() {
422-
let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
423-
bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
424-
}
425-
426-
bcx
398+
trans_struct_drop(cx, t, v0, dtor_did, class_did, substs)
427399
}
428400
}
429401

430-
pub fn trans_struct_drop(mut bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did: ast::DefId,
402+
pub fn trans_struct_drop(bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did: ast::DefId,
431403
class_did: ast::DefId, substs: &ty::substs) -> @mut Block {
432404
let repr = adt::represent_type(bcx.ccx(), t);
433405

@@ -445,19 +417,24 @@ pub fn trans_struct_drop(mut bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did:
445417
// just consist of the environment (self)
446418
assert_eq!(params.len(), 1);
447419

448-
let self_arg = PointerCast(bcx, v0, params[0]);
449-
let args = ~[self_arg];
420+
// Be sure to put all of the fields into a scope so we can use an invoke
421+
// instruction to call the user destructor but still call the field
422+
// destructors if the user destructor fails.
423+
do with_scope(bcx, None, "field drops") |bcx| {
424+
let self_arg = PointerCast(bcx, v0, params[0]);
425+
let args = ~[self_arg];
450426

451-
Call(bcx, dtor_addr, args, []);
427+
// Add all the fields as a value which needs to be cleaned at the end of
428+
// this scope.
429+
let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
430+
for (i, fld) in field_tys.iter().enumerate() {
431+
let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
432+
add_clean(bcx, llfld_a, fld.mt.ty);
433+
}
452434

453-
// Drop the fields
454-
let field_tys = ty::struct_fields(bcx.tcx(), class_did, substs);
455-
for (i, fld) in field_tys.iter().enumerate() {
456-
let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
457-
bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
435+
let (_, bcx) = invoke(bcx, dtor_addr, args, []);
436+
bcx
458437
}
459-
460-
bcx
461438
}
462439

463440
pub fn make_drop_glue(bcx: @mut Block, v0: ValueRef, t: ty::t) -> @mut Block {

Diff for: src/test/run-pass/fail-in-dtor-drops-fields.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::task;
12+
13+
static mut dropped: bool = false;
14+
15+
struct A {
16+
b: B,
17+
}
18+
19+
struct B {
20+
foo: int,
21+
}
22+
23+
impl Drop for A {
24+
fn drop(&mut self) {
25+
fail!()
26+
}
27+
}
28+
29+
impl Drop for B {
30+
fn drop(&mut self) {
31+
unsafe { dropped = true; }
32+
}
33+
}
34+
35+
pub fn main() {
36+
let ret = do task::try {
37+
let _a = A { b: B { foo: 3 } };
38+
};
39+
assert!(ret.is_err());
40+
unsafe { assert!(dropped); }
41+
}
42+

0 commit comments

Comments
 (0)