1 // Copyright 2012-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.
13 This file actually contains two passes related to regions. The first
14 pass builds up the `scope_map`, which describes the parent links in
15 the region hierarchy. The second pass infers which types must be
18 Most of the documentation on regions can be found in
19 `middle/typeck/infer/region_inference.rs`
24 use driver::session::Session;
25 use middle::ty::{FreeRegion};
27 use util::nodemap::NodeMap;
28 use util::common::can_reach;
30 use std::cell::RefCell;
31 use std::collections::{HashMap, HashSet};
32 use syntax::codemap::Span;
33 use syntax::{ast, visit};
34 use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
35 use syntax::ast_util::{stmt_id};
37 use syntax::visit::{Visitor, FnKind};
40 The region maps encode information about region relationships.
42 - `scope_map` maps from a scope id to the enclosing scope id; this is
43 usually corresponding to the lexical nesting, though in the case of
44 closures the parent scope is the innermost conditional expression or repeating
47 - `var_map` maps from a variable or binding id to the block in which
48 that variable is declared.
50 - `free_region_map` maps from a free region `a` to a list of free
51 regions `bs` such that `a <= b for all b in bs`
52 - the free region map is populated during type check as we check
53 each function. See the function `relate_free_regions` for
56 - `rvalue_scopes` includes entries for those expressions whose cleanup
57 scope is larger than the default. The map goes from the expression
58 id to the cleanup scope id. For rvalues not present in this table,
59 the appropriate cleanup scope is the innermost enclosing statement,
60 conditional expression, or repeating block (see `terminating_scopes`).
62 - `terminating_scopes` is a set containing the ids of each statement,
63 or conditional/repeating expression. These scopes are calling "terminating
64 scopes" because, when attempting to find the scope of a temporary, by
65 default we search up the enclosing scopes until we encounter the
66 terminating scope. A conditional/repeating
67 expression is one which is not guaranteed to execute exactly once
68 upon entering the parent scope. This could be because the expression
69 only executes conditionally, such as the expression `b` in `a && b`,
70 or because the expression may execute many times, such as a loop
71 body. The reason that we distinguish such expressions is that, upon
72 exiting the parent scope, we cannot statically know how many times
73 the expression executed, and thus if the expression creates
74 temporaries we cannot know statically how many such temporaries we
75 would have to cleanup. Therefore we ensure that the temporaries never
76 outlast the conditional/repeating expression, preventing the need
77 for dynamic checks and/or arbitrary amounts of stack space.
79 pub struct RegionMaps {
80 scope_map: RefCell<NodeMap<ast::NodeId>>,
81 var_map: RefCell<NodeMap<ast::NodeId>>,
82 free_region_map: RefCell<HashMap<FreeRegion, Vec<FreeRegion>>>,
83 rvalue_scopes: RefCell<NodeMap<ast::NodeId>>,
84 terminating_scopes: RefCell<HashSet<ast::NodeId>>,
88 var_parent: Option<ast::NodeId>,
90 // Innermost enclosing expression
91 parent: Option<ast::NodeId>,
94 struct RegionResolutionVisitor<'a> {
98 region_maps: &'a RegionMaps,
105 pub fn relate_free_regions(&self, sub: FreeRegion, sup: FreeRegion) {
106 match self.free_region_map.borrow_mut().find_mut(&sub) {
108 if !sups.iter().any(|x| x == &sup) {
116 debug!("relate_free_regions(sub={:?}, sup={:?})", sub, sup);
117 self.free_region_map.borrow_mut().insert(sub, vec!(sup));
120 pub fn record_encl_scope(&self, sub: ast::NodeId, sup: ast::NodeId) {
121 debug!("record_encl_scope(sub={}, sup={})", sub, sup);
123 self.scope_map.borrow_mut().insert(sub, sup);
126 pub fn record_var_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) {
127 debug!("record_var_scope(sub={}, sup={})", var, lifetime);
128 assert!(var != lifetime);
129 self.var_map.borrow_mut().insert(var, lifetime);
132 pub fn record_rvalue_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) {
133 debug!("record_rvalue_scope(sub={}, sup={})", var, lifetime);
134 assert!(var != lifetime);
135 self.rvalue_scopes.borrow_mut().insert(var, lifetime);
138 pub fn mark_as_terminating_scope(&self, scope_id: ast::NodeId) {
140 * Records that a scope is a TERMINATING SCOPE. Whenever we
141 * create automatic temporaries -- e.g. by an
142 * expression like `a().f` -- they will be freed within
143 * the innermost terminating scope.
146 debug!("record_terminating_scope(scope_id={})", scope_id);
147 self.terminating_scopes.borrow_mut().insert(scope_id);
150 pub fn opt_encl_scope(&self, id: ast::NodeId) -> Option<ast::NodeId> {
151 //! Returns the narrowest scope that encloses `id`, if any.
152 self.scope_map.borrow().find(&id).map(|x| *x)
155 #[allow(dead_code)] // used in middle::cfg
156 pub fn encl_scope(&self, id: ast::NodeId) -> ast::NodeId {
157 //! Returns the narrowest scope that encloses `id`, if any.
158 match self.scope_map.borrow().find(&id) {
160 None => { fail!("no enclosing scope for id {}", id); }
164 pub fn var_scope(&self, var_id: ast::NodeId) -> ast::NodeId {
166 * Returns the lifetime of the local variable `var_id`
168 match self.var_map.borrow().find(&var_id) {
170 None => { fail!("no enclosing scope for id {}", var_id); }
174 pub fn temporary_scope(&self, expr_id: ast::NodeId) -> Option<ast::NodeId> {
175 //! Returns the scope when temp created by expr_id will be cleaned up
177 // check for a designated rvalue scope
178 match self.rvalue_scopes.borrow().find(&expr_id) {
180 debug!("temporary_scope({}) = {} [custom]", expr_id, s);
186 // else, locate the innermost terminating scope
187 // if there's one. Static items, for instance, won't
188 // have an enclosing scope, hence no scope will be
190 let mut id = match self.opt_encl_scope(expr_id) {
192 None => { return None; }
195 while !self.terminating_scopes.borrow().contains(&id) {
196 match self.opt_encl_scope(id) {
201 debug!("temporary_scope({}) = None", expr_id);
206 debug!("temporary_scope({}) = {} [enclosing]", expr_id, id);
210 pub fn var_region(&self, id: ast::NodeId) -> ty::Region {
211 //! Returns the lifetime of the variable `id`.
213 let scope = ty::ReScope(self.var_scope(id));
214 debug!("var_region({}) = {:?}", id, scope);
218 pub fn scopes_intersect(&self, scope1: ast::NodeId, scope2: ast::NodeId)
220 self.is_subscope_of(scope1, scope2) ||
221 self.is_subscope_of(scope2, scope1)
224 pub fn is_subscope_of(&self,
225 subscope: ast::NodeId,
226 superscope: ast::NodeId)
229 * Returns true if `subscope` is equal to or is lexically
230 * nested inside `superscope` and false otherwise.
233 let mut s = subscope;
234 while superscope != s {
235 match self.scope_map.borrow().find(&s) {
237 debug!("is_subscope_of({}, {}, s={})=false",
238 subscope, superscope, s);
242 Some(&scope) => s = scope
246 debug!("is_subscope_of({}, {})=true",
247 subscope, superscope);
252 pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool {
254 * Determines whether two free regions have a subregion relationship
255 * by walking the graph encoded in `free_region_map`. Note that
256 * it is possible that `sub != sup` and `sub <= sup` and `sup <= sub`
257 * (that is, the user can give two different names to the same lifetime).
260 can_reach(&*self.free_region_map.borrow(), sub, sup)
263 pub fn is_subregion_of(&self,
264 sub_region: ty::Region,
265 super_region: ty::Region)
268 * Determines whether one region is a subregion of another. This is
269 * intended to run *after inference* and sadly the logic is somewhat
270 * duplicated with the code in infer.rs.
273 debug!("is_subregion_of(sub_region={:?}, super_region={:?})",
274 sub_region, super_region);
276 sub_region == super_region || {
277 match (sub_region, super_region) {
279 (_, ty::ReStatic) => {
283 (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
284 self.is_subscope_of(sub_scope, super_scope)
287 (ty::ReScope(sub_scope), ty::ReFree(ref fr)) => {
288 self.is_subscope_of(sub_scope, fr.scope_id)
291 (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => {
292 self.sub_free_region(sub_fr, super_fr)
295 (ty::ReEarlyBound(param_id_a, param_space_a, index_a, _),
296 ty::ReEarlyBound(param_id_b, param_space_b, index_b, _)) => {
297 // This case is used only to make sure that explicitly-
298 // specified `Self` types match the real self type in
300 param_id_a == param_id_b &&
301 param_space_a == param_space_b &&
312 pub fn nearest_common_ancestor(&self,
313 scope_a: ast::NodeId,
314 scope_b: ast::NodeId)
315 -> Option<ast::NodeId> {
317 * Finds the nearest common ancestor (if any) of two scopes. That
318 * is, finds the smallest scope which is greater than or equal to
319 * both `scope_a` and `scope_b`.
322 if scope_a == scope_b { return Some(scope_a); }
324 let a_ancestors = ancestors_of(self, scope_a);
325 let b_ancestors = ancestors_of(self, scope_b);
326 let mut a_index = a_ancestors.len() - 1u;
327 let mut b_index = b_ancestors.len() - 1u;
329 // Here, ~[ab]_ancestors is a vector going from narrow to broad.
330 // The end of each vector will be the item where the scope is
331 // defined; if there are any common ancestors, then the tails of
332 // the vector will be the same. So basically we want to walk
333 // backwards from the tail of each vector and find the first point
334 // where they diverge. If one vector is a suffix of the other,
335 // then the corresponding scope is a superscope of the other.
337 if *a_ancestors.get(a_index) != *b_ancestors.get(b_index) {
342 // Loop invariant: a_ancestors[a_index] == b_ancestors[b_index]
343 // for all indices between a_index and the end of the array
344 if a_index == 0u { return Some(scope_a); }
345 if b_index == 0u { return Some(scope_b); }
348 if *a_ancestors.get(a_index) != *b_ancestors.get(b_index) {
349 return Some(*a_ancestors.get(a_index + 1u));
353 fn ancestors_of(this: &RegionMaps, scope: ast::NodeId)
354 -> Vec<ast::NodeId> {
355 // debug!("ancestors_of(scope={})", scope);
356 let mut result = vec!(scope);
357 let mut scope = scope;
359 match this.scope_map.borrow().find(&scope) {
360 None => return result,
361 Some(&superscope) => {
362 result.push(superscope);
366 // debug!("ancestors_of_loop(scope={})", scope);
372 /// Records the current parent (if any) as the parent of `child_id`.
373 fn record_superlifetime(visitor: &mut RegionResolutionVisitor,
374 child_id: ast::NodeId,
376 match visitor.cx.parent {
378 visitor.region_maps.record_encl_scope(child_id, parent_id);
384 /// Records the lifetime of a local variable as `cx.var_parent`
385 fn record_var_lifetime(visitor: &mut RegionResolutionVisitor,
388 match visitor.cx.var_parent {
390 visitor.region_maps.record_var_scope(var_id, parent_id);
393 // this can happen in extern fn declarations like
395 // extern fn isalnum(c: c_int) -> c_int
400 fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
401 debug!("resolve_block(blk.id={})", blk.id);
403 // Record the parent of this block.
404 record_superlifetime(visitor, blk.id, blk.span);
406 // We treat the tail expression in the block (if any) somewhat
407 // differently from the statements. The issue has to do with
408 // temporary lifetimes. If the user writes:
415 let prev_cx = visitor.cx;
416 visitor.cx = Context {var_parent: Some(blk.id), parent: Some(blk.id)};
417 visit::walk_block(visitor, blk);
418 visitor.cx = prev_cx;
421 fn resolve_arm(visitor: &mut RegionResolutionVisitor, arm: &ast::Arm) {
422 visitor.region_maps.mark_as_terminating_scope(arm.body.id);
426 visitor.region_maps.mark_as_terminating_scope(expr.id);
431 visit::walk_arm(visitor, arm);
434 fn resolve_pat(visitor: &mut RegionResolutionVisitor, pat: &ast::Pat) {
435 record_superlifetime(visitor, pat.id, pat.span);
437 // If this is a binding (or maybe a binding, I'm too lazy to check
438 // the def map) then record the lifetime of that binding.
440 ast::PatIdent(..) => {
441 record_var_lifetime(visitor, pat.id, pat.span);
446 visit::walk_pat(visitor, pat);
449 fn resolve_stmt(visitor: &mut RegionResolutionVisitor, stmt: &ast::Stmt) {
450 let stmt_id = stmt_id(stmt);
451 debug!("resolve_stmt(stmt.id={})", stmt_id);
453 visitor.region_maps.mark_as_terminating_scope(stmt_id);
454 record_superlifetime(visitor, stmt_id, stmt.span);
456 let prev_parent = visitor.cx.parent;
457 visitor.cx.parent = Some(stmt_id);
458 visit::walk_stmt(visitor, stmt);
459 visitor.cx.parent = prev_parent;
462 fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
463 debug!("resolve_expr(expr.id={})", expr.id);
465 record_superlifetime(visitor, expr.id, expr.span);
467 let prev_cx = visitor.cx;
468 visitor.cx.parent = Some(expr.id);
470 // Conditional or repeating scopes are always terminating
471 // scopes, meaning that temporaries cannot outlive them.
472 // This ensures fixed size stacks.
474 ast::ExprBinary(ast::BiAnd, _, ref r) |
475 ast::ExprBinary(ast::BiOr, _, ref r) => {
476 // For shortcircuiting operators, mark the RHS as a terminating
477 // scope since it only executes conditionally.
478 visitor.region_maps.mark_as_terminating_scope(r.id);
481 ast::ExprIf(_, ref then, Some(ref otherwise)) => {
482 visitor.region_maps.mark_as_terminating_scope(then.id);
483 visitor.region_maps.mark_as_terminating_scope(otherwise.id);
486 ast::ExprIf(ref expr, ref then, None) => {
487 visitor.region_maps.mark_as_terminating_scope(expr.id);
488 visitor.region_maps.mark_as_terminating_scope(then.id);
491 ast::ExprLoop(ref body, _) => {
492 visitor.region_maps.mark_as_terminating_scope(body.id);
495 ast::ExprWhile(ref expr, ref body, _) => {
496 visitor.region_maps.mark_as_terminating_scope(expr.id);
497 visitor.region_maps.mark_as_terminating_scope(body.id);
500 ast::ExprForLoop(ref _pat, ref _head, ref body, _) => {
501 visitor.region_maps.mark_as_terminating_scope(body.id);
503 // The variable parent of everything inside (most importantly, the
504 // pattern) is the body.
505 visitor.cx.var_parent = Some(body.id);
508 ast::ExprMatch(..) => {
509 visitor.cx.var_parent = Some(expr.id);
512 ast::ExprAssignOp(..) | ast::ExprIndex(..) |
513 ast::ExprUnary(..) | ast::ExprCall(..) | ast::ExprMethodCall(..) => {
514 // FIXME(#6268) Nested method calls
516 // The lifetimes for a call or method call look as follows:
524 // The idea is that call.callee_id represents *the time when
525 // the invoked function is actually running* and call.id
526 // represents *the time to prepare the arguments and make the
527 // call*. See the section "Borrows in Calls" borrowck/doc.rs
528 // for an extended explanation of why this distinction is
531 // record_superlifetime(new_cx, expr.callee_id);
537 visit::walk_expr(visitor, expr);
538 visitor.cx = prev_cx;
541 fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local) {
542 debug!("resolve_local(local.id={},local.init={})",
543 local.id,local.init.is_some());
545 let blk_id = match visitor.cx.var_parent {
548 visitor.sess.span_bug(
550 "local without enclosing block");
554 // For convenience in trans, associate with the local-id the var
555 // scope that will be used for any bindings declared in this
557 visitor.region_maps.record_var_scope(local.id, blk_id);
559 // As an exception to the normal rules governing temporary
560 // lifetimes, initializers in a let have a temporary lifetime
561 // of the enclosing block. This means that e.g. a program
562 // like the following is legal:
564 // let ref x = HashMap::new();
566 // Because the hash map will be freed in the enclosing block.
568 // We express the rules more formally based on 3 grammars (defined
569 // fully in the helpers below that implement them):
571 // 1. `E&`, which matches expressions like `&<rvalue>` that
572 // own a pointer into the stack.
574 // 2. `P&`, which matches patterns like `ref x` or `(ref x, ref
575 // y)` that produce ref bindings into the value they are
576 // matched against or something (at least partially) owned by
577 // the value they are matched against. (By partially owned,
578 // I mean that creating a binding into a ref-counted or managed value
579 // would still count.)
581 // 3. `ET`, which matches both rvalues like `foo()` as well as lvalues
582 // based on rvalues like `foo().x[2].y`.
584 // A subexpression `<rvalue>` that appears in a let initializer
585 // `let pat [: ty] = expr` has an extended temporary lifetime if
586 // any of the following conditions are met:
588 // A. `pat` matches `P&` and `expr` matches `ET`
589 // (covers cases where `pat` creates ref bindings into an rvalue
590 // produced by `expr`)
591 // B. `ty` is a borrowed pointer and `expr` matches `ET`
592 // (covers cases where coercion creates a borrow)
593 // C. `expr` matches `E&`
594 // (covers cases `expr` borrows an rvalue that is then assigned
595 // to memory (at least partially) owned by the binding)
597 // Here are some examples hopefully giving an intuition where each
598 // rule comes into play and why:
600 // Rule A. `let (ref x, ref y) = (foo().x, 44)`. The rvalue `(22, 44)`
601 // would have an extended lifetime, but not `foo()`.
603 // Rule B. `let x: &[...] = [foo().x]`. The rvalue `[foo().x]`
604 // would have an extended lifetime, but not `foo()`.
606 // Rule C. `let x = &foo().x`. The rvalue ``foo()` would have extended
609 // In some cases, multiple rules may apply (though not to the same
610 // rvalue). For example:
612 // let ref x = [&a(), &b()];
614 // Here, the expression `[...]` has an extended lifetime due to rule
615 // A, but the inner rvalues `a()` and `b()` have an extended lifetime
618 // FIXME(#6308) -- Note that `[]` patterns work more smoothly post-DST.
622 record_rvalue_scope_if_borrow_expr(visitor, &**expr, blk_id);
624 if is_binding_pat(&*local.pat) || is_borrowed_ty(&*local.ty) {
625 record_rvalue_scope(visitor, &**expr, blk_id);
632 visit::walk_local(visitor, local);
634 fn is_binding_pat(pat: &ast::Pat) -> bool {
636 * True if `pat` match the `P&` nonterminal:
639 * | StructName { ..., P&, ... }
640 * | VariantName(..., P&, ...)
647 ast::PatIdent(ast::BindByRef(_), _, _) => true,
649 ast::PatStruct(_, ref field_pats, _) => {
650 field_pats.iter().any(|fp| is_binding_pat(&*fp.pat))
653 ast::PatVec(ref pats1, ref pats2, ref pats3) => {
654 pats1.iter().any(|p| is_binding_pat(&**p)) ||
655 pats2.iter().any(|p| is_binding_pat(&**p)) ||
656 pats3.iter().any(|p| is_binding_pat(&**p))
659 ast::PatEnum(_, Some(ref subpats)) |
660 ast::PatTup(ref subpats) => {
661 subpats.iter().any(|p| is_binding_pat(&**p))
664 ast::PatBox(ref subpat) => {
665 is_binding_pat(&**subpat)
672 fn is_borrowed_ty(ty: &ast::Ty) -> bool {
674 * True if `ty` is a borrowed pointer type
675 * like `&int` or `&[...]`.
679 ast::TyRptr(..) => true,
684 fn record_rvalue_scope_if_borrow_expr(visitor: &mut RegionResolutionVisitor,
686 blk_id: ast::NodeId) {
688 * If `expr` matches the `E&` grammar, then records an extended
689 * rvalue scope as appropriate:
692 * | StructName { ..., f: E&, ... }
702 ast::ExprAddrOf(_, ref subexpr) => {
703 record_rvalue_scope_if_borrow_expr(visitor, &**subexpr, blk_id);
704 record_rvalue_scope(visitor, &**subexpr, blk_id);
706 ast::ExprStruct(_, ref fields, _) => {
707 for field in fields.iter() {
708 record_rvalue_scope_if_borrow_expr(
709 visitor, &*field.expr, blk_id);
712 ast::ExprVec(ref subexprs) |
713 ast::ExprTup(ref subexprs) => {
714 for subexpr in subexprs.iter() {
715 record_rvalue_scope_if_borrow_expr(
716 visitor, &**subexpr, blk_id);
719 ast::ExprUnary(ast::UnUniq, ref subexpr) => {
720 record_rvalue_scope_if_borrow_expr(visitor, &**subexpr, blk_id);
722 ast::ExprCast(ref subexpr, _) |
723 ast::ExprParen(ref subexpr) => {
724 record_rvalue_scope_if_borrow_expr(visitor, &**subexpr, blk_id)
726 ast::ExprBlock(ref block) => {
728 Some(ref subexpr) => {
729 record_rvalue_scope_if_borrow_expr(
730 visitor, &**subexpr, blk_id);
740 fn record_rvalue_scope<'a>(visitor: &mut RegionResolutionVisitor,
742 blk_id: ast::NodeId) {
744 * Applied to an expression `expr` if `expr` -- or something
745 * owned or partially owned by `expr` -- is going to be
746 * indirectly referenced by a variable in a let statement. In
747 * that case, the "temporary lifetime" or `expr` is extended
748 * to be the block enclosing the `let` statement.
750 * More formally, if `expr` matches the grammar `ET`, record
751 * the rvalue scope of the matching `<rvalue>` as `blk_id`:
759 * Note: ET is intended to match "rvalues or
760 * lvalues based on rvalues".
765 // Note: give all the expressions matching `ET` with the
766 // extended temporary lifetime, not just the innermost rvalue,
767 // because in trans if we must compile e.g. `*rvalue()`
768 // into a temporary, we request the temporary scope of the
770 visitor.region_maps.record_rvalue_scope(expr.id, blk_id);
773 ast::ExprAddrOf(_, ref subexpr) |
774 ast::ExprUnary(ast::UnDeref, ref subexpr) |
775 ast::ExprField(ref subexpr, _, _) |
776 ast::ExprTupField(ref subexpr, _, _) |
777 ast::ExprIndex(ref subexpr, _) |
778 ast::ExprParen(ref subexpr) => {
779 let subexpr: &'a P<Expr> = subexpr; // FIXME(#11586)
790 fn resolve_item(visitor: &mut RegionResolutionVisitor, item: &ast::Item) {
791 // Items create a new outer block scope as far as we're concerned.
792 let prev_cx = visitor.cx;
793 visitor.cx = Context {var_parent: None, parent: None};
794 visit::walk_item(visitor, item);
795 visitor.cx = prev_cx;
798 fn resolve_fn(visitor: &mut RegionResolutionVisitor,
804 debug!("region::resolve_fn(id={}, \
809 visitor.sess.codemap().span_to_string(sp),
813 visitor.region_maps.mark_as_terminating_scope(body.id);
815 let outer_cx = visitor.cx;
817 // The arguments and `self` are parented to the body of the fn.
818 visitor.cx = Context { parent: Some(body.id),
819 var_parent: Some(body.id) };
820 visit::walk_fn_decl(visitor, decl);
822 // The body of the fn itself is either a root scope (top-level fn)
823 // or it continues with the inherited scope (closures).
825 visit::FkItemFn(..) | visit::FkMethod(..) => {
826 visitor.cx = Context { parent: None, var_parent: None };
827 visitor.visit_block(body);
828 visitor.cx = outer_cx;
830 visit::FkFnBlock(..) => {
831 // FIXME(#3696) -- at present we are place the closure body
832 // within the region hierarchy exactly where it appears lexically.
833 // This is wrong because the closure may live longer
834 // than the enclosing expression. We should probably fix this,
835 // but the correct fix is a bit subtle, and I am also not sure
836 // that the present approach is unsound -- it may not permit
837 // any illegal programs. See issue for more details.
838 visitor.cx = outer_cx;
839 visitor.visit_block(body);
844 impl<'a, 'v> Visitor<'v> for RegionResolutionVisitor<'a> {
846 fn visit_block(&mut self, b: &Block) {
847 resolve_block(self, b);
850 fn visit_item(&mut self, i: &Item) {
851 resolve_item(self, i);
854 fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
855 b: &'v Block, s: Span, n: NodeId) {
856 resolve_fn(self, fk, fd, b, s, n);
858 fn visit_arm(&mut self, a: &Arm) {
859 resolve_arm(self, a);
861 fn visit_pat(&mut self, p: &Pat) {
862 resolve_pat(self, p);
864 fn visit_stmt(&mut self, s: &Stmt) {
865 resolve_stmt(self, s);
867 fn visit_expr(&mut self, ex: &Expr) {
868 resolve_expr(self, ex);
870 fn visit_local(&mut self, l: &Local) {
871 resolve_local(self, l);
875 pub fn resolve_crate(sess: &Session, krate: &ast::Crate) -> RegionMaps {
876 let maps = RegionMaps {
877 scope_map: RefCell::new(NodeMap::new()),
878 var_map: RefCell::new(NodeMap::new()),
879 free_region_map: RefCell::new(HashMap::new()),
880 rvalue_scopes: RefCell::new(NodeMap::new()),
881 terminating_scopes: RefCell::new(HashSet::new()),
884 let mut visitor = RegionResolutionVisitor {
887 cx: Context { parent: None, var_parent: None }
889 visit::walk_crate(&mut visitor, krate);
894 pub fn resolve_inlined_item(sess: &Session,
895 region_maps: &RegionMaps,
896 item: &ast::InlinedItem) {
897 let mut visitor = RegionResolutionVisitor {
899 region_maps: region_maps,
900 cx: Context { parent: None, var_parent: None }
902 visit::walk_inlined_item(&mut visitor, item);