1 // Copyright 2012 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;
29 use std::cell::RefCell;
30 use collections::{HashMap, HashSet};
31 use syntax::codemap::Span;
32 use syntax::{ast, visit};
33 use syntax::visit::{Visitor, FnKind};
34 use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
35 use syntax::ast_util::{stmt_id};
38 The region maps encode information about region relationships.
40 - `scope_map` maps from a scope id to the enclosing scope id; this is
41 usually corresponding to the lexical nesting, though in the case of
42 closures the parent scope is the innermost conditinal expression or repeating
45 - `var_map` maps from a variable or binding id to the block in which
46 that variable is declared.
48 - `free_region_map` maps from a free region `a` to a list of free
49 regions `bs` such that `a <= b for all b in bs`
50 - the free region map is populated during type check as we check
51 each function. See the function `relate_free_regions` for
54 - `rvalue_scopes` includes entries for those expressions whose cleanup
55 scope is larger than the default. The map goes from the expression
56 id to the cleanup scope id. For rvalues not present in this table,
57 the appropriate cleanup scope is the innermost enclosing statement,
58 conditional expression, or repeating block (see `terminating_scopes`).
60 - `terminating_scopes` is a set containing the ids of each statement,
61 or conditional/repeating expression. These scopes are calling "terminating
62 scopes" because, when attempting to find the scope of a temporary, by
63 default we search up the enclosing scopes until we encounter the
64 terminating scope. A conditional/repeating
65 expression is one which is not guaranteed to execute exactly once
66 upon entering the parent scope. This could be because the expression
67 only executes conditionally, such as the expression `b` in `a && b`,
68 or because the expression may execute many times, such as a loop
69 body. The reason that we distinguish such expressions is that, upon
70 exiting the parent scope, we cannot statically know how many times
71 the expression executed, and thus if the expression creates
72 temporaries we cannot know statically how many such temporaries we
73 would have to cleanup. Therefore we ensure that the temporaries never
74 outlast the conditional/repeating expression, preventing the need
75 for dynamic checks and/or arbitrary amounts of stack space.
77 pub struct RegionMaps {
78 priv scope_map: RefCell<NodeMap<ast::NodeId>>,
79 priv var_map: RefCell<NodeMap<ast::NodeId>>,
80 priv free_region_map: RefCell<HashMap<FreeRegion, ~[FreeRegion]>>,
81 priv rvalue_scopes: RefCell<NodeMap<ast::NodeId>>,
82 priv terminating_scopes: RefCell<HashSet<ast::NodeId>>,
87 var_parent: Option<ast::NodeId>,
89 // Innermost enclosing expression
90 parent: Option<ast::NodeId>,
93 struct RegionResolutionVisitor<'a> {
97 region_maps: &'a RegionMaps,
102 pub fn relate_free_regions(&self, sub: FreeRegion, sup: FreeRegion) {
103 let mut free_region_map = self.free_region_map.borrow_mut();
104 match free_region_map.get().find_mut(&sub) {
106 if !sups.iter().any(|x| x == &sup) {
114 debug!("relate_free_regions(sub={:?}, sup={:?})", sub, sup);
116 free_region_map.get().insert(sub, ~[sup]);
119 pub fn record_encl_scope(&self, sub: ast::NodeId, sup: ast::NodeId) {
120 debug!("record_encl_scope(sub={}, sup={})", sub, sup);
123 let mut scope_map = self.scope_map.borrow_mut();
124 scope_map.get().insert(sub, sup);
127 pub fn record_var_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) {
128 debug!("record_var_scope(sub={}, sup={})", var, lifetime);
129 assert!(var != lifetime);
131 let mut var_map = self.var_map.borrow_mut();
132 var_map.get().insert(var, lifetime);
135 pub fn record_rvalue_scope(&self, var: ast::NodeId, lifetime: ast::NodeId) {
136 debug!("record_rvalue_scope(sub={}, sup={})", var, lifetime);
137 assert!(var != lifetime);
139 let mut rvalue_scopes = self.rvalue_scopes.borrow_mut();
140 rvalue_scopes.get().insert(var, lifetime);
143 pub fn mark_as_terminating_scope(&self, scope_id: ast::NodeId) {
145 * Records that a scope is a TERMINATING SCOPE. Whenever we
146 * create automatic temporaries -- e.g. by an
147 * expression like `a().f` -- they will be freed within
148 * the innermost terminating scope.
151 debug!("record_terminating_scope(scope_id={})", scope_id);
152 let mut terminating_scopes = self.terminating_scopes.borrow_mut();
153 terminating_scopes.get().insert(scope_id);
156 pub fn opt_encl_scope(&self, id: ast::NodeId) -> Option<ast::NodeId> {
157 //! Returns the narrowest scope that encloses `id`, if any.
159 let scope_map = self.scope_map.borrow();
160 scope_map.get().find(&id).map(|x| *x)
163 pub fn encl_scope(&self, id: ast::NodeId) -> ast::NodeId {
164 //! Returns the narrowest scope that encloses `id`, if any.
166 let scope_map = self.scope_map.borrow();
167 match scope_map.get().find(&id) {
169 None => { fail!("no enclosing scope for id {}", id); }
173 pub fn var_scope(&self, var_id: ast::NodeId) -> ast::NodeId {
175 * Returns the lifetime of the local variable `var_id`
178 let var_map = self.var_map.borrow();
179 match var_map.get().find(&var_id) {
181 None => { fail!("no enclosing scope for id {}", var_id); }
185 pub fn temporary_scope(&self, expr_id: ast::NodeId) -> Option<ast::NodeId> {
186 //! Returns the scope when temp created by expr_id will be cleaned up
188 // check for a designated rvalue scope
189 let rvalue_scopes = self.rvalue_scopes.borrow();
190 match rvalue_scopes.get().find(&expr_id) {
192 debug!("temporary_scope({}) = {} [custom]", expr_id, s);
198 // else, locate the innermost terminating scope
199 // if there's one. Static items, for instance, won't
200 // have an enclusing scope, hence no scope will be
202 let mut id = match self.opt_encl_scope(expr_id) {
204 None => { return None; }
207 let terminating_scopes = self.terminating_scopes.borrow();
208 while !terminating_scopes.get().contains(&id) {
209 match self.opt_encl_scope(id) {
214 debug!("temporary_scope({}) = None", expr_id);
219 debug!("temporary_scope({}) = {} [enclosing]", expr_id, id);
223 pub fn encl_region(&self, id: ast::NodeId) -> ty::Region {
224 //! Returns the narrowest scope region that encloses `id`, if any.
226 ty::ReScope(self.encl_scope(id))
229 pub fn var_region(&self, id: ast::NodeId) -> ty::Region {
230 //! Returns the lifetime of the variable `id`.
232 ty::ReScope(self.var_scope(id))
235 pub fn scopes_intersect(&self, scope1: ast::NodeId, scope2: ast::NodeId)
237 self.is_subscope_of(scope1, scope2) ||
238 self.is_subscope_of(scope2, scope1)
241 pub fn is_subscope_of(&self,
242 subscope: ast::NodeId,
243 superscope: ast::NodeId)
246 * Returns true if `subscope` is equal to or is lexically
247 * nested inside `superscope` and false otherwise.
250 let mut s = subscope;
251 while superscope != s {
252 let scope_map = self.scope_map.borrow();
253 match scope_map.get().find(&s) {
255 debug!("is_subscope_of({}, {}, s={})=false",
256 subscope, superscope, s);
260 Some(&scope) => s = scope
264 debug!("is_subscope_of({}, {})=true",
265 subscope, superscope);
270 pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool {
272 * Determines whether two free regions have a subregion relationship
273 * by walking the graph encoded in `free_region_map`. Note that
274 * it is possible that `sub != sup` and `sub <= sup` and `sup <= sub`
275 * (that is, the user can give two different names to the same lifetime).
282 // Do a little breadth-first-search here. The `queue` list
283 // doubles as a way to detect if we've seen a particular FR
284 // before. Note that we expect this graph to be an *extremely
286 let mut queue = ~[sub];
288 while i < queue.len() {
289 let free_region_map = self.free_region_map.borrow();
290 match free_region_map.get().find(&queue[i]) {
292 for parent in parents.iter() {
297 if !queue.iter().any(|x| x == parent) {
309 pub fn is_subregion_of(&self,
310 sub_region: ty::Region,
311 super_region: ty::Region)
314 * Determines whether one region is a subregion of another. This is
315 * intended to run *after inference* and sadly the logic is somewhat
316 * duplicated with the code in infer.rs.
319 debug!("is_subregion_of(sub_region={:?}, super_region={:?})",
320 sub_region, super_region);
322 sub_region == super_region || {
323 match (sub_region, super_region) {
324 (_, ty::ReStatic) => {
328 (ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
329 self.is_subscope_of(sub_scope, super_scope)
332 (ty::ReScope(sub_scope), ty::ReFree(ref fr)) => {
333 self.is_subscope_of(sub_scope, fr.scope_id)
336 (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => {
337 self.sub_free_region(sub_fr, super_fr)
347 pub fn nearest_common_ancestor(&self,
348 scope_a: ast::NodeId,
349 scope_b: ast::NodeId)
350 -> Option<ast::NodeId> {
352 * Finds the nearest common ancestor (if any) of two scopes. That
353 * is, finds the smallest scope which is greater than or equal to
354 * both `scope_a` and `scope_b`.
357 if scope_a == scope_b { return Some(scope_a); }
359 let a_ancestors = ancestors_of(self, scope_a);
360 let b_ancestors = ancestors_of(self, scope_b);
361 let mut a_index = a_ancestors.len() - 1u;
362 let mut b_index = b_ancestors.len() - 1u;
364 // Here, ~[ab]_ancestors is a vector going from narrow to broad.
365 // The end of each vector will be the item where the scope is
366 // defined; if there are any common ancestors, then the tails of
367 // the vector will be the same. So basically we want to walk
368 // backwards from the tail of each vector and find the first point
369 // where they diverge. If one vector is a suffix of the other,
370 // then the corresponding scope is a superscope of the other.
372 if a_ancestors[a_index] != b_ancestors[b_index] {
377 // Loop invariant: a_ancestors[a_index] == b_ancestors[b_index]
378 // for all indices between a_index and the end of the array
379 if a_index == 0u { return Some(scope_a); }
380 if b_index == 0u { return Some(scope_b); }
383 if a_ancestors[a_index] != b_ancestors[b_index] {
384 return Some(a_ancestors[a_index + 1u]);
388 fn ancestors_of(this: &RegionMaps, scope: ast::NodeId)
391 // debug!("ancestors_of(scope={})", scope);
392 let mut result = ~[scope];
393 let mut scope = scope;
395 let scope_map = this.scope_map.borrow();
396 match scope_map.get().find(&scope) {
397 None => return result,
398 Some(&superscope) => {
399 result.push(superscope);
403 // debug!("ancestors_of_loop(scope={})", scope);
409 /// Records the current parent (if any) as the parent of `child_id`.
410 fn record_superlifetime(visitor: &mut RegionResolutionVisitor,
412 child_id: ast::NodeId,
414 for &parent_id in cx.parent.iter() {
415 visitor.region_maps.record_encl_scope(child_id, parent_id);
419 /// Records the lifetime of a local variable as `cx.var_parent`
420 fn record_var_lifetime(visitor: &mut RegionResolutionVisitor,
424 match cx.var_parent {
426 visitor.region_maps.record_var_scope(var_id, parent_id);
429 // this can happen in extern fn declarations like
431 // extern fn isalnum(c: c_int) -> c_int
436 fn resolve_block(visitor: &mut RegionResolutionVisitor,
439 debug!("resolve_block(blk.id={})", blk.id);
441 // Record the parent of this block.
442 record_superlifetime(visitor, cx, blk.id, blk.span);
444 // We treat the tail expression in the block (if any) somewhat
445 // differently from the statements. The issue has to do with
446 // temporary lifetimes. If the user writes:
453 let subcx = Context {var_parent: Some(blk.id), parent: Some(blk.id)};
454 visit::walk_block(visitor, blk, subcx);
457 fn resolve_arm(visitor: &mut RegionResolutionVisitor,
460 visitor.region_maps.mark_as_terminating_scope(arm.body.id);
464 visitor.region_maps.mark_as_terminating_scope(expr.id);
469 visit::walk_arm(visitor, arm, cx);
472 fn resolve_pat(visitor: &mut RegionResolutionVisitor,
475 record_superlifetime(visitor, cx, pat.id, pat.span);
477 // If this is a binding (or maybe a binding, I'm too lazy to check
478 // the def map) then record the lifetime of that binding.
480 ast::PatIdent(..) => {
481 record_var_lifetime(visitor, cx, pat.id, pat.span);
486 visit::walk_pat(visitor, pat, cx);
489 fn resolve_stmt(visitor: &mut RegionResolutionVisitor,
492 let stmt_id = stmt_id(stmt);
493 debug!("resolve_stmt(stmt.id={})", stmt_id);
495 visitor.region_maps.mark_as_terminating_scope(stmt_id);
496 record_superlifetime(visitor, cx, stmt_id, stmt.span);
498 let subcx = Context {parent: Some(stmt_id), ..cx};
499 visit::walk_stmt(visitor, stmt, subcx);
502 fn resolve_expr(visitor: &mut RegionResolutionVisitor,
505 debug!("resolve_expr(expr.id={})", expr.id);
507 record_superlifetime(visitor, cx, expr.id, expr.span);
510 new_cx.parent = Some(expr.id);
512 // Conditional or repeating scopes are always terminating
513 // scopes, meaning that temporaries cannot outlive them.
514 // This ensures fixed size stacks.
516 ast::ExprBinary(ast::BiAnd, _, r) |
517 ast::ExprBinary(ast::BiOr, _, r) => {
518 // For shortcircuiting operators, mark the RHS as a terminating
519 // scope since it only executes conditionally.
520 visitor.region_maps.mark_as_terminating_scope(r.id);
523 ast::ExprIf(_, then, Some(otherwise)) => {
524 visitor.region_maps.mark_as_terminating_scope(then.id);
525 visitor.region_maps.mark_as_terminating_scope(otherwise.id);
528 ast::ExprIf(expr, then, None) => {
529 visitor.region_maps.mark_as_terminating_scope(expr.id);
530 visitor.region_maps.mark_as_terminating_scope(then.id);
533 ast::ExprLoop(body, _) => {
534 visitor.region_maps.mark_as_terminating_scope(body.id);
537 ast::ExprWhile(expr, body) => {
538 visitor.region_maps.mark_as_terminating_scope(expr.id);
539 visitor.region_maps.mark_as_terminating_scope(body.id);
542 ast::ExprMatch(..) => {
543 new_cx.var_parent = Some(expr.id);
546 ast::ExprAssignOp(..) | ast::ExprIndex(..) |
547 ast::ExprUnary(..) | ast::ExprCall(..) | ast::ExprMethodCall(..) => {
548 // FIXME(#6268) Nested method calls
550 // The lifetimes for a call or method call look as follows:
558 // The idea is that call.callee_id represents *the time when
559 // the invoked function is actually running* and call.id
560 // represents *the time to prepare the arguments and make the
561 // call*. See the section "Borrows in Calls" borrowck/doc.rs
562 // for an extended explanantion of why this distinction is
565 // record_superlifetime(new_cx, expr.callee_id);
572 visit::walk_expr(visitor, expr, new_cx);
575 fn resolve_local(visitor: &mut RegionResolutionVisitor,
578 debug!("resolve_local(local.id={},local.init={})",
579 local.id,local.init.is_some());
581 let blk_id = match cx.var_parent {
584 visitor.sess.span_bug(
586 "local without enclosing block");
590 // For convenience in trans, associate with the local-id the var
591 // scope that will be used for any bindings declared in this
593 visitor.region_maps.record_var_scope(local.id, blk_id);
595 // As an exception to the normal rules governing temporary
596 // lifetimes, initializers in a let have a temporary lifetime
597 // of the enclosing block. This means that e.g. a program
598 // like the following is legal:
600 // let ref x = HashMap::new();
602 // Because the hash map will be freed in the enclosing block.
604 // We express the rules more formally based on 3 grammars (defined
605 // fully in the helpers below that implement them):
607 // 1. `E&`, which matches expressions like `&<rvalue>` that
608 // own a pointer into the stack.
610 // 2. `P&`, which matches patterns like `ref x` or `(ref x, ref
611 // y)` that produce ref bindings into the value they are
612 // matched against or something (at least partially) owned by
613 // the value they are matched against. (By partially owned,
614 // I mean that creating a binding into a ref-counted or managed value
615 // would still count.)
617 // 3. `ET`, which matches both rvalues like `foo()` as well as lvalues
618 // based on rvalues like `foo().x[2].y`.
620 // A subexpression `<rvalue>` that appears in a let initializer
621 // `let pat [: ty] = expr` has an extended temporary lifetime if
622 // any of the following conditions are met:
624 // A. `pat` matches `P&` and `expr` matches `ET`
625 // (covers cases where `pat` creates ref bindings into an rvalue
626 // produced by `expr`)
627 // B. `ty` is a borrowed pointer and `expr` matches `ET`
628 // (covers cases where coercion creates a borrow)
629 // C. `expr` matches `E&`
630 // (covers cases `expr` borrows an rvalue that is then assigned
631 // to memory (at least partially) owned by the binding)
633 // Here are some examples hopefully giving an intution where each
634 // rule comes into play and why:
636 // Rule A. `let (ref x, ref y) = (foo().x, 44)`. The rvalue `(22, 44)`
637 // would have an extended lifetime, but not `foo()`.
639 // Rule B. `let x: &[...] = [foo().x]`. The rvalue `[foo().x]`
640 // would have an extended lifetime, but not `foo()`.
642 // Rule C. `let x = &foo().x`. The rvalue ``foo()` would have extended
645 // In some cases, multiple rules may apply (though not to the same
646 // rvalue). For example:
648 // let ref x = [&a(), &b()];
650 // Here, the expression `[...]` has an extended lifetime due to rule
651 // A, but the inner rvalues `a()` and `b()` have an extended lifetime
654 // FIXME(#6308) -- Note that `[]` patterns work more smoothly post-DST.
658 record_rvalue_scope_if_borrow_expr(visitor, expr, blk_id);
660 if is_binding_pat(local.pat) || is_borrowed_ty(local.ty) {
661 record_rvalue_scope(visitor, expr, blk_id);
668 visit::walk_local(visitor, local, cx);
670 fn is_binding_pat(pat: &ast::Pat) -> bool {
672 * True if `pat` match the `P&` nonterminal:
675 * | StructName { ..., P&, ... }
676 * | VariantName(..., P&, ...)
684 ast::PatIdent(ast::BindByRef(_), _, _) => true,
686 ast::PatStruct(_, ref field_pats, _) => {
687 field_pats.iter().any(|fp| is_binding_pat(fp.pat))
690 ast::PatVec(ref pats1, ref pats2, ref pats3) => {
691 pats1.iter().any(|&p| is_binding_pat(p)) ||
692 pats2.iter().any(|&p| is_binding_pat(p)) ||
693 pats3.iter().any(|&p| is_binding_pat(p))
696 ast::PatEnum(_, Some(ref subpats)) |
697 ast::PatTup(ref subpats) => {
698 subpats.iter().any(|&p| is_binding_pat(p))
701 ast::PatUniq(subpat) => {
702 is_binding_pat(subpat)
709 fn is_borrowed_ty(ty: &ast::Ty) -> bool {
711 * True if `ty` is a borrowed pointer type
712 * like `&int` or `&[...]`.
716 ast::TyRptr(..) => true,
721 fn record_rvalue_scope_if_borrow_expr(visitor: &mut RegionResolutionVisitor,
723 blk_id: ast::NodeId) {
725 * If `expr` matches the `E&` grammar, then records an extended
726 * rvalue scope as appropriate:
729 * | StructName { ..., f: E&, ... }
739 ast::ExprAddrOf(_, subexpr) => {
740 record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
741 record_rvalue_scope(visitor, subexpr, blk_id);
743 ast::ExprStruct(_, ref fields, _) => {
744 for field in fields.iter() {
745 record_rvalue_scope_if_borrow_expr(
746 visitor, field.expr, blk_id);
749 ast::ExprVstore(subexpr, _) => {
750 visitor.region_maps.record_rvalue_scope(subexpr.id, blk_id);
751 record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
753 ast::ExprVec(ref subexprs, _) |
754 ast::ExprTup(ref subexprs) => {
755 for &subexpr in subexprs.iter() {
756 record_rvalue_scope_if_borrow_expr(
757 visitor, subexpr, blk_id);
760 ast::ExprUnary(ast::UnUniq, subexpr) => {
761 record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id);
763 ast::ExprCast(subexpr, _) |
764 ast::ExprParen(subexpr) => {
765 record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id)
767 ast::ExprBlock(ref block) => {
770 record_rvalue_scope_if_borrow_expr(
771 visitor, subexpr, blk_id);
781 fn record_rvalue_scope<'a>(visitor: &mut RegionResolutionVisitor,
783 blk_id: ast::NodeId) {
785 * Applied to an expression `expr` if `expr` -- or something
786 * owned or partially owned by `expr` -- is going to be
787 * indirectly referenced by a variable in a let statement. In
788 * that case, the "temporary lifetime" or `expr` is extended
789 * to be the block enclosing the `let` statement.
791 * More formally, if `expr` matches the grammar `ET`, record
792 * the rvalue scope of the matching `<rvalue>` as `blk_id`:
800 * Note: ET is intended to match "rvalues or
801 * lvalues based on rvalues".
806 // Note: give all the expressions matching `ET` with the
807 // extended temporary lifetime, not just the innermost rvalue,
808 // because in trans if we must compile e.g. `*rvalue()`
809 // into a temporary, we request the temporary scope of the
811 visitor.region_maps.record_rvalue_scope(expr.id, blk_id);
814 ast::ExprAddrOf(_, ref subexpr) |
815 ast::ExprUnary(ast::UnDeref, ref subexpr) |
816 ast::ExprField(ref subexpr, _, _) |
817 ast::ExprIndex(ref subexpr, _) |
818 ast::ExprParen(ref subexpr) => {
819 let subexpr: &'a @Expr = subexpr; // FIXME(#11586)
830 fn resolve_item(visitor: &mut RegionResolutionVisitor,
833 // Items create a new outer block scope as far as we're concerned.
834 let new_cx = Context {var_parent: None, parent: None, ..cx};
835 visit::walk_item(visitor, item, new_cx);
838 fn resolve_fn(visitor: &mut RegionResolutionVisitor,
845 debug!("region::resolve_fn(id={}, \
850 visitor.sess.codemap.span_to_str(sp),
854 visitor.region_maps.mark_as_terminating_scope(body.id);
856 // The arguments and `self` are parented to the body of the fn.
857 let decl_cx = Context {parent: Some(body.id),
858 var_parent: Some(body.id)};
859 visit::walk_fn_decl(visitor, decl, decl_cx);
861 // The body of the fn itself is either a root scope (top-level fn)
862 // or it continues with the inherited scope (closures).
863 let body_cx = match *fk {
864 visit::FkItemFn(..) | visit::FkMethod(..) => {
865 Context {parent: None, var_parent: None, ..cx}
867 visit::FkFnBlock(..) => {
868 // FIXME(#3696) -- at present we are place the closure body
869 // within the region hierarchy exactly where it appears lexically.
870 // This is wrong because the closure may live longer
871 // than the enclosing expression. We should probably fix this,
872 // but the correct fix is a bit subtle, and I am also not sure
873 // that the present approach is unsound -- it may not permit
874 // any illegal programs. See issue for more details.
878 visitor.visit_block(body, body_cx);
881 impl<'a> Visitor<Context> for RegionResolutionVisitor<'a> {
883 fn visit_block(&mut self, b: &Block, cx: Context) {
884 resolve_block(self, b, cx);
887 fn visit_item(&mut self, i: &Item, cx: Context) {
888 resolve_item(self, i, cx);
891 fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl,
892 b: &Block, s: Span, n: NodeId, cx: Context) {
893 resolve_fn(self, fk, fd, b, s, n, cx);
895 fn visit_arm(&mut self, a: &Arm, cx: Context) {
896 resolve_arm(self, a, cx);
898 fn visit_pat(&mut self, p: &Pat, cx: Context) {
899 resolve_pat(self, p, cx);
901 fn visit_stmt(&mut self, s: &Stmt, cx: Context) {
902 resolve_stmt(self, s, cx);
904 fn visit_expr(&mut self, ex: &Expr, cx: Context) {
905 resolve_expr(self, ex, cx);
907 fn visit_local(&mut self, l: &Local, cx: Context) {
908 resolve_local(self, l, cx);
912 pub fn resolve_crate(sess: Session, krate: &ast::Crate) -> RegionMaps {
913 let maps = RegionMaps {
914 scope_map: RefCell::new(NodeMap::new()),
915 var_map: RefCell::new(NodeMap::new()),
916 free_region_map: RefCell::new(HashMap::new()),
917 rvalue_scopes: RefCell::new(NodeMap::new()),
918 terminating_scopes: RefCell::new(HashSet::new()),
921 let mut visitor = RegionResolutionVisitor {
925 let cx = Context { parent: None, var_parent: None };
926 visit::walk_crate(&mut visitor, krate, cx);
931 pub fn resolve_inlined_item(sess: Session,
932 region_maps: &RegionMaps,
933 item: &ast::InlinedItem) {
934 let cx = Context {parent: None,
936 let mut visitor = RegionResolutionVisitor {
938 region_maps: region_maps,
940 visit::walk_inlined_item(&mut visitor, item, cx);