@@ -18,7 +18,6 @@ use ich::{StableHashingContext, NodeIdHashingMode};
18
18
use util:: nodemap:: { FxHashMap , FxHashSet } ;
19
19
use ty;
20
20
21
- use std:: collections:: hash_map:: Entry ;
22
21
use std:: mem;
23
22
use std:: rc:: Rc ;
24
23
use syntax:: codemap;
@@ -250,8 +249,80 @@ pub struct ScopeTree {
250
249
closure_tree : FxHashMap < hir:: ItemLocalId , hir:: ItemLocalId > ,
251
250
252
251
/// If there are any `yield` nested within a scope, this map
253
- /// stores the `Span` of the first one.
254
- yield_in_scope : FxHashMap < Scope , Span > ,
252
+ /// stores the `Span` of the last one and its index in the
253
+ /// postorder of the Visitor traversal on the HIR.
254
+ ///
255
+ /// HIR Visitor postorder indexes might seem like a peculiar
256
+ /// thing to care about. but it turns out that HIR bindings
257
+ /// and the temporary results of HIR expressions are never
258
+ /// storage-live at the end of HIR nodes with postorder indexes
259
+ /// lower than theirs, and therefore don't need to be suspended
260
+ /// at yield-points at these indexes.
261
+ ///
262
+ /// For an example, suppose we have some code such as:
263
+ /// ```rust,ignore (example)
264
+ /// foo(f(), yield y, bar(g()))
265
+ /// ```
266
+ ///
267
+ /// With the HIR tree (calls numbered for expository purposes)
268
+ /// ```
269
+ /// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))])
270
+ /// ```
271
+ ///
272
+ /// Obviously, the result of `f()` was created before the yield
273
+ /// (and therefore needs to be kept valid over the yield) while
274
+ /// the result of `g()` occurs after the yield (and therefore
275
+ /// doesn't). If we want to infer that, we can look at the
276
+ /// postorder traversal:
277
+ /// ```
278
+ /// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0
279
+ /// ```
280
+ ///
281
+ /// In which we can easily see that `Call#1` occurs before the yield,
282
+ /// and `Call#3` after it.
283
+ ///
284
+ /// To see that this method works, consider:
285
+ ///
286
+ /// Let `D` be our binding/temporary and `U` be our other HIR node, with
287
+ /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be
288
+ /// the yield and D would be one of the calls). Let's show that
289
+ /// `D` is storage-dead at `U`.
290
+ ///
291
+ /// Remember that storage-live/storage-dead refers to the state of
292
+ /// the *storage*, and does not consider moves/drop flags.
293
+ ///
294
+ /// Then:
295
+ /// 1. From the ordering guarantee of HIR visitors (see
296
+ /// `rustc::hir::intravisit`), `D` does not dominate `U`.
297
+ /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because
298
+ /// we might visit `U` without ever getting to `D`).
299
+ /// 3. However, we guarantee that at each HIR point, each
300
+ /// binding/temporary is always either always storage-live
301
+ /// or always storage-dead. This is what is being guaranteed
302
+ /// by `terminating_scopes` including all blocks where the
303
+ /// count of executions is not guaranteed.
304
+ /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`,
305
+ /// QED.
306
+ ///
307
+ /// I don't think this property relies on `3.` in an essential way - it
308
+ /// is probably still correct even if we have "unrestricted" terminating
309
+ /// scopes. However, why use the complicated proof when a simple one
310
+ /// works?
311
+ ///
312
+ /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It
313
+ /// might seem that a `box` expression creates a `Box<T>` temporary
314
+ /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might
315
+ /// be true in the MIR desugaring, but it is not important in the semantics.
316
+ ///
317
+ /// The reason is that semantically, until the `box` expression returns,
318
+ /// the values are still owned by their containing expressions. So
319
+ /// we'll see that `&x`.
320
+ yield_in_scope : FxHashMap < Scope , ( Span , usize ) > ,
321
+
322
+ /// The number of visit_expr and visit_pat calls done in the body.
323
+ /// Used to sanity check visit_expr/visit_pat call count when
324
+ /// calculating geneartor interiors.
325
+ body_expr_count : FxHashMap < hir:: BodyId , usize > ,
255
326
}
256
327
257
328
#[ derive( Debug , Copy , Clone ) ]
@@ -274,6 +345,9 @@ pub struct Context {
274
345
struct RegionResolutionVisitor < ' a , ' tcx : ' a > {
275
346
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
276
347
348
+ // The number of expressions and patterns visited in the current body
349
+ expr_and_pat_count : usize ,
350
+
277
351
// Generated scope tree:
278
352
scope_tree : ScopeTree ,
279
353
@@ -611,10 +685,18 @@ impl<'tcx> ScopeTree {
611
685
}
612
686
613
687
/// Checks whether the given scope contains a `yield`. If so,
614
- /// returns `Some(span)` with the span of a yield we found.
615
- pub fn yield_in_scope ( & self , scope : Scope ) -> Option < Span > {
688
+ /// returns `Some((span, expr_count))` with the span of a yield we found and
689
+ /// the number of expressions appearing before the `yield` in the body.
690
+ pub fn yield_in_scope ( & self , scope : Scope ) -> Option < ( Span , usize ) > {
616
691
self . yield_in_scope . get ( & scope) . cloned ( )
617
692
}
693
+
694
+ /// Gives the number of expressions visited in a body.
695
+ /// Used to sanity check visit_expr call count when
696
+ /// calculating geneartor interiors.
697
+ pub fn body_expr_count ( & self , body_id : hir:: BodyId ) -> Option < usize > {
698
+ self . body_expr_count . get ( & body_id) . map ( |r| * r)
699
+ }
618
700
}
619
701
620
702
/// Records the lifetime of a local variable as `cx.var_parent`
@@ -714,6 +796,8 @@ fn resolve_pat<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, pat: &
714
796
}
715
797
716
798
intravisit:: walk_pat ( visitor, pat) ;
799
+
800
+ visitor. expr_and_pat_count += 1 ;
717
801
}
718
802
719
803
fn resolve_stmt < ' a , ' tcx > ( visitor : & mut RegionResolutionVisitor < ' a , ' tcx > , stmt : & ' tcx hir:: Stmt ) {
@@ -804,29 +888,6 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
804
888
// record_superlifetime(new_cx, expr.callee_id);
805
889
}
806
890
807
- hir:: ExprYield ( ..) => {
808
- // Mark this expr's scope and all parent scopes as containing `yield`.
809
- let mut scope = Scope :: Node ( expr. hir_id . local_id ) ;
810
- loop {
811
- match visitor. scope_tree . yield_in_scope . entry ( scope) {
812
- // Another `yield` has already been found.
813
- Entry :: Occupied ( _) => break ,
814
-
815
- Entry :: Vacant ( entry) => {
816
- entry. insert ( expr. span ) ;
817
- }
818
- }
819
-
820
- // Keep traversing up while we can.
821
- match visitor. scope_tree . parent_map . get ( & scope) {
822
- // Don't cross from closure bodies to their parent.
823
- Some ( & Scope :: CallSite ( _) ) => break ,
824
- Some ( & superscope) => scope = superscope,
825
- None => break
826
- }
827
- }
828
- }
829
-
830
891
_ => { }
831
892
}
832
893
}
@@ -842,6 +903,25 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
842
903
_ => intravisit:: walk_expr ( visitor, expr)
843
904
}
844
905
906
+ visitor. expr_and_pat_count += 1 ;
907
+
908
+ if let hir:: ExprYield ( ..) = expr. node {
909
+ // Mark this expr's scope and all parent scopes as containing `yield`.
910
+ let mut scope = Scope :: Node ( expr. hir_id . local_id ) ;
911
+ loop {
912
+ visitor. scope_tree . yield_in_scope . insert ( scope,
913
+ ( expr. span , visitor. expr_and_pat_count ) ) ;
914
+
915
+ // Keep traversing up while we can.
916
+ match visitor. scope_tree . parent_map . get ( & scope) {
917
+ // Don't cross from closure bodies to their parent.
918
+ Some ( & Scope :: CallSite ( _) ) => break ,
919
+ Some ( & superscope) => scope = superscope,
920
+ None => break
921
+ }
922
+ }
923
+ }
924
+
845
925
visitor. cx = prev_cx;
846
926
}
847
927
@@ -1120,6 +1200,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
1120
1200
body_id,
1121
1201
self . cx. parent) ;
1122
1202
1203
+ let outer_ec = mem:: replace ( & mut self . expr_and_pat_count , 0 ) ;
1123
1204
let outer_cx = self . cx ;
1124
1205
let outer_ts = mem:: replace ( & mut self . terminating_scopes , FxHashSet ( ) ) ;
1125
1206
self . terminating_scopes . insert ( body. value . hir_id . local_id ) ;
@@ -1165,7 +1246,12 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
1165
1246
resolve_local ( self , None , Some ( & body. value ) ) ;
1166
1247
}
1167
1248
1249
+ if body. is_generator {
1250
+ self . scope_tree . body_expr_count . insert ( body_id, self . expr_and_pat_count ) ;
1251
+ }
1252
+
1168
1253
// Restore context we had at the start.
1254
+ self . expr_and_pat_count = outer_ec;
1169
1255
self . cx = outer_cx;
1170
1256
self . terminating_scopes = outer_ts;
1171
1257
}
@@ -1200,6 +1286,7 @@ fn region_scope_tree<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
1200
1286
let mut visitor = RegionResolutionVisitor {
1201
1287
tcx,
1202
1288
scope_tree : ScopeTree :: default ( ) ,
1289
+ expr_and_pat_count : 0 ,
1203
1290
cx : Context {
1204
1291
root_id : None ,
1205
1292
parent : None ,
@@ -1246,6 +1333,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ScopeTree {
1246
1333
let ScopeTree {
1247
1334
root_body,
1248
1335
root_parent,
1336
+ ref body_expr_count,
1249
1337
ref parent_map,
1250
1338
ref var_map,
1251
1339
ref destruction_scopes,
@@ -1259,6 +1347,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ScopeTree {
1259
1347
root_parent. hash_stable ( hcx, hasher) ;
1260
1348
} ) ;
1261
1349
1350
+ body_expr_count. hash_stable ( hcx, hasher) ;
1262
1351
parent_map. hash_stable ( hcx, hasher) ;
1263
1352
var_map. hash_stable ( hcx, hasher) ;
1264
1353
destruction_scopes. hash_stable ( hcx, hasher) ;
0 commit comments