@@ -371,7 +371,12 @@ struct RegionResolutionVisitor<'tcx> {
371
371
372
372
// The number of expressions and patterns visited in the current body
373
373
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 > ,
375
380
// Generated scope tree:
376
381
scope_tree : ScopeTree ,
377
382
@@ -947,12 +952,107 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
947
952
}
948
953
}
949
954
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
+
950
1008
match expr. node {
951
1009
// Manually recurse over closures, because they are the only
952
1010
// case of nested bodies that share the parent environment.
953
1011
hir:: ExprKind :: Closure ( .., body, _, _) => {
954
1012
let body = visitor. tcx . hir ( ) . body ( body) ;
955
1013
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
+
956
1056
}
957
1057
958
1058
_ => intravisit:: walk_expr ( visitor, expr)
@@ -972,6 +1072,10 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
972
1072
source : * source,
973
1073
} ;
974
1074
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
+ }
975
1079
976
1080
// Keep traversing up while we can.
977
1081
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
1360
1464
var_parent : None ,
1361
1465
} ,
1362
1466
terminating_scopes : Default :: default ( ) ,
1467
+ pessimistic_yield : false ,
1468
+ fixup_scopes : vec ! [ ] ,
1363
1469
} ;
1364
1470
1365
1471
let body = tcx. hir ( ) . body ( body_id) ;
0 commit comments