@@ -206,50 +206,66 @@ impl CodeExtent {
206
206
}
207
207
208
208
/// 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.
247
209
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.)
248
216
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.
249
220
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.
250
228
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`).
251
236
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.
252
256
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. See the "Modeling closures"
266
+ /// section of the README in middle::infer::region_inference for
267
+ /// more details.
268
+ fn_tree : RefCell < NodeMap < ast:: NodeId > > ,
253
269
}
254
270
255
271
/// Carries the node id for the innermost block or match expression,
@@ -320,6 +336,14 @@ impl InnermostEnclosingExpr {
320
336
321
337
#[ derive( Debug , Copy ) ]
322
338
pub struct Context {
339
+ /// the root of the current region tree. This is typically the id
340
+ /// of the innermost fn body. Each fn forms its own disjoint tree
341
+ /// in the region hierarchy. These fn bodies are themselves
342
+ /// arranged into a tree. See the "Modeling closures" section of
343
+ /// the README in middle::infer::region_inference for more
344
+ /// details.
345
+ root_id : Option < ast:: NodeId > ,
346
+
323
347
/// the scope that contains any new variables declared
324
348
var_parent : InnermostDeclaringBlock ,
325
349
@@ -381,19 +405,40 @@ impl RegionMaps {
381
405
self . free_region_map . borrow_mut ( ) . insert ( sub, vec ! ( sup) ) ;
382
406
}
383
407
408
+ /// Records that `sub_fn` is defined within `sup_fn`. These ids
409
+ /// should be the id of the block that is the fn body, which is
410
+ /// also the root of the region hierarchy for that fn.
411
+ fn record_fn_parent ( & self , sub_fn : ast:: NodeId , sup_fn : ast:: NodeId ) {
412
+ debug ! ( "record_fn_parent(sub_fn={:?}, sup_fn={:?})" , sub_fn, sup_fn) ;
413
+ assert ! ( sub_fn != sup_fn) ;
414
+ let previous = self . fn_tree . borrow_mut ( ) . insert ( sub_fn, sup_fn) ;
415
+ assert ! ( previous. is_none( ) ) ;
416
+ }
417
+
418
+ fn fn_is_enclosed_by ( & self , mut sub_fn : ast:: NodeId , sup_fn : ast:: NodeId ) -> bool {
419
+ let fn_tree = self . fn_tree . borrow ( ) ;
420
+ loop {
421
+ if sub_fn == sup_fn { return true ; }
422
+ match fn_tree. get ( & sub_fn) {
423
+ Some ( & s) => { sub_fn = s; }
424
+ None => { return false ; }
425
+ }
426
+ }
427
+ }
428
+
384
429
pub fn record_encl_scope ( & self , sub : CodeExtent , sup : CodeExtent ) {
385
430
debug ! ( "record_encl_scope(sub={:?}, sup={:?})" , sub, sup) ;
386
431
assert ! ( sub != sup) ;
387
432
self . scope_map . borrow_mut ( ) . insert ( sub, sup) ;
388
433
}
389
434
390
- pub fn record_var_scope ( & self , var : ast:: NodeId , lifetime : CodeExtent ) {
435
+ fn record_var_scope ( & self , var : ast:: NodeId , lifetime : CodeExtent ) {
391
436
debug ! ( "record_var_scope(sub={:?}, sup={:?})" , var, lifetime) ;
392
437
assert ! ( var != lifetime. node_id( ) ) ;
393
438
self . var_map . borrow_mut ( ) . insert ( var, lifetime) ;
394
439
}
395
440
396
- pub fn record_rvalue_scope ( & self , var : ast:: NodeId , lifetime : CodeExtent ) {
441
+ fn record_rvalue_scope ( & self , var : ast:: NodeId , lifetime : CodeExtent ) {
397
442
debug ! ( "record_rvalue_scope(sub={:?}, sup={:?})" , var, lifetime) ;
398
443
assert ! ( var != lifetime. node_id( ) ) ;
399
444
self . rvalue_scopes . borrow_mut ( ) . insert ( var, lifetime) ;
@@ -402,7 +447,7 @@ impl RegionMaps {
402
447
/// Records that a scope is a TERMINATING SCOPE. Whenever we create automatic temporaries --
403
448
/// e.g. by an expression like `a().f` -- they will be freed within the innermost terminating
404
449
/// scope.
405
- pub fn mark_as_terminating_scope ( & self , scope_id : CodeExtent ) {
450
+ fn mark_as_terminating_scope ( & self , scope_id : CodeExtent ) {
406
451
debug ! ( "record_terminating_scope(scope_id={:?})" , scope_id) ;
407
452
self . terminating_scopes . borrow_mut ( ) . insert ( scope_id) ;
408
453
}
@@ -562,15 +607,15 @@ impl RegionMaps {
562
607
pub fn nearest_common_ancestor ( & self ,
563
608
scope_a : CodeExtent ,
564
609
scope_b : CodeExtent )
565
- -> Option < CodeExtent > {
566
- if scope_a == scope_b { return Some ( scope_a) ; }
610
+ -> CodeExtent {
611
+ if scope_a == scope_b { return scope_a; }
567
612
568
613
let a_ancestors = ancestors_of ( self , scope_a) ;
569
614
let b_ancestors = ancestors_of ( self , scope_b) ;
570
615
let mut a_index = a_ancestors. len ( ) - 1 ;
571
616
let mut b_index = b_ancestors. len ( ) - 1 ;
572
617
573
- // Here, ~ [ab]_ancestors is a vector going from narrow to broad.
618
+ // Here, [ab]_ancestors is a vector going from narrow to broad.
574
619
// The end of each vector will be the item where the scope is
575
620
// defined; if there are any common ancestors, then the tails of
576
621
// the vector will be the same. So basically we want to walk
@@ -579,23 +624,47 @@ impl RegionMaps {
579
624
// then the corresponding scope is a superscope of the other.
580
625
581
626
if a_ancestors[ a_index] != b_ancestors[ b_index] {
582
- return None ;
627
+ // In this case, the two regions belong to completely
628
+ // different functions. Compare those fn for lexical
629
+ // nesting. The reasoning behind this is subtle. See the
630
+ // "Modeling closures" section of the README in
631
+ // middle::infer::region_inference for more details.
632
+ let a_root_scope = a_ancestors[ a_index] ;
633
+ let b_root_scope = a_ancestors[ a_index] ;
634
+ return match ( a_root_scope, b_root_scope) {
635
+ ( CodeExtent :: DestructionScope ( a_root_id) ,
636
+ CodeExtent :: DestructionScope ( b_root_id) ) => {
637
+ if self . fn_is_enclosed_by ( a_root_id, b_root_id) {
638
+ // `a` is enclosed by `b`, hence `b` is the ancestor of everything in `a`
639
+ scope_b
640
+ } else if self . fn_is_enclosed_by ( b_root_id, a_root_id) {
641
+ // `b` is enclosed by `a`, hence `a` is the ancestor of everything in `b`
642
+ scope_a
643
+ } else {
644
+ // neither fn encloses the other
645
+ unreachable ! ( )
646
+ }
647
+ }
648
+ _ => {
649
+ // root ids are always Misc right now
650
+ unreachable ! ( )
651
+ }
652
+ } ;
583
653
}
584
654
585
655
loop {
586
656
// Loop invariant: a_ancestors[a_index] == b_ancestors[b_index]
587
657
// for all indices between a_index and the end of the array
588
- if a_index == 0 { return Some ( scope_a) ; }
589
- if b_index == 0 { return Some ( scope_b) ; }
658
+ if a_index == 0 { return scope_a; }
659
+ if b_index == 0 { return scope_b; }
590
660
a_index -= 1 ;
591
661
b_index -= 1 ;
592
662
if a_ancestors[ a_index] != b_ancestors[ b_index] {
593
- return Some ( a_ancestors[ a_index + 1 ] ) ;
663
+ return a_ancestors[ a_index + 1 ] ;
594
664
}
595
665
}
596
666
597
- fn ancestors_of ( this : & RegionMaps , scope : CodeExtent )
598
- -> Vec < CodeExtent > {
667
+ fn ancestors_of ( this : & RegionMaps , scope : CodeExtent ) -> Vec < CodeExtent > {
599
668
// debug!("ancestors_of(scope={:?})", scope);
600
669
let mut result = vec ! ( scope) ;
601
670
let mut scope = scope;
@@ -645,6 +714,7 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
645
714
let prev_cx = visitor. cx ;
646
715
647
716
let blk_scope = CodeExtent :: Misc ( blk. id ) ;
717
+
648
718
// If block was previously marked as a terminating scope during
649
719
// the recursive visit of its parent node in the AST, then we need
650
720
// to account for the destruction scope representing the extent of
@@ -684,6 +754,7 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
684
754
// itself has returned.
685
755
686
756
visitor. cx = Context {
757
+ root_id : prev_cx. root_id ,
687
758
var_parent : InnermostDeclaringBlock :: Block ( blk. id ) ,
688
759
parent : InnermostEnclosingExpr :: Some ( blk. id ) ,
689
760
} ;
@@ -710,6 +781,7 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
710
781
record_superlifetime (
711
782
visitor, declaring. to_code_extent ( ) , statement. span ) ;
712
783
visitor. cx = Context {
784
+ root_id : prev_cx. root_id ,
713
785
var_parent : InnermostDeclaringBlock :: Statement ( declaring) ,
714
786
parent : InnermostEnclosingExpr :: Statement ( declaring) ,
715
787
} ;
@@ -1103,6 +1175,7 @@ fn resolve_item(visitor: &mut RegionResolutionVisitor, item: &ast::Item) {
1103
1175
// Items create a new outer block scope as far as we're concerned.
1104
1176
let prev_cx = visitor. cx ;
1105
1177
visitor. cx = Context {
1178
+ root_id : None ,
1106
1179
var_parent : InnermostDeclaringBlock :: None ,
1107
1180
parent : InnermostEnclosingExpr :: None
1108
1181
} ;
@@ -1111,7 +1184,7 @@ fn resolve_item(visitor: &mut RegionResolutionVisitor, item: &ast::Item) {
1111
1184
}
1112
1185
1113
1186
fn resolve_fn ( visitor : & mut RegionResolutionVisitor ,
1114
- fk : FnKind ,
1187
+ _ : FnKind ,
1115
1188
decl : & ast:: FnDecl ,
1116
1189
body : & ast:: Block ,
1117
1190
sp : Span ,
@@ -1127,42 +1200,36 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
1127
1200
1128
1201
let body_scope = CodeExtent :: from_node_id ( body. id ) ;
1129
1202
visitor. region_maps . mark_as_terminating_scope ( body_scope) ;
1203
+
1130
1204
let dtor_scope = CodeExtent :: DestructionScope ( body. id ) ;
1131
1205
visitor. region_maps . record_encl_scope ( body_scope, dtor_scope) ;
1206
+
1132
1207
record_superlifetime ( visitor, dtor_scope, body. span ) ;
1133
1208
1209
+ if let Some ( root_id) = visitor. cx . root_id {
1210
+ visitor. region_maps . record_fn_parent ( body. id , root_id) ;
1211
+ }
1212
+
1134
1213
let outer_cx = visitor. cx ;
1135
1214
1136
1215
// The arguments and `self` are parented to the body of the fn.
1137
1216
visitor. cx = Context {
1217
+ root_id : Some ( body. id ) ,
1138
1218
parent : InnermostEnclosingExpr :: Some ( body. id ) ,
1139
1219
var_parent : InnermostDeclaringBlock :: Block ( body. id )
1140
1220
} ;
1141
1221
visit:: walk_fn_decl ( visitor, decl) ;
1142
1222
1143
- // The body of the fn itself is either a root scope (top-level fn)
1144
- // or it continues with the inherited scope (closures).
1145
- match fk {
1146
- visit:: FkItemFn ( ..) | visit:: FkMethod ( ..) => {
1147
- visitor. cx = Context {
1148
- parent : InnermostEnclosingExpr :: None ,
1149
- var_parent : InnermostDeclaringBlock :: None
1150
- } ;
1151
- visitor. visit_block ( body) ;
1152
- visitor. cx = outer_cx;
1153
- }
1154
- visit:: FkFnBlock ( ..) => {
1155
- // FIXME(#3696) -- at present we are place the closure body
1156
- // within the region hierarchy exactly where it appears lexically.
1157
- // This is wrong because the closure may live longer
1158
- // than the enclosing expression. We should probably fix this,
1159
- // but the correct fix is a bit subtle, and I am also not sure
1160
- // that the present approach is unsound -- it may not permit
1161
- // any illegal programs. See issue for more details.
1162
- visitor. cx = outer_cx;
1163
- visitor. visit_block ( body) ;
1164
- }
1165
- }
1223
+ // The body of the every fn is a root scope.
1224
+ visitor. cx = Context {
1225
+ root_id : Some ( body. id ) ,
1226
+ parent : InnermostEnclosingExpr :: None ,
1227
+ var_parent : InnermostDeclaringBlock :: None
1228
+ } ;
1229
+ visitor. visit_block ( body) ;
1230
+
1231
+ // Restore context we had at the start.
1232
+ visitor. cx = outer_cx;
1166
1233
}
1167
1234
1168
1235
impl < ' a , ' v > Visitor < ' v > for RegionResolutionVisitor < ' a > {
@@ -1203,12 +1270,14 @@ pub fn resolve_crate(sess: &Session, krate: &ast::Crate) -> RegionMaps {
1203
1270
free_region_map : RefCell :: new ( FnvHashMap ( ) ) ,
1204
1271
rvalue_scopes : RefCell :: new ( NodeMap ( ) ) ,
1205
1272
terminating_scopes : RefCell :: new ( FnvHashSet ( ) ) ,
1273
+ fn_tree : RefCell :: new ( NodeMap ( ) ) ,
1206
1274
} ;
1207
1275
{
1208
1276
let mut visitor = RegionResolutionVisitor {
1209
1277
sess : sess,
1210
1278
region_maps : & maps,
1211
1279
cx : Context {
1280
+ root_id : None ,
1212
1281
parent : InnermostEnclosingExpr :: None ,
1213
1282
var_parent : InnermostDeclaringBlock :: None ,
1214
1283
}
@@ -1225,6 +1294,7 @@ pub fn resolve_inlined_item(sess: &Session,
1225
1294
sess : sess,
1226
1295
region_maps : region_maps,
1227
1296
cx : Context {
1297
+ root_id : None ,
1228
1298
parent : InnermostEnclosingExpr :: None ,
1229
1299
var_parent : InnermostDeclaringBlock :: None
1230
1300
}
0 commit comments