@@ -18,7 +18,6 @@ use ich::{StableHashingContext, NodeIdHashingMode};
1818use util:: nodemap:: { FxHashMap , FxHashSet } ;
1919use ty;
2020
21- use std:: collections:: hash_map:: Entry ;
2221use std:: mem;
2322use std:: rc:: Rc ;
2423use syntax:: codemap;
@@ -250,8 +249,80 @@ pub struct ScopeTree {
250249 closure_tree : FxHashMap < hir:: ItemLocalId , hir:: ItemLocalId > ,
251250
252251 /// 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 > ,
255326}
256327
257328#[ derive( Debug , Copy , Clone ) ]
@@ -274,6 +345,9 @@ pub struct Context {
274345struct RegionResolutionVisitor < ' a , ' tcx : ' a > {
275346 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
276347
348+ // The number of expressions and patterns visited in the current body
349+ expr_and_pat_count : usize ,
350+
277351 // Generated scope tree:
278352 scope_tree : ScopeTree ,
279353
@@ -611,10 +685,18 @@ impl<'tcx> ScopeTree {
611685 }
612686
613687 /// 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 ) > {
616691 self . yield_in_scope . get ( & scope) . cloned ( )
617692 }
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+ }
618700}
619701
620702/// 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: &
714796 }
715797
716798 intravisit:: walk_pat ( visitor, pat) ;
799+
800+ visitor. expr_and_pat_count += 1 ;
717801}
718802
719803fn 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:
804888 // record_superlifetime(new_cx, expr.callee_id);
805889 }
806890
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-
830891 _ => { }
831892 }
832893 }
@@ -842,6 +903,25 @@ fn resolve_expr<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, expr:
842903 _ => intravisit:: walk_expr ( visitor, expr)
843904 }
844905
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+
845925 visitor. cx = prev_cx;
846926}
847927
@@ -1120,6 +1200,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
11201200 body_id,
11211201 self . cx. parent) ;
11221202
1203+ let outer_ec = mem:: replace ( & mut self . expr_and_pat_count , 0 ) ;
11231204 let outer_cx = self . cx ;
11241205 let outer_ts = mem:: replace ( & mut self . terminating_scopes , FxHashSet ( ) ) ;
11251206 self . terminating_scopes . insert ( body. value . hir_id . local_id ) ;
@@ -1165,7 +1246,12 @@ impl<'a, 'tcx> Visitor<'tcx> for RegionResolutionVisitor<'a, 'tcx> {
11651246 resolve_local ( self , None , Some ( & body. value ) ) ;
11661247 }
11671248
1249+ if body. is_generator {
1250+ self . scope_tree . body_expr_count . insert ( body_id, self . expr_and_pat_count ) ;
1251+ }
1252+
11681253 // Restore context we had at the start.
1254+ self . expr_and_pat_count = outer_ec;
11691255 self . cx = outer_cx;
11701256 self . terminating_scopes = outer_ts;
11711257 }
@@ -1200,6 +1286,7 @@ fn region_scope_tree<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
12001286 let mut visitor = RegionResolutionVisitor {
12011287 tcx,
12021288 scope_tree : ScopeTree :: default ( ) ,
1289+ expr_and_pat_count : 0 ,
12031290 cx : Context {
12041291 root_id : None ,
12051292 parent : None ,
@@ -1246,6 +1333,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ScopeTree {
12461333 let ScopeTree {
12471334 root_body,
12481335 root_parent,
1336+ ref body_expr_count,
12491337 ref parent_map,
12501338 ref var_map,
12511339 ref destruction_scopes,
@@ -1259,6 +1347,7 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ScopeTree {
12591347 root_parent. hash_stable ( hcx, hasher) ;
12601348 } ) ;
12611349
1350+ body_expr_count. hash_stable ( hcx, hasher) ;
12621351 parent_map. hash_stable ( hcx, hasher) ;
12631352 var_map. hash_stable ( hcx, hasher) ;
12641353 destruction_scopes. hash_stable ( hcx, hasher) ;
0 commit comments