163 lines
5.6 KiB
Rust
163 lines
5.6 KiB
Rust
// Copyright 2013-2014 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.
|
|
|
|
//! ## The Cleanup module
|
|
//!
|
|
//! The cleanup module tracks what values need to be cleaned up as scopes
|
|
//! are exited, either via panic or just normal control flow.
|
|
//!
|
|
//! Cleanup items can be scheduled into any of the scopes on the stack.
|
|
//! Typically, when a scope is finished, we generate the cleanup code. This
|
|
//! corresponds to a normal exit from a block (for example, an expression
|
|
//! completing evaluation successfully without panic).
|
|
|
|
use llvm::BasicBlockRef;
|
|
use base;
|
|
use mir::lvalue::LvalueRef;
|
|
use rustc::mir::tcx::LvalueTy;
|
|
use builder::Builder;
|
|
use common::Funclet;
|
|
use glue;
|
|
use type_::Type;
|
|
|
|
pub struct CleanupScope<'tcx> {
|
|
// Cleanup to run upon scope exit.
|
|
cleanup: Option<DropValue<'tcx>>,
|
|
|
|
// Computed on creation if compiling with landing pads (!sess.no_landing_pads)
|
|
pub landing_pad: Option<BasicBlockRef>,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct DropValue<'tcx> {
|
|
val: LvalueRef<'tcx>,
|
|
skip_dtor: bool,
|
|
}
|
|
|
|
impl<'tcx> DropValue<'tcx> {
|
|
fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &Builder<'a, 'tcx>) {
|
|
glue::call_drop_glue(bcx, self.val, self.skip_dtor, funclet)
|
|
}
|
|
|
|
/// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary
|
|
/// for an unwind and then `resume` to continue error propagation:
|
|
///
|
|
/// landing_pad -> ... cleanups ... -> [resume]
|
|
///
|
|
/// This should only be called once per function, as it creates an alloca for the landingpad.
|
|
fn get_landing_pad<'a>(&self, bcx: &Builder<'a, 'tcx>) -> BasicBlockRef {
|
|
debug!("get_landing_pad");
|
|
let bcx = bcx.build_sibling_block("cleanup_unwind");
|
|
let llpersonality = bcx.ccx.eh_personality();
|
|
bcx.set_personality_fn(llpersonality);
|
|
|
|
if base::wants_msvc_seh(bcx.sess()) {
|
|
let pad = bcx.cleanup_pad(None, &[]);
|
|
let funclet = Some(Funclet::new(pad));
|
|
self.trans(funclet.as_ref(), &bcx);
|
|
|
|
bcx.cleanup_ret(pad, None);
|
|
} else {
|
|
// The landing pad return type (the type being propagated). Not sure
|
|
// what this represents but it's determined by the personality
|
|
// function and this is what the EH proposal example uses.
|
|
let llretty = Type::struct_(bcx.ccx, &[Type::i8p(bcx.ccx), Type::i32(bcx.ccx)], false);
|
|
|
|
// The only landing pad clause will be 'cleanup'
|
|
let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.llfn());
|
|
|
|
// The landing pad block is a cleanup
|
|
bcx.set_cleanup(llretval);
|
|
|
|
// Insert cleanup instructions into the cleanup block
|
|
self.trans(None, &bcx);
|
|
|
|
if !bcx.sess().target.target.options.custom_unwind_resume {
|
|
bcx.resume(llretval);
|
|
} else {
|
|
let exc_ptr = bcx.extract_value(llretval, 0);
|
|
bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None);
|
|
bcx.unreachable();
|
|
}
|
|
}
|
|
|
|
bcx.llbb()
|
|
}
|
|
}
|
|
|
|
impl<'a, 'tcx> CleanupScope<'tcx> {
|
|
/// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
|
|
pub fn schedule_drop_mem(
|
|
bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
|
|
) -> CleanupScope<'tcx> {
|
|
if let LvalueTy::Downcast { .. } = val.ty {
|
|
bug!("Cannot drop downcast ty yet");
|
|
}
|
|
if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
|
|
return CleanupScope::noop();
|
|
}
|
|
let drop = DropValue {
|
|
val: val,
|
|
skip_dtor: false,
|
|
};
|
|
|
|
CleanupScope::new(bcx, drop)
|
|
}
|
|
|
|
/// Issue #23611: Schedules a (deep) drop of the contents of
|
|
/// `val`, which is a pointer to an instance of struct/enum type
|
|
/// `ty`. The scheduled code handles extracting the discriminant
|
|
/// and dropping the contents associated with that variant
|
|
/// *without* executing any associated drop implementation.
|
|
pub fn schedule_drop_adt_contents(
|
|
bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
|
|
) -> CleanupScope<'tcx> {
|
|
if let LvalueTy::Downcast { .. } = val.ty {
|
|
bug!("Cannot drop downcast ty yet");
|
|
}
|
|
// `if` below could be "!contents_needs_drop"; skipping drop
|
|
// is just an optimization, so sound to be conservative.
|
|
if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
|
|
return CleanupScope::noop();
|
|
}
|
|
|
|
let drop = DropValue {
|
|
val: val,
|
|
skip_dtor: true,
|
|
};
|
|
|
|
CleanupScope::new(bcx, drop)
|
|
}
|
|
|
|
fn new(bcx: &Builder<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
|
|
CleanupScope {
|
|
cleanup: Some(drop_val),
|
|
landing_pad: if !bcx.sess().no_landing_pads() {
|
|
Some(drop_val.get_landing_pad(bcx))
|
|
} else {
|
|
None
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn noop() -> CleanupScope<'tcx> {
|
|
CleanupScope {
|
|
cleanup: None,
|
|
landing_pad: None,
|
|
}
|
|
}
|
|
|
|
pub fn trans(self, bcx: &'a Builder<'a, 'tcx>) {
|
|
if let Some(cleanup) = self.cleanup {
|
|
cleanup.trans(None, &bcx);
|
|
}
|
|
}
|
|
}
|