@@ -219,19 +219,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
219219 // `async-await/async-closures/force-move-due-to-inferred-kind.rs`.
220220 //
221221 // 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`.
223224 // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`.
224225 //
225226 // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
226227 // coroutine bodies can't borrow from their parent closure. To fix this,
227228 // we force the inner coroutine to also be `move`. This only matters for
228229 // coroutine-closures that are `move` since otherwise they themselves will
229230 // 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`.
235231 if let UpvarArgs :: Coroutine ( ..) = args
236232 && let hir:: CoroutineKind :: Desugared ( _, hir:: CoroutineSource :: Closure ) =
237233 self . tcx . coroutine_kind ( closure_def_id) . expect ( "coroutine should have kind" )
@@ -246,19 +242,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
246242 capture_clause = hir:: CaptureBy :: Value { move_kw } ;
247243 }
248244 // (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) {
255246 capture_clause = hir:: CaptureBy :: Value { move_kw } ;
256247 }
257248 }
258249
259250 // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
260251 // 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`.
262255 //
263256 // We force all of these arguments to be captured by move before we do expr use analysis.
264257 //
@@ -535,6 +528,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
535528 }
536529 }
537530
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+
538578 // Returns a list of `Ty`s for each upvar.
539579 fn final_upvar_tys ( & self , closure_id : LocalDefId ) -> Vec < Ty < ' tcx > > {
540580 self . typeck_results
0 commit comments