@@ -371,7 +371,12 @@ struct RegionResolutionVisitor<'tcx> {
371371
372372 // The number of expressions and patterns visited in the current body
373373 expr_and_pat_count : usize ,
374-
374+ // When this is `true`, we record the `Scopes` we encounter
375+ // when processing a Yield expression. This allows us to fix
376+ // up their indices.
377+ pessimistic_yield : bool ,
378+ // Stores scopes when pessimistic_yield is true.
379+ fixup_scopes : Vec < Scope > ,
375380 // Generated scope tree:
376381 scope_tree : ScopeTree ,
377382
@@ -947,12 +952,107 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
947952 }
948953 }
949954
955+ let prev_pessimistic = visitor. pessimistic_yield ;
956+
957+ // Ordinarily, we can rely on the visit order of HIR intravisit
958+ // to correspond to the actual execution order of statements.
959+ // However, there's a weird corner case with compund assignment
960+ // operators (e.g. `a += b`). The evaluation order depends on whether
961+ // or not the operator is overloaded (e.g. whether or not a trait
962+ // like AddAssign is implemented).
963+
964+ // For primitive types (which, despite having a trait impl, don't actually
965+ // end up calling it), the evluation order is right-to-left. For example,
966+ // the following code snippet:
967+ //
968+ // let y = &mut 0;
969+ // *{println!("LHS!"); y} += {println!("RHS!"); 1};
970+ //
971+ // will print:
972+ //
973+ // RHS!
974+ // LHS!
975+ //
976+ // However, if the operator is used on a non-primitive type,
977+ // the evaluation order will be left-to-right, since the operator
978+ // actually get desugared to a method call. For example, this
979+ // nearly identical code snippet:
980+ //
981+ // let y = &mut String::new();
982+ // *{println!("LHS String"); y} += {println!("RHS String"); "hi"};
983+ //
984+ // will print:
985+ // LHS String
986+ // RHS String
987+ //
988+ // To determine the actual execution order, we need to perform
989+ // trait resolution. Unfortunately, we need to be able to compute
990+ // yield_in_scope before type checking is even done, as it gets
991+ // used by AST borrowcheck.
992+ //
993+ // Fortunately, we don't need to know the actual execution order.
994+ // It suffices to know the 'worst case' order with respect to yields.
995+ // Specifically, we need to know the highest 'expr_and_pat_count'
996+ // that we could assign to the yield expression. To do this,
997+ // we pick the greater of the two values from the left-hand
998+ // and right-hand expressions. This makes us overly conservative
999+ // about what types could possibly live across yield points,
1000+ // but we will never fail to detect that a type does actually
1001+ // live across a yield point. The latter part is critical -
1002+ // we're already overly conservative about what types will live
1003+ // across yield points, as the generated MIR will determine
1004+ // when things are actually live. However, for typecheck to work
1005+ // properly, we can't miss any types.
1006+
1007+
9501008 match expr. node {
9511009 // Manually recurse over closures, because they are the only
9521010 // case of nested bodies that share the parent environment.
9531011 hir:: ExprKind :: Closure ( .., body, _, _) => {
9541012 let body = visitor. tcx . hir ( ) . body ( body) ;
9551013 visitor. visit_body ( body) ;
1014+ } ,
1015+ hir:: ExprKind :: AssignOp ( _, ref left_expr, ref right_expr) => {
1016+ debug ! ( "resolve_expr - enabling pessimistic_yield, was previously {}" ,
1017+ prev_pessimistic) ;
1018+
1019+ let start_point = visitor. fixup_scopes . len ( ) ;
1020+ visitor. pessimistic_yield = true ;
1021+
1022+ // If the actual execution order turns out to be right-to-left,
1023+ // then we're fine. However, if the actual execution order is left-to-right,
1024+ // then we'll assign too low a count to any `yield` expressions
1025+ // we encounter in 'right_expression' - they should really occur after all of the
1026+ // expressions in 'left_expression'.
1027+ visitor. visit_expr ( & right_expr) ;
1028+ visitor. pessimistic_yield = prev_pessimistic;
1029+
1030+ debug ! ( "resolve_expr - restoring pessimistic_yield to {}" , prev_pessimistic) ;
1031+ visitor. visit_expr ( & left_expr) ;
1032+ debug ! ( "resolve_expr - fixing up counts to {}" , visitor. expr_and_pat_count) ;
1033+
1034+ // Remove and process any scopes pushed by the visitor
1035+ let target_scopes = visitor. fixup_scopes . drain ( start_point..) ;
1036+
1037+ for scope in target_scopes {
1038+ let mut yield_data = visitor. scope_tree . yield_in_scope . get_mut ( & scope) . unwrap ( ) ;
1039+ let count = yield_data. expr_and_pat_count ;
1040+ let span = yield_data. span ;
1041+
1042+ // expr_and_pat_count never decreases. Since we recorded counts in yield_in_scope
1043+ // before walking the left-hand side, it should be impossible for the recorded
1044+ // count to be greater than the left-hand side count.
1045+ if count > visitor. expr_and_pat_count {
1046+ bug ! ( "Encountered greater count {} at span {:?} - expected no greater than {}" ,
1047+ count, span, visitor. expr_and_pat_count) ;
1048+ }
1049+ let new_count = visitor. expr_and_pat_count ;
1050+ debug ! ( "resolve_expr - increasing count for scope {:?} from {} to {} at span {:?}" ,
1051+ scope, count, new_count, span) ;
1052+
1053+ yield_data. expr_and_pat_count = new_count;
1054+ }
1055+
9561056 }
9571057
9581058 _ => intravisit:: walk_expr ( visitor, expr)
@@ -972,6 +1072,10 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
9721072 source : * source,
9731073 } ;
9741074 visitor. scope_tree . yield_in_scope . insert ( scope, data) ;
1075+ if visitor. pessimistic_yield {
1076+ debug ! ( "resolve_expr in pessimistic_yield - marking scope {:?} for fixup" , scope) ;
1077+ visitor. fixup_scopes . push ( scope) ;
1078+ }
9751079
9761080 // Keep traversing up while we can.
9771081 match visitor. scope_tree . parent_map . get ( & scope) {
@@ -1360,6 +1464,8 @@ fn region_scope_tree<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ScopeTree
13601464 var_parent : None ,
13611465 } ,
13621466 terminating_scopes : Default :: default ( ) ,
1467+ pessimistic_yield : false ,
1468+ fixup_scopes : vec ! [ ] ,
13631469 } ;
13641470
13651471 let body = tcx. hir ( ) . body ( body_id) ;
0 commit comments