@@ -219,19 +219,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
219
219
// `async-await/async-closures/force-move-due-to-inferred-kind.rs`.
220
220
//
221
221
// 2. If the coroutine-closure is forced to be `FnOnce` due to the way it
222
- // uses its upvars, but not *all* upvars would force the closure to `FnOnce`.
222
+ // uses its upvars (e.g. it consumes a non-copy value), but not *all* upvars
223
+ // would force the closure to `FnOnce`.
223
224
// See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`.
224
225
//
225
226
// This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
226
227
// coroutine bodies can't borrow from their parent closure. To fix this,
227
228
// we force the inner coroutine to also be `move`. This only matters for
228
229
// coroutine-closures that are `move` since otherwise they themselves will
229
230
// be borrowing from the outer environment, so there's no self-borrows occuring.
230
- //
231
- // One *important* note is that we do a call to `process_collected_capture_information`
232
- // to eagerly test whether the coroutine would end up `FnOnce`, but we do this
233
- // *before* capturing all the closure args by-value below, since that would always
234
- // cause the analysis to return `FnOnce`.
235
231
if let UpvarArgs :: Coroutine ( ..) = args
236
232
&& let hir:: CoroutineKind :: Desugared ( _, hir:: CoroutineSource :: Closure ) =
237
233
self . tcx . coroutine_kind ( closure_def_id) . expect ( "coroutine should have kind" )
@@ -246,19 +242,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
246
242
capture_clause = hir:: CaptureBy :: Value { move_kw } ;
247
243
}
248
244
// (2.) The way that the closure uses its upvars means it's `FnOnce`.
249
- else if let ( _, ty:: ClosureKind :: FnOnce , _) = self
250
- . process_collected_capture_information (
251
- capture_clause,
252
- & delegate. capture_information ,
253
- )
254
- {
245
+ else if self . coroutine_body_consumes_upvars ( closure_def_id, body) {
255
246
capture_clause = hir:: CaptureBy :: Value { move_kw } ;
256
247
}
257
248
}
258
249
259
250
// As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
260
251
// to `ByRef` for the `async {}` block internal to async fns/closure. This means
261
- // that we would *not* be moving all of the parameters into the async block by default.
252
+ // that we would *not* be moving all of the parameters into the async block in all cases.
253
+ // For example, when one of the arguments is `Copy`, we turn a consuming use into a copy of
254
+ // a reference, so for `async fn x(t: i32) {}`, we'd only take a reference to `t`.
262
255
//
263
256
// We force all of these arguments to be captured by move before we do expr use analysis.
264
257
//
@@ -535,6 +528,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
535
528
}
536
529
}
537
530
531
+ /// Determines whether the body of the coroutine uses its upvars in a way that
532
+ /// consumes (i.e. moves) the value, which would force the coroutine to `FnOnce`.
533
+ /// In a more detailed comment above, we care whether this happens, since if
534
+ /// this happens, we want to force the coroutine to move all of the upvars it
535
+ /// would've borrowed from the parent coroutine-closure.
536
+ ///
537
+ /// This only really makes sense to be called on the child coroutine of a
538
+ /// coroutine-closure.
539
+ fn coroutine_body_consumes_upvars (
540
+ & self ,
541
+ coroutine_def_id : LocalDefId ,
542
+ body : & ' tcx hir:: Body < ' tcx > ,
543
+ ) -> bool {
544
+ // This block contains argument capturing details. Since arguments
545
+ // aren't upvars, we do not care about them for determining if the
546
+ // coroutine body actually consumes its upvars.
547
+ let hir:: ExprKind :: Block ( & hir:: Block { expr : Some ( body) , .. } , None ) = body. value . kind
548
+ else {
549
+ bug ! ( ) ;
550
+ } ;
551
+ // Specifically, we only care about the *real* body of the coroutine.
552
+ // We skip out into the drop-temps within the block of the body in order
553
+ // to skip over the args of the desugaring.
554
+ let hir:: ExprKind :: DropTemps ( body) = body. kind else {
555
+ bug ! ( ) ;
556
+ } ;
557
+
558
+ let mut delegate = InferBorrowKind {
559
+ closure_def_id : coroutine_def_id,
560
+ capture_information : Default :: default ( ) ,
561
+ fake_reads : Default :: default ( ) ,
562
+ } ;
563
+
564
+ let _ = euv:: ExprUseVisitor :: new (
565
+ & FnCtxt :: new ( self , self . tcx . param_env ( coroutine_def_id) , coroutine_def_id) ,
566
+ & mut delegate,
567
+ )
568
+ . consume_expr ( body) ;
569
+
570
+ let ( _, kind, _) = self . process_collected_capture_information (
571
+ hir:: CaptureBy :: Ref ,
572
+ & delegate. capture_information ,
573
+ ) ;
574
+
575
+ matches ! ( kind, ty:: ClosureKind :: FnOnce )
576
+ }
577
+
538
578
// Returns a list of `Ty`s for each upvar.
539
579
fn final_upvar_tys ( & self , closure_id : LocalDefId ) -> Vec < Ty < ' tcx > > {
540
580
self . typeck_results
0 commit comments