@@ -204,6 +204,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
204204 fake_reads : Default :: default ( ) ,
205205 } ;
206206
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+
207261 // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
208262 // to `ByRef` for the `async {}` block internal to async fns/closure. This means
209263 // 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> {
253307 }
254308 }
255309
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-
284310 debug ! (
285311 "For closure={:?}, capture_information={:#?}" ,
286312 closure_def_id, delegate. capture_information
@@ -289,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
289315 self . log_capture_analysis_first_pass ( closure_def_id, & delegate. capture_information , span) ;
290316
291317 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 ) ;
293319
294320 self . compute_min_captures ( closure_def_id, capture_information, span) ;
295321
@@ -545,13 +571,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
545571 fn process_collected_capture_information (
546572 & self ,
547573 capture_clause : hir:: CaptureBy ,
548- capture_information : InferredCaptureInformation < ' tcx > ,
574+ capture_information : & InferredCaptureInformation < ' tcx > ,
549575 ) -> ( InferredCaptureInformation < ' tcx > , ty:: ClosureKind , Option < ( Span , Place < ' tcx > ) > ) {
550576 let mut closure_kind = ty:: ClosureKind :: LATTICE_BOTTOM ;
551577 let mut origin: Option < ( Span , Place < ' tcx > ) > = None ;
552578
553579 let processed = capture_information
554- . into_iter ( )
580+ . iter ( )
581+ . cloned ( )
555582 . map ( |( place, mut capture_info) | {
556583 // Apply rules for safety before inferring closure kind
557584 let ( place, capture_kind) =
0 commit comments