Skip to content

Commit bc959fd

Browse files
committed
Add a meta-hierarchy of trees -- in future, each fn body will inhabit
its own disjoint region tree, and the new table encodes the lexical relationships between those trees.
1 parent d754722 commit bc959fd

File tree

1 file changed

+93
-44
lines changed

1 file changed

+93
-44
lines changed

src/librustc/middle/region.rs

+93-44
Original file line numberDiff line numberDiff line change
@@ -206,50 +206,64 @@ impl CodeExtent {
206206
}
207207

208208
/// The region maps encode information about region relationships.
209-
///
210-
/// - `scope_map` maps from a scope id to the enclosing scope id; this is
211-
/// usually corresponding to the lexical nesting, though in the case of
212-
/// closures the parent scope is the innermost conditional expression or repeating
213-
/// block. (Note that the enclosing scope id for the block
214-
/// associated with a closure is the closure itself.)
215-
///
216-
/// - `var_map` maps from a variable or binding id to the block in which
217-
/// that variable is declared.
218-
///
219-
/// - `free_region_map` maps from a free region `a` to a list of free
220-
/// regions `bs` such that `a <= b for all b in bs`
221-
/// - the free region map is populated during type check as we check
222-
/// each function. See the function `relate_free_regions` for
223-
/// more information.
224-
///
225-
/// - `rvalue_scopes` includes entries for those expressions whose cleanup
226-
/// scope is larger than the default. The map goes from the expression
227-
/// id to the cleanup scope id. For rvalues not present in this table,
228-
/// the appropriate cleanup scope is the innermost enclosing statement,
229-
/// conditional expression, or repeating block (see `terminating_scopes`).
230-
///
231-
/// - `terminating_scopes` is a set containing the ids of each statement,
232-
/// or conditional/repeating expression. These scopes are calling "terminating
233-
/// scopes" because, when attempting to find the scope of a temporary, by
234-
/// default we search up the enclosing scopes until we encounter the
235-
/// terminating scope. A conditional/repeating
236-
/// expression is one which is not guaranteed to execute exactly once
237-
/// upon entering the parent scope. This could be because the expression
238-
/// only executes conditionally, such as the expression `b` in `a && b`,
239-
/// or because the expression may execute many times, such as a loop
240-
/// body. The reason that we distinguish such expressions is that, upon
241-
/// exiting the parent scope, we cannot statically know how many times
242-
/// the expression executed, and thus if the expression creates
243-
/// temporaries we cannot know statically how many such temporaries we
244-
/// would have to cleanup. Therefore we ensure that the temporaries never
245-
/// outlast the conditional/repeating expression, preventing the need
246-
/// for dynamic checks and/or arbitrary amounts of stack space.
247209
pub struct RegionMaps {
210+
/// `scope_map` maps from a scope id to the enclosing scope id;
211+
/// this is usually corresponding to the lexical nesting, though
212+
/// in the case of closures the parent scope is the innermost
213+
/// conditional expression or repeating block. (Note that the
214+
/// enclosing scope id for the block associated with a closure is
215+
/// the closure itself.)
248216
scope_map: RefCell<FnvHashMap<CodeExtent, CodeExtent>>,
217+
218+
/// `var_map` maps from a variable or binding id to the block in
219+
/// which that variable is declared.
249220
var_map: RefCell<NodeMap<CodeExtent>>,
221+
222+
/// `free_region_map` maps from a free region `a` to a list of
223+
/// free regions `bs` such that `a <= b for all b in bs`
224+
///
225+
/// NB. the free region map is populated during type check as we
226+
/// check each function. See the function `relate_free_regions`
227+
/// for more information.
250228
free_region_map: RefCell<FnvHashMap<FreeRegion, Vec<FreeRegion>>>,
229+
230+
/// `rvalue_scopes` includes entries for those expressions whose cleanup scope is
231+
/// larger than the default. The map goes from the expression id
232+
/// to the cleanup scope id. For rvalues not present in this
233+
/// table, the appropriate cleanup scope is the innermost
234+
/// enclosing statement, conditional expression, or repeating
235+
/// block (see `terminating_scopes`).
251236
rvalue_scopes: RefCell<NodeMap<CodeExtent>>,
237+
238+
/// `terminating_scopes` is a set containing the ids of each
239+
/// statement, or conditional/repeating expression. These scopes
240+
/// are calling "terminating scopes" because, when attempting to
241+
/// find the scope of a temporary, by default we search up the
242+
/// enclosing scopes until we encounter the terminating scope. A
243+
/// conditional/repeating expression is one which is not
244+
/// guaranteed to execute exactly once upon entering the parent
245+
/// scope. This could be because the expression only executes
246+
/// conditionally, such as the expression `b` in `a && b`, or
247+
/// because the expression may execute many times, such as a loop
248+
/// body. The reason that we distinguish such expressions is that,
249+
/// upon exiting the parent scope, we cannot statically know how
250+
/// many times the expression executed, and thus if the expression
251+
/// creates temporaries we cannot know statically how many such
252+
/// temporaries we would have to cleanup. Therefore we ensure that
253+
/// the temporaries never outlast the conditional/repeating
254+
/// expression, preventing the need for dynamic checks and/or
255+
/// arbitrary amounts of stack space.
252256
terminating_scopes: RefCell<FnvHashSet<CodeExtent>>,
257+
258+
/// Encodes the hierarchy of fn bodies. Every fn body (including
259+
/// closures) forms its own distinct region hierarchy, rooted in
260+
/// the block that is the fn body. This map points from the id of
261+
/// that root block to the id of the root block for the enclosing
262+
/// fn, if any. Thus the map structures the fn bodies into a
263+
/// hierarchy based on their lexical mapping. This is used to
264+
/// handle the relationships between regions in a fn and in a
265+
/// closure defined by that fn.
266+
fn_tree: RefCell<NodeMap<ast::NodeId>>,
253267
}
254268

255269
/// Carries the node id for the innermost block or match expression,
@@ -320,6 +334,12 @@ impl InnermostEnclosingExpr {
320334

321335
#[derive(Debug, Copy)]
322336
pub struct Context {
337+
/// the root of the current region tree. This is typically the id
338+
/// of the innermost fn body. Each fn forms its own disjoint tree
339+
/// in the region hierarchy. These fn bodies are themselves
340+
/// arranged into a tree.
341+
root_id: Option<ast::NodeId>,
342+
323343
/// the scope that contains any new variables declared
324344
var_parent: InnermostDeclaringBlock,
325345

@@ -381,19 +401,29 @@ impl RegionMaps {
381401
self.free_region_map.borrow_mut().insert(sub, vec!(sup));
382402
}
383403

384-
pub fn record_encl_scope(&self, sub: CodeExtent, sup: CodeExtent) {
404+
/// Records that `sub_fn` is defined within `sup_fn`. These ids
405+
/// should be the id of the block that is the fn body, which is
406+
/// also the root of the region hierarchy for that fn.
407+
fn record_fn_parent(&self, sub_fn: ast::NodeId, sup_fn: ast::NodeId) {
408+
debug!("record_fn_parent(sub_fn={:?}, sup_fn={:?})", sub_fn, sup_fn);
409+
assert!(sub_fn != sup_fn);
410+
let previous = self.fn_tree.borrow_mut().insert(sub_fn, sup_fn);
411+
assert!(previous.is_none());
412+
}
413+
414+
fn record_encl_scope(&self, sub: CodeExtent, sup: CodeExtent) {
385415
debug!("record_encl_scope(sub={:?}, sup={:?})", sub, sup);
386416
assert!(sub != sup);
387417
self.scope_map.borrow_mut().insert(sub, sup);
388418
}
389419

390-
pub fn record_var_scope(&self, var: ast::NodeId, lifetime: CodeExtent) {
420+
fn record_var_scope(&self, var: ast::NodeId, lifetime: CodeExtent) {
391421
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
392422
assert!(var != lifetime.node_id());
393423
self.var_map.borrow_mut().insert(var, lifetime);
394424
}
395425

396-
pub fn record_rvalue_scope(&self, var: ast::NodeId, lifetime: CodeExtent) {
426+
fn record_rvalue_scope(&self, var: ast::NodeId, lifetime: CodeExtent) {
397427
debug!("record_rvalue_scope(sub={:?}, sup={:?})", var, lifetime);
398428
assert!(var != lifetime.node_id());
399429
self.rvalue_scopes.borrow_mut().insert(var, lifetime);
@@ -402,7 +432,7 @@ impl RegionMaps {
402432
/// Records that a scope is a TERMINATING SCOPE. Whenever we create automatic temporaries --
403433
/// e.g. by an expression like `a().f` -- they will be freed within the innermost terminating
404434
/// scope.
405-
pub fn mark_as_terminating_scope(&self, scope_id: CodeExtent) {
435+
fn mark_as_terminating_scope(&self, scope_id: CodeExtent) {
406436
debug!("record_terminating_scope(scope_id={:?})", scope_id);
407437
self.terminating_scopes.borrow_mut().insert(scope_id);
408438
}
@@ -684,6 +714,7 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
684714
// itself has returned.
685715

686716
visitor.cx = Context {
717+
root_id: prev_cx.root_id,
687718
var_parent: InnermostDeclaringBlock::Block(blk.id),
688719
parent: InnermostEnclosingExpr::Some(blk.id),
689720
};
@@ -710,6 +741,7 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
710741
record_superlifetime(
711742
visitor, declaring.to_code_extent(), statement.span);
712743
visitor.cx = Context {
744+
root_id: prev_cx.root_id,
713745
var_parent: InnermostDeclaringBlock::Statement(declaring),
714746
parent: InnermostEnclosingExpr::Statement(declaring),
715747
};
@@ -1103,6 +1135,7 @@ fn resolve_item(visitor: &mut RegionResolutionVisitor, item: &ast::Item) {
11031135
// Items create a new outer block scope as far as we're concerned.
11041136
let prev_cx = visitor.cx;
11051137
visitor.cx = Context {
1138+
root_id: None,
11061139
var_parent: InnermostDeclaringBlock::None,
11071140
parent: InnermostEnclosingExpr::None
11081141
};
@@ -1127,14 +1160,21 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
11271160

11281161
let body_scope = CodeExtent::from_node_id(body.id);
11291162
visitor.region_maps.mark_as_terminating_scope(body_scope);
1163+
11301164
let dtor_scope = CodeExtent::DestructionScope(body.id);
11311165
visitor.region_maps.record_encl_scope(body_scope, dtor_scope);
1166+
11321167
record_superlifetime(visitor, dtor_scope, body.span);
11331168

1169+
if let Some(root_id) = visitor.cx.root_id {
1170+
visitor.region_maps.record_fn_parent(body.id, root_id);
1171+
}
1172+
11341173
let outer_cx = visitor.cx;
11351174

11361175
// The arguments and `self` are parented to the body of the fn.
11371176
visitor.cx = Context {
1177+
root_id: Some(body.id),
11381178
parent: InnermostEnclosingExpr::Some(body.id),
11391179
var_parent: InnermostDeclaringBlock::Block(body.id)
11401180
};
@@ -1145,11 +1185,11 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
11451185
match fk {
11461186
visit::FkItemFn(..) | visit::FkMethod(..) => {
11471187
visitor.cx = Context {
1188+
root_id: Some(body.id),
11481189
parent: InnermostEnclosingExpr::None,
11491190
var_parent: InnermostDeclaringBlock::None
11501191
};
11511192
visitor.visit_block(body);
1152-
visitor.cx = outer_cx;
11531193
}
11541194
visit::FkFnBlock(..) => {
11551195
// FIXME(#3696) -- at present we are place the closure body
@@ -1159,10 +1199,16 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
11591199
// but the correct fix is a bit subtle, and I am also not sure
11601200
// that the present approach is unsound -- it may not permit
11611201
// any illegal programs. See issue for more details.
1162-
visitor.cx = outer_cx;
1202+
visitor.cx = Context {
1203+
root_id: Some(body.id),
1204+
..outer_cx
1205+
};
11631206
visitor.visit_block(body);
11641207
}
11651208
}
1209+
1210+
// Restore context we had at the start.
1211+
visitor.cx = outer_cx;
11661212
}
11671213

11681214
impl<'a, 'v> Visitor<'v> for RegionResolutionVisitor<'a> {
@@ -1203,12 +1249,14 @@ pub fn resolve_crate(sess: &Session, krate: &ast::Crate) -> RegionMaps {
12031249
free_region_map: RefCell::new(FnvHashMap()),
12041250
rvalue_scopes: RefCell::new(NodeMap()),
12051251
terminating_scopes: RefCell::new(FnvHashSet()),
1252+
fn_tree: RefCell::new(NodeMap()),
12061253
};
12071254
{
12081255
let mut visitor = RegionResolutionVisitor {
12091256
sess: sess,
12101257
region_maps: &maps,
12111258
cx: Context {
1259+
root_id: None,
12121260
parent: InnermostEnclosingExpr::None,
12131261
var_parent: InnermostDeclaringBlock::None,
12141262
}
@@ -1225,6 +1273,7 @@ pub fn resolve_inlined_item(sess: &Session,
12251273
sess: sess,
12261274
region_maps: region_maps,
12271275
cx: Context {
1276+
root_id: None,
12281277
parent: InnermostEnclosingExpr::None,
12291278
var_parent: InnermostDeclaringBlock::None
12301279
}

0 commit comments

Comments
 (0)