@@ -204,6 +204,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
204
204
fake_reads : Default :: default ( ) ,
205
205
} ;
206
206
207
+ let _ = euv:: ExprUseVisitor :: new (
208
+ & FnCtxt :: new ( self , self . tcx . param_env ( closure_def_id) , closure_def_id) ,
209
+ & mut delegate,
210
+ )
211
+ . consume_body ( body) ;
212
+
213
+ // There are several curious situations with coroutine-closures where
214
+ // analysis is too aggressive with borrows when the coroutine-closure is
215
+ // marked `move`. Specifically:
216
+ //
217
+ // 1. If the coroutine-closure was inferred to be `FnOnce` during signature
218
+ // inference, then it's still possible that we try to borrow upvars from
219
+ // the coroutine-closure because they are not used by the coroutine body
220
+ // in a way that forces a move. See the test:
221
+ // `async-await/async-closures/force-move-due-to-inferred-kind.rs`.
222
+ //
223
+ // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it
224
+ // uses its upvars, but not *all* upvars would force the closure to `FnOnce`.
225
+ // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`.
226
+ //
227
+ // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
228
+ // coroutine bodies can't borrow from their parent closure. To fix this,
229
+ // we force the inner coroutine to also be `move`. This only matters for
230
+ // coroutine-closures that are `move` since otherwise they themselves will
231
+ // be borrowing from the outer environment, so there's no self-borrows occuring.
232
+ //
233
+ // One *important* note is that we do a call to `process_collected_capture_information`
234
+ // to eagerly test whether the coroutine would end up `FnOnce`, but we do this
235
+ // *before* capturing all the closure args by-value below, since that would always
236
+ // cause the analysis to return `FnOnce`.
237
+ if let UpvarArgs :: Coroutine ( ..) = args
238
+ && let hir:: CoroutineKind :: Desugared ( _, hir:: CoroutineSource :: Closure ) =
239
+ self . tcx . coroutine_kind ( closure_def_id) . expect ( "coroutine should have kind" )
240
+ && let parent_hir_id =
241
+ self . tcx . local_def_id_to_hir_id ( self . tcx . local_parent ( closure_def_id) )
242
+ && let parent_ty = self . node_ty ( parent_hir_id)
243
+ && let hir:: CaptureBy :: Value { move_kw } =
244
+ self . tcx . hir_node ( parent_hir_id) . expect_closure ( ) . capture_clause
245
+ {
246
+ // (1.) Closure signature inference forced this closure to `FnOnce`.
247
+ if let Some ( ty:: ClosureKind :: FnOnce ) = self . closure_kind ( parent_ty) {
248
+ capture_clause = hir:: CaptureBy :: Value { move_kw } ;
249
+ }
250
+ // (2.) The way that the closure uses its upvars means it's `FnOnce`.
251
+ else if let ( _, ty:: ClosureKind :: FnOnce , _) = self
252
+ . process_collected_capture_information (
253
+ capture_clause,
254
+ & delegate. capture_information ,
255
+ )
256
+ {
257
+ capture_clause = hir:: CaptureBy :: Value { move_kw } ;
258
+ }
259
+ }
260
+
207
261
// As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
208
262
// to `ByRef` for the `async {}` block internal to async fns/closure. This means
209
263
// that we would *not* be moving all of the parameters into the async block by default.
@@ -253,34 +307,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
253
307
}
254
308
}
255
309
256
- let _ = euv:: ExprUseVisitor :: new (
257
- & FnCtxt :: new ( self , self . tcx . param_env ( closure_def_id) , closure_def_id) ,
258
- & mut delegate,
259
- )
260
- . consume_body ( body) ;
261
-
262
- // If a coroutine is comes from a coroutine-closure that is `move`, but
263
- // the coroutine-closure was inferred to be `FnOnce` during signature
264
- // inference, then it's still possible that we try to borrow upvars from
265
- // the coroutine-closure because they are not used by the coroutine body
266
- // in a way that forces a move.
267
- //
268
- // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
269
- // coroutine bodies can't borrow from their parent closure. To fix this,
270
- // we force the inner coroutine to also be `move`. This only matters for
271
- // coroutine-closures that are `move` since otherwise they themselves will
272
- // be borrowing from the outer environment, so there's no self-borrows occuring.
273
- if let UpvarArgs :: Coroutine ( ..) = args
274
- && let hir:: CoroutineKind :: Desugared ( _, hir:: CoroutineSource :: Closure ) =
275
- self . tcx . coroutine_kind ( closure_def_id) . expect ( "coroutine should have kind" )
276
- && let parent_hir_id =
277
- self . tcx . local_def_id_to_hir_id ( self . tcx . local_parent ( closure_def_id) )
278
- && let parent_ty = self . node_ty ( parent_hir_id)
279
- && let Some ( ty:: ClosureKind :: FnOnce ) = self . closure_kind ( parent_ty)
280
- {
281
- capture_clause = self . tcx . hir_node ( parent_hir_id) . expect_closure ( ) . capture_clause ;
282
- }
283
-
284
310
debug ! (
285
311
"For closure={:?}, capture_information={:#?}" ,
286
312
closure_def_id, delegate. capture_information
@@ -289,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
289
315
self . log_capture_analysis_first_pass ( closure_def_id, & delegate. capture_information , span) ;
290
316
291
317
let ( capture_information, closure_kind, origin) = self
292
- . process_collected_capture_information ( capture_clause, delegate. capture_information ) ;
318
+ . process_collected_capture_information ( capture_clause, & delegate. capture_information ) ;
293
319
294
320
self . compute_min_captures ( closure_def_id, capture_information, span) ;
295
321
@@ -545,13 +571,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
545
571
fn process_collected_capture_information (
546
572
& self ,
547
573
capture_clause : hir:: CaptureBy ,
548
- capture_information : InferredCaptureInformation < ' tcx > ,
574
+ capture_information : & InferredCaptureInformation < ' tcx > ,
549
575
) -> ( InferredCaptureInformation < ' tcx > , ty:: ClosureKind , Option < ( Span , Place < ' tcx > ) > ) {
550
576
let mut closure_kind = ty:: ClosureKind :: LATTICE_BOTTOM ;
551
577
let mut origin: Option < ( Span , Place < ' tcx > ) > = None ;
552
578
553
579
let processed = capture_information
554
- . into_iter ( )
580
+ . iter ( )
581
+ . cloned ( )
555
582
. map ( |( place, mut capture_info) | {
556
583
// Apply rules for safety before inferring closure kind
557
584
let ( place, capture_kind) =
0 commit comments