@@ -33,6 +33,8 @@ struct InteriorVisitor<'a, 'tcx> {
33
33
prev_unresolved_span : Option < Span > ,
34
34
linted_values : HirIdSet ,
35
35
drop_ranges : DropRanges ,
36
+ task_context_hir_id : Option < HirId > ,
37
+ in_lowered_await : bool ,
36
38
}
37
39
38
40
impl < ' a , ' tcx > InteriorVisitor < ' a , ' tcx > {
@@ -53,7 +55,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
53
55
ty, hir_id, scope, expr, source_span, self . expr_count,
54
56
) ;
55
57
56
- let live_across_yield = scope
58
+ let mut live_across_yield = scope
57
59
. map ( |s| {
58
60
self . region_scope_tree . yield_in_scope ( s) . and_then ( |yield_data| {
59
61
// If we are recording an expression that is the last yield
@@ -92,6 +94,34 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
92
94
Some ( YieldData { span : DUMMY_SP , expr_and_pat_count : 0 , source : self . kind . into ( ) } )
93
95
} ) ;
94
96
97
+ // If this is the `&mut Context<'_>` async resume argument, or we are
98
+ // just visiting a `_task_context = yield ()` expression from async
99
+ // lowering, we do *not* consider this type to be live across yields.
100
+ if Some ( hir_id) == self . task_context_hir_id || self . in_lowered_await {
101
+ #[ cfg( debug_assertions) ]
102
+ {
103
+ // As `record` is being invoked for multiple parts / types of the
104
+ // lowered `.await`, the `ty` has to either be the `()` going *into*
105
+ // the `yield`, or a `&mut Context<'_>` coming *out* of it.
106
+ let tcx = self . fcx . tcx ;
107
+ if ty == tcx. types . unit {
108
+ // all good
109
+ } else if let ty:: Ref ( _, adt, hir:: Mutability :: Mut ) = ty. kind ( ) && let ty:: Adt ( adt, _) = adt. kind ( )
110
+ {
111
+ let context_def_id = tcx. lang_items ( ) . context ( ) ;
112
+ assert_eq ! (
113
+ Some ( adt. did( ) ) ,
114
+ context_def_id,
115
+ "expected `&mut Context<'_>, found `{:?}` instead`" ,
116
+ ty
117
+ ) ;
118
+ } else {
119
+ panic ! ( "expected `()` or `&mut Context<'_>`, found `{:?}` instead" , ty) ;
120
+ }
121
+ }
122
+ live_across_yield = None ;
123
+ }
124
+
95
125
if let Some ( yield_data) = live_across_yield {
96
126
debug ! (
97
127
"type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}" ,
@@ -183,6 +213,17 @@ pub fn resolve_interior<'a, 'tcx>(
183
213
kind : hir:: GeneratorKind ,
184
214
) {
185
215
let body = fcx. tcx . hir ( ) . body ( body_id) ;
216
+
217
+ // In case we are in an async block, this is the Param/Pat HirId of the
218
+ // `&mut Context<'_>` resume type. We can use this to explicitly prevent it
219
+ // from being considered as `live_across_yield`, which it is not, but the
220
+ // simple scope-based analysis can't tell.
221
+ let task_context_hir_id = if matches ! ( kind, hir:: GeneratorKind :: Async ( _) ) {
222
+ Some ( body. params [ 0 ] . pat . hir_id )
223
+ } else {
224
+ None
225
+ } ;
226
+
186
227
let typeck_results = fcx. inh . typeck_results . borrow ( ) ;
187
228
let mut visitor = InteriorVisitor {
188
229
fcx,
@@ -194,6 +235,8 @@ pub fn resolve_interior<'a, 'tcx>(
194
235
prev_unresolved_span : None ,
195
236
linted_values : <_ >:: default ( ) ,
196
237
drop_ranges : drop_ranges:: compute_drop_ranges ( fcx, def_id, body) ,
238
+ task_context_hir_id,
239
+ in_lowered_await : false ,
197
240
} ;
198
241
intravisit:: walk_body ( & mut visitor, body) ;
199
242
@@ -426,6 +469,22 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
426
469
}
427
470
_ => intravisit:: walk_expr ( self , expr) ,
428
471
} ,
472
+ ExprKind :: Assign ( _, rhs, _) => {
473
+ // An `.await` expression will be lowered to `_task_context = yield ()`.
474
+ // In that case, other forms of `yield` are considered errors in lowering.
475
+ if self . task_context_hir_id . is_some ( )
476
+ && matches ! ( rhs. kind, hir:: ExprKind :: Yield ( ..) )
477
+ {
478
+ assert ! ( !self . in_lowered_await) ;
479
+ self . in_lowered_await = true ;
480
+ }
481
+ // We are still walking the whole expression including its types.
482
+ // First, we need to keep `expr_count` in sync as it is asserted
483
+ // at the very end, and to keep all the other computations in
484
+ // place just in case they are causing other side effects.
485
+ intravisit:: walk_expr ( self , expr) ;
486
+ self . in_lowered_await = false ;
487
+ }
429
488
_ => intravisit:: walk_expr ( self , expr) ,
430
489
}
431
490
0 commit comments