1 // Copyright 2013-2014 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.
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.
12 * Code pertaining to cleanup of temporaries as well as execution of
13 * drop glue. See discussion in `doc.rs` for a high-level summary.
16 use lib::llvm::{BasicBlockRef, ValueRef};
17 use middle::lang_items::{EhPersonalityLangItem};
18 use middle::trans::base;
19 use middle::trans::build;
20 use middle::trans::callee;
21 use middle::trans::common;
22 use middle::trans::common::{Block, FunctionContext, ExprId};
23 use middle::trans::glue;
24 use middle::trans::type_::Type;
27 use util::ppaux::Repr;
29 pub struct CleanupScope<'a> {
30 // The id of this cleanup scope. If the id is None,
31 // this is a *temporary scope* that is pushed during trans to
32 // cleanup miscellaneous garbage that trans may generate whose
33 // lifetime is a subset of some expression. See module doc for
35 kind: CleanupScopeKind<'a>,
37 // Cleanups to run upon scope exit.
38 cleanups: Vec<~Cleanup>,
40 cached_early_exits: Vec<CachedEarlyExit>,
41 cached_landing_pad: Option<BasicBlockRef>,
44 pub struct CustomScopeIndex {
48 pub static EXIT_BREAK: uint = 0;
49 pub static EXIT_LOOP: uint = 1;
50 pub static EXIT_MAX: uint = 2;
52 pub enum CleanupScopeKind<'a> {
54 AstScopeKind(ast::NodeId),
55 LoopScopeKind(ast::NodeId, [&'a Block<'a>, ..EXIT_MAX])
59 pub enum EarlyExitLabel {
62 LoopExit(ast::NodeId, uint)
65 pub struct CachedEarlyExit {
66 label: EarlyExitLabel,
67 cleanup_block: BasicBlockRef,
71 fn clean_on_unwind(&self) -> bool;
72 fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a>;
76 AstScope(ast::NodeId),
77 CustomScope(CustomScopeIndex)
80 impl<'a> CleanupMethods<'a> for FunctionContext<'a> {
81 fn push_ast_cleanup_scope(&self, id: ast::NodeId) {
83 * Invoked when we start to trans the code contained
84 * within a new cleanup scope.
87 debug!("push_ast_cleanup_scope({})",
88 self.ccx.tcx.map.node_to_str(id));
90 // FIXME(#2202) -- currently closure bodies have a parent
91 // region, which messes up the assertion below, since there
92 // are no cleanup scopes on the stack at the start of
93 // trans'ing a closure body. I think though that this should
94 // eventually be fixed by closure bodies not having a parent
95 // region, though that's a touch unclear, and it might also be
96 // better just to narrow this assertion more (i.e., by
97 // excluding id's that correspond to closure bodies only). For
98 // now we just say that if there is already an AST scope on the stack,
99 // this new AST scope had better be its immediate child.
100 let top_scope = self.top_ast_scope();
101 if top_scope.is_some() {
102 assert_eq!(self.ccx.tcx.region_maps.opt_encl_scope(id), top_scope);
105 self.push_scope(CleanupScope::new(AstScopeKind(id)));
108 fn push_loop_cleanup_scope(&self,
110 exits: [&'a Block<'a>, ..EXIT_MAX]) {
111 debug!("push_loop_cleanup_scope({})",
112 self.ccx.tcx.map.node_to_str(id));
113 assert_eq!(Some(id), self.top_ast_scope());
115 self.push_scope(CleanupScope::new(LoopScopeKind(id, exits)));
118 fn push_custom_cleanup_scope(&self) -> CustomScopeIndex {
119 let index = self.scopes_len();
120 debug!("push_custom_cleanup_scope(): {}", index);
121 self.push_scope(CleanupScope::new(CustomScopeKind));
122 CustomScopeIndex { index: index }
125 fn pop_and_trans_ast_cleanup_scope(&self,
127 cleanup_scope: ast::NodeId)
130 * Removes the cleanup scope for id `cleanup_scope`, which
131 * must be at the top of the cleanup stack, and generates the
132 * code to do its cleanups for normal exit.
135 debug!("pop_and_trans_ast_cleanup_scope({})",
136 self.ccx.tcx.map.node_to_str(cleanup_scope));
138 assert!(self.top_scope(|s| s.kind.is_ast_with_id(cleanup_scope)));
140 let scope = self.pop_scope();
141 self.trans_scope_cleanups(bcx, &scope)
145 fn pop_loop_cleanup_scope(&self,
146 cleanup_scope: ast::NodeId) {
148 * Removes the loop cleanup scope for id `cleanup_scope`, which
149 * must be at the top of the cleanup stack. Does not generate
150 * any cleanup code, since loop scopes should exit by
151 * branching to a block generated by `normal_exit_block`.
154 debug!("pop_loop_cleanup_scope({})",
155 self.ccx.tcx.map.node_to_str(cleanup_scope));
157 assert!(self.top_scope(|s| s.kind.is_loop_with_id(cleanup_scope)));
159 let _ = self.pop_scope();
162 fn pop_custom_cleanup_scope(&self,
163 custom_scope: CustomScopeIndex) {
165 * Removes the top cleanup scope from the stack without
166 * executing its cleanups. The top cleanup scope must
167 * be the temporary scope `custom_scope`.
170 debug!("pop_custom_cleanup_scope({})", custom_scope.index);
171 assert!(self.is_valid_to_pop_custom_scope(custom_scope));
172 let _ = self.pop_scope();
175 fn pop_and_trans_custom_cleanup_scope(&self,
177 custom_scope: CustomScopeIndex)
180 * Removes the top cleanup scope from the stack, which must be
181 * a temporary scope, and generates the code to do its
182 * cleanups for normal exit.
185 debug!("pop_and_trans_custom_cleanup_scope({:?})", custom_scope);
186 assert!(self.is_valid_to_pop_custom_scope(custom_scope));
188 let scope = self.pop_scope();
189 self.trans_scope_cleanups(bcx, &scope)
192 fn top_loop_scope(&self) -> ast::NodeId {
194 * Returns the id of the top-most loop scope
197 for scope in self.scopes.borrow().iter().rev() {
199 LoopScopeKind(id, _) => {
205 self.ccx.sess().bug("no loop scope found");
208 fn normal_exit_block(&'a self,
209 cleanup_scope: ast::NodeId,
210 exit: uint) -> BasicBlockRef {
212 * Returns a block to branch to which will perform all pending
213 * cleanups and then break/continue (depending on `exit`) out
214 * of the loop with id `cleanup_scope`
217 self.trans_cleanups_to_exit_scope(LoopExit(cleanup_scope, exit))
220 fn return_exit_block(&'a self) -> BasicBlockRef {
222 * Returns a block to branch to which will perform all pending
223 * cleanups and then return from this function
226 self.trans_cleanups_to_exit_scope(ReturnExit)
229 fn schedule_drop_mem(&self,
230 cleanup_scope: ScopeId,
234 * Schedules a (deep) drop of `val`, which is a pointer to an
238 if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; }
239 let drop = box DropValue {
241 on_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty),
246 debug!("schedule_drop_mem({:?}, val={}, ty={})",
248 self.ccx.tn.val_to_str(val),
249 ty.repr(self.ccx.tcx()));
251 self.schedule_clean(cleanup_scope, drop as ~Cleanup);
254 fn schedule_drop_immediate(&self,
255 cleanup_scope: ScopeId,
259 * Schedules a (deep) drop of `val`, which is an instance of `ty`
262 if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; }
263 let drop = box DropValue {
265 on_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty),
270 debug!("schedule_drop_immediate({:?}, val={}, ty={})",
272 self.ccx.tn.val_to_str(val),
273 ty.repr(self.ccx.tcx()));
275 self.schedule_clean(cleanup_scope, drop as ~Cleanup);
278 fn schedule_free_value(&self,
279 cleanup_scope: ScopeId,
283 * Schedules a call to `free(val)`. Note that this is a shallow
287 let drop = box FreeValue { ptr: val, heap: heap };
289 debug!("schedule_free_value({:?}, val={}, heap={:?})",
291 self.ccx.tn.val_to_str(val),
294 self.schedule_clean(cleanup_scope, drop as ~Cleanup);
297 fn schedule_clean(&self,
298 cleanup_scope: ScopeId,
300 match cleanup_scope {
301 AstScope(id) => self.schedule_clean_in_ast_scope(id, cleanup),
302 CustomScope(id) => self.schedule_clean_in_custom_scope(id, cleanup),
306 fn schedule_clean_in_ast_scope(&self,
307 cleanup_scope: ast::NodeId,
310 * Schedules a cleanup to occur upon exit from `cleanup_scope`.
311 * If `cleanup_scope` is not provided, then the cleanup is scheduled
312 * in the topmost scope, which must be a temporary scope.
315 debug!("schedule_clean_in_ast_scope(cleanup_scope={:?})",
318 for scope in self.scopes.borrow_mut().mut_iter().rev() {
319 if scope.kind.is_ast_with_id(cleanup_scope) {
320 scope.cleanups.push(cleanup);
321 scope.clear_cached_exits();
324 // will be adding a cleanup to some enclosing scope
325 scope.clear_cached_exits();
330 format!("no cleanup scope {} found",
331 self.ccx.tcx.map.node_to_str(cleanup_scope)));
334 fn schedule_clean_in_custom_scope(&self,
335 custom_scope: CustomScopeIndex,
338 * Schedules a cleanup to occur in the top-most scope,
339 * which must be a temporary scope.
342 debug!("schedule_clean_in_custom_scope(custom_scope={})",
345 assert!(self.is_valid_custom_scope(custom_scope));
347 let mut scopes = self.scopes.borrow_mut();
348 let scope = scopes.get_mut(custom_scope.index);
349 scope.cleanups.push(cleanup);
350 scope.clear_cached_exits();
353 fn needs_invoke(&self) -> bool {
355 * Returns true if there are pending cleanups that should
356 * execute on failure.
359 self.scopes.borrow().iter().rev().any(|s| s.needs_invoke())
362 fn get_landing_pad(&'a self) -> BasicBlockRef {
364 * Returns a basic block to branch to in the event of a failure.
365 * This block will run the failure cleanups and eventually
366 * invoke the LLVM `Resume` instruction.
369 let _icx = base::push_ctxt("get_landing_pad");
371 debug!("get_landing_pad");
373 let orig_scopes_len = self.scopes_len();
374 assert!(orig_scopes_len > 0);
376 // Remove any scopes that do not have cleanups on failure:
377 let mut popped_scopes = vec!();
378 while !self.top_scope(|s| s.needs_invoke()) {
379 debug!("top scope does not need invoke");
380 popped_scopes.push(self.pop_scope());
383 // Check for an existing landing pad in the new topmost scope:
384 let llbb = self.get_or_create_landing_pad();
386 // Push the scopes we removed back on:
388 match popped_scopes.pop() {
389 Some(scope) => self.push_scope(scope),
394 assert_eq!(self.scopes_len(), orig_scopes_len);
400 impl<'a> CleanupHelperMethods<'a> for FunctionContext<'a> {
401 fn top_ast_scope(&self) -> Option<ast::NodeId> {
403 * Returns the id of the current top-most AST scope, if any.
405 for scope in self.scopes.borrow().iter().rev() {
407 CustomScopeKind | LoopScopeKind(..) => {}
416 fn top_nonempty_cleanup_scope(&self) -> Option<uint> {
417 self.scopes.borrow().iter().rev().position(|s| !s.cleanups.is_empty())
420 fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool {
421 self.is_valid_custom_scope(custom_scope) &&
422 custom_scope.index == self.scopes.borrow().len() - 1
425 fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool {
426 let scopes = self.scopes.borrow();
427 custom_scope.index < scopes.len() &&
428 scopes.get(custom_scope.index).kind.is_temp()
431 fn trans_scope_cleanups(&self, // cannot borrow self, will recurse
433 scope: &CleanupScope) -> &'a Block<'a> {
434 /*! Generates the cleanups for `scope` into `bcx` */
437 if !bcx.unreachable.get() {
438 for cleanup in scope.cleanups.iter().rev() {
439 bcx = cleanup.trans(bcx);
445 fn scopes_len(&self) -> uint {
446 self.scopes.borrow().len()
449 fn push_scope(&self, scope: CleanupScope<'a>) {
450 self.scopes.borrow_mut().push(scope)
453 fn pop_scope(&self) -> CleanupScope<'a> {
454 debug!("popping cleanup scope {}, {} scopes remaining",
455 self.top_scope(|s| s.block_name("")),
456 self.scopes_len() - 1);
458 self.scopes.borrow_mut().pop().unwrap()
461 fn top_scope<R>(&self, f: |&CleanupScope<'a>| -> R) -> R {
462 f(self.scopes.borrow().last().unwrap())
465 fn trans_cleanups_to_exit_scope(&'a self,
466 label: EarlyExitLabel)
469 * Used when the caller wishes to jump to an early exit, such
470 * as a return, break, continue, or unwind. This function will
471 * generate all cleanups between the top of the stack and the
472 * exit `label` and return a basic block that the caller can
475 * For example, if the current stack of cleanups were as follows:
484 * and the `label` specifies a break from `Loop 23`, then this
485 * function would generate a series of basic blocks as follows:
487 * Cleanup(AST 24) -> Cleanup(Custom 2) -> break_blk
489 * where `break_blk` is the block specified in `Loop 23` as
490 * the target for breaks. The return value would be the first
491 * basic block in that sequence (`Cleanup(AST 24)`). The
492 * caller could then branch to `Cleanup(AST 24)` and it will
493 * perform all cleanups and finally branch to the `break_blk`.
496 debug!("trans_cleanups_to_exit_scope label={:?} scopes={}",
497 label, self.scopes_len());
499 let orig_scopes_len = self.scopes_len();
501 let mut popped_scopes = vec!();
503 // First we pop off all the cleanup stacks that are
504 // traversed until the exit is reached, pushing them
505 // onto the side vector `popped_scopes`. No code is
506 // generated at this time.
508 // So, continuing the example from above, we would wind up
509 // with a `popped_scopes` vector of `[AST 24, Custom 2]`.
510 // (Presuming that there are no cached exits)
512 if self.scopes_len() == 0 {
515 // Generate a block that will `Resume`.
516 let prev_bcx = self.new_block(true, "resume", None);
517 let personality = self.personality.get().expect(
518 "create_landing_pad() should have set this");
519 build::Resume(prev_bcx,
520 build::Load(prev_bcx, personality));
521 prev_llbb = prev_bcx.llbb;
526 prev_llbb = self.get_llreturn();
531 self.ccx.sess().bug(format!(
532 "cannot exit from scope {:?}, \
538 // Check if we have already cached the unwinding of this
539 // scope for this label. If so, we can stop popping scopes
540 // and branch to the cached label, since it contains the
541 // cleanups for any subsequent scopes.
542 match self.top_scope(|s| s.cached_early_exit(label)) {
543 Some(cleanup_block) => {
544 prev_llbb = cleanup_block;
550 // Pop off the scope, since we will be generating
551 // unwinding code for it. If we are searching for a loop exit,
552 // and this scope is that loop, then stop popping and set
553 // `prev_llbb` to the appropriate exit block from the loop.
554 popped_scopes.push(self.pop_scope());
555 let scope = popped_scopes.last().unwrap();
557 UnwindExit | ReturnExit => { }
558 LoopExit(id, exit) => {
559 match scope.kind.early_exit_block(id, exit) {
561 prev_llbb = exitllbb;
571 debug!("trans_cleanups_to_exit_scope: popped {} scopes",
572 popped_scopes.len());
574 // Now push the popped scopes back on. As we go,
575 // we track in `prev_llbb` the exit to which this scope
576 // should branch when it's done.
578 // So, continuing with our example, we will start out with
579 // `prev_llbb` being set to `break_blk` (or possibly a cached
580 // early exit). We will then pop the scopes from `popped_scopes`
581 // and generate a basic block for each one, prepending it in the
582 // series and updating `prev_llbb`. So we begin by popping `Custom 2`
583 // and generating `Cleanup(Custom 2)`. We make `Cleanup(Custom 2)`
584 // branch to `prev_llbb == break_blk`, giving us a sequence like:
586 // Cleanup(Custom 2) -> prev_llbb
588 // We then pop `AST 24` and repeat the process, giving us the sequence:
590 // Cleanup(AST 24) -> Cleanup(Custom 2) -> prev_llbb
592 // At this point, `popped_scopes` is empty, and so the final block
593 // that we return to the user is `Cleanup(AST 24)`.
594 while !popped_scopes.is_empty() {
595 let mut scope = popped_scopes.pop().unwrap();
597 if scope.cleanups.iter().any(|c| cleanup_is_suitable_for(*c, label))
599 let name = scope.block_name("clean");
600 debug!("generating cleanups for {}", name);
601 let bcx_in = self.new_block(label.is_unwind(), name, None);
602 let mut bcx_out = bcx_in;
603 for cleanup in scope.cleanups.iter().rev() {
604 if cleanup_is_suitable_for(*cleanup, label) {
605 bcx_out = cleanup.trans(bcx_out);
608 build::Br(bcx_out, prev_llbb);
609 prev_llbb = bcx_in.llbb;
611 debug!("no suitable cleanups in {}",
612 scope.block_name("clean"));
615 scope.add_cached_early_exit(label, prev_llbb);
616 self.push_scope(scope);
619 debug!("trans_cleanups_to_exit_scope: prev_llbb={}", prev_llbb);
621 assert_eq!(self.scopes_len(), orig_scopes_len);
625 fn get_or_create_landing_pad(&'a self) -> BasicBlockRef {
627 * Creates a landing pad for the top scope, if one does not
628 * exist. The landing pad will perform all cleanups necessary
629 * for an unwind and then `resume` to continue error
632 * landing_pad -> ... cleanups ... -> [resume]
634 * (The cleanups and resume instruction are created by
635 * `trans_cleanups_to_exit_scope()`, not in this function
641 debug!("get_or_create_landing_pad");
643 // Check if a landing pad block exists; if not, create one.
645 let mut scopes = self.scopes.borrow_mut();
646 let last_scope = scopes.mut_last().unwrap();
647 match last_scope.cached_landing_pad {
648 Some(llbb) => { return llbb; }
650 let name = last_scope.block_name("unwind");
651 pad_bcx = self.new_block(true, name, None);
652 last_scope.cached_landing_pad = Some(pad_bcx.llbb);
657 // The landing pad return type (the type being propagated). Not sure what
658 // this represents but it's determined by the personality function and
659 // this is what the EH proposal example uses.
660 let llretty = Type::struct_(self.ccx,
661 [Type::i8p(self.ccx), Type::i32(self.ccx)],
664 // The exception handling personality function.
665 let def_id = common::langcall(pad_bcx, None, "", EhPersonalityLangItem);
666 let llpersonality = callee::trans_fn_ref(pad_bcx, def_id, ExprId(0));
668 // The only landing pad clause will be 'cleanup'
669 let llretval = build::LandingPad(pad_bcx, llretty, llpersonality, 1u);
671 // The landing pad block is a cleanup
672 build::SetCleanup(pad_bcx, llretval);
674 // We store the retval in a function-central alloca, so that calls to
675 // Resume can find it.
676 match self.personality.get() {
678 build::Store(pad_bcx, llretval, addr);
681 let addr = base::alloca(pad_bcx, common::val_ty(llretval), "");
682 self.personality.set(Some(addr));
683 build::Store(pad_bcx, llretval, addr);
687 // Generate the cleanup block and branch to it.
688 let cleanup_llbb = self.trans_cleanups_to_exit_scope(UnwindExit);
689 build::Br(pad_bcx, cleanup_llbb);
695 impl<'a> CleanupScope<'a> {
696 fn new(kind: CleanupScopeKind<'a>) -> CleanupScope<'a> {
700 cached_early_exits: vec!(),
701 cached_landing_pad: None,
705 fn clear_cached_exits(&mut self) {
706 self.cached_early_exits = vec!();
707 self.cached_landing_pad = None;
710 fn cached_early_exit(&self,
711 label: EarlyExitLabel)
712 -> Option<BasicBlockRef> {
713 self.cached_early_exits.iter().
714 find(|e| e.label == label).
715 map(|e| e.cleanup_block)
718 fn add_cached_early_exit(&mut self,
719 label: EarlyExitLabel,
720 blk: BasicBlockRef) {
721 self.cached_early_exits.push(
722 CachedEarlyExit { label: label,
723 cleanup_block: blk });
726 fn needs_invoke(&self) -> bool {
727 /*! True if this scope has cleanups for use during unwinding */
729 self.cached_landing_pad.is_some() ||
730 self.cleanups.iter().any(|c| c.clean_on_unwind())
733 fn block_name(&self, prefix: &str) -> ~str {
735 * Returns a suitable name to use for the basic block that
736 * handles this cleanup scope
740 CustomScopeKind => format!("{}_custom_", prefix),
741 AstScopeKind(id) => format!("{}_ast_{}_", prefix, id),
742 LoopScopeKind(id, _) => format!("{}_loop_{}_", prefix, id),
747 impl<'a> CleanupScopeKind<'a> {
748 fn is_temp(&self) -> bool {
750 CustomScopeKind => true,
751 LoopScopeKind(..) | AstScopeKind(..) => false,
755 fn is_ast_with_id(&self, id: ast::NodeId) -> bool {
757 CustomScopeKind | LoopScopeKind(..) => false,
758 AstScopeKind(i) => i == id
762 fn is_loop_with_id(&self, id: ast::NodeId) -> bool {
764 CustomScopeKind | AstScopeKind(..) => false,
765 LoopScopeKind(i, _) => i == id
769 fn early_exit_block(&self,
771 exit: uint) -> Option<BasicBlockRef> {
773 * If this is a loop scope with id `id`, return the early
774 * exit block `exit`, else `None`
778 LoopScopeKind(i, ref exits) if id == i => Some(exits[exit].llbb),
784 impl EarlyExitLabel {
785 fn is_unwind(&self) -> bool {
793 ///////////////////////////////////////////////////////////////////////////
796 pub struct DropValue {
803 impl Cleanup for DropValue {
804 fn clean_on_unwind(&self) -> bool {
808 fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> {
809 if self.is_immediate {
810 glue::drop_ty_immediate(bcx, self.val, self.ty)
812 glue::drop_ty(bcx, self.val, self.ty)
822 pub struct FreeValue {
827 impl Cleanup for FreeValue {
828 fn clean_on_unwind(&self) -> bool {
832 fn trans<'a>(&self, bcx: &'a Block<'a>) -> &'a Block<'a> {
835 glue::trans_free(bcx, self.ptr)
838 glue::trans_exchange_free(bcx, self.ptr)
844 pub fn temporary_scope(tcx: &ty::ctxt,
847 match tcx.region_maps.temporary_scope(id) {
849 let r = AstScope(scope);
850 debug!("temporary_scope({}) = {:?}", id, r);
854 tcx.sess.bug(format!("no temporary scope available for expr {}", id))
859 pub fn var_scope(tcx: &ty::ctxt,
862 let r = AstScope(tcx.region_maps.var_scope(id));
863 debug!("var_scope({}) = {:?}", id, r);
867 fn cleanup_is_suitable_for(c: &Cleanup,
868 label: EarlyExitLabel) -> bool {
869 !label.is_unwind() || c.clean_on_unwind()
872 ///////////////////////////////////////////////////////////////////////////
873 // These traits just exist to put the methods into this file.
875 pub trait CleanupMethods<'a> {
876 fn push_ast_cleanup_scope(&self, id: ast::NodeId);
877 fn push_loop_cleanup_scope(&self,
879 exits: [&'a Block<'a>, ..EXIT_MAX]);
880 fn push_custom_cleanup_scope(&self) -> CustomScopeIndex;
881 fn pop_and_trans_ast_cleanup_scope(&self,
883 cleanup_scope: ast::NodeId)
885 fn pop_loop_cleanup_scope(&self,
886 cleanup_scope: ast::NodeId);
887 fn pop_custom_cleanup_scope(&self,
888 custom_scope: CustomScopeIndex);
889 fn pop_and_trans_custom_cleanup_scope(&self,
891 custom_scope: CustomScopeIndex)
893 fn top_loop_scope(&self) -> ast::NodeId;
894 fn normal_exit_block(&'a self,
895 cleanup_scope: ast::NodeId,
896 exit: uint) -> BasicBlockRef;
897 fn return_exit_block(&'a self) -> BasicBlockRef;
898 fn schedule_drop_mem(&self,
899 cleanup_scope: ScopeId,
902 fn schedule_drop_immediate(&self,
903 cleanup_scope: ScopeId,
906 fn schedule_free_value(&self,
907 cleanup_scope: ScopeId,
910 fn schedule_clean(&self,
911 cleanup_scope: ScopeId,
913 fn schedule_clean_in_ast_scope(&self,
914 cleanup_scope: ast::NodeId,
916 fn schedule_clean_in_custom_scope(&self,
917 custom_scope: CustomScopeIndex,
919 fn needs_invoke(&self) -> bool;
920 fn get_landing_pad(&'a self) -> BasicBlockRef;
923 trait CleanupHelperMethods<'a> {
924 fn top_ast_scope(&self) -> Option<ast::NodeId>;
925 fn top_nonempty_cleanup_scope(&self) -> Option<uint>;
926 fn is_valid_to_pop_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool;
927 fn is_valid_custom_scope(&self, custom_scope: CustomScopeIndex) -> bool;
928 fn trans_scope_cleanups(&self,
930 scope: &CleanupScope<'a>) -> &'a Block<'a>;
931 fn trans_cleanups_to_exit_scope(&'a self,
932 label: EarlyExitLabel)
934 fn get_or_create_landing_pad(&'a self) -> BasicBlockRef;
935 fn scopes_len(&self) -> uint;
936 fn push_scope(&self, scope: CleanupScope<'a>);
937 fn pop_scope(&self) -> CleanupScope<'a>;
938 fn top_scope<R>(&self, f: |&CleanupScope<'a>| -> R) -> R;