@@ -367,37 +367,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
367367 ty:: INNERMOST ,
368368 ty:: BoundRegion { var : ty:: BoundVar :: ZERO , kind : ty:: BoundRegionKind :: BrEnv } ,
369369 ) ;
370+
371+ let num_args = args
372+ . as_coroutine_closure ( )
373+ . coroutine_closure_sig ( )
374+ . skip_binder ( )
375+ . tupled_inputs_ty
376+ . tuple_fields ( )
377+ . len ( ) ;
378+ let typeck_results = self . typeck_results . borrow ( ) ;
379+
370380 let tupled_upvars_ty_for_borrow = Ty :: new_tup_from_iter (
371381 self . tcx ,
372- self . typeck_results
373- . borrow ( )
374- . closure_min_captures_flattened (
375- self . tcx . coroutine_for_closure ( closure_def_id ) . expect_local ( ) ,
376- )
377- // Skip the captures that are just moving the closure's args
378- // into the coroutine. These are always by move, and we append
379- // those later in the `CoroutineClosureSignature` helper functions.
380- . skip (
381- args . as_coroutine_closure ( )
382- . coroutine_closure_sig ( )
383- . skip_binder ( )
384- . tupled_inputs_ty
385- . tuple_fields ( )
386- . len ( ) ,
387- )
388- . map ( |captured_place| {
389- let upvar_ty = captured_place . place . ty ( ) ;
390- let capture = captured_place . info . capture_kind ;
382+ ty :: analyze_coroutine_closure_captures (
383+ typeck_results . closure_min_captures_flattened ( closure_def_id ) ,
384+ typeck_results
385+ . closure_min_captures_flattened (
386+ self . tcx . coroutine_for_closure ( closure_def_id ) . expect_local ( ) ,
387+ )
388+ // Skip the captures that are just moving the closure's args
389+ // into the coroutine. These are always by move, and we append
390+ // those later in the `CoroutineClosureSignature` helper functions.
391+ . skip ( num_args ) ,
392+ | ( _ , parent_capture ) , ( _ , child_capture ) | {
393+ // This is subtle. See documentation on function.
394+ let needs_ref = should_reborrow_from_env_of_parent_coroutine_closure (
395+ parent_capture ,
396+ child_capture ,
397+ ) ;
398+
399+ let upvar_ty = child_capture . place . ty ( ) ;
400+ let capture = child_capture . info . capture_kind ;
391401 // Not all upvars are captured by ref, so use
392402 // `apply_capture_kind_on_capture_ty` to ensure that we
393403 // compute the right captured type.
394- apply_capture_kind_on_capture_ty (
404+ return apply_capture_kind_on_capture_ty (
395405 self . tcx ,
396406 upvar_ty,
397407 capture,
398- Some ( closure_env_region) ,
399- )
400- } ) ,
408+ if needs_ref { Some ( closure_env_region) } else { child_capture. region } ,
409+ ) ;
410+ } ,
411+ ) ,
401412 ) ;
402413 let coroutine_captures_by_ref_ty = Ty :: new_fn_ptr (
403414 self . tcx ,
@@ -1761,6 +1772,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17611772 }
17621773}
17631774
1775+ /// Determines whether a child capture that is derived from a parent capture
1776+ /// should be borrowed with the lifetime of the parent coroutine-closure's env.
1777+ ///
1778+ /// There are two cases when this needs to happen:
1779+ ///
1780+ /// (1.) Are we borrowing data owned by the parent closure? We can determine if
1781+ /// that is the case by checking if the parent capture is by move, EXCEPT if we
1782+ /// apply a deref projection, which means we're reborrowing a reference that we
1783+ /// captured by move.
1784+ ///
1785+ /// ```rust
1786+ /// #![feature(async_closure)]
1787+ /// let x = &1i32; // Let's call this lifetime `'1`.
1788+ /// let c = async move || {
1789+ /// println!("{:?}", *x);
1790+ /// // Even though the inner coroutine borrows by ref, we're only capturing `*x`,
1791+ /// // not `x`, so the inner closure is allowed to reborrow the data for `'1`.
1792+ /// };
1793+ /// ```
1794+ ///
1795+ /// (2.) If a coroutine is mutably borrowing from a parent capture, then that
1796+ /// mutable borrow cannot live for longer than either the parent *or* the borrow
1797+ /// that we have on the original upvar. Therefore we always need to borrow the
1798+ /// child capture with the lifetime of the parent coroutine-closure's env.
1799+ ///
1800+ /// ```rust
1801+ /// #![feature(async_closure)]
1802+ /// let mut x = 1i32;
1803+ /// let c = async || {
1804+ /// x = 1;
1805+ /// // The parent borrows `x` for some `&'1 mut i32`.
1806+ /// // However, when we call `c()`, we implicitly autoref for the signature of
1807+ /// // `AsyncFnMut::async_call_mut`. Let's call that lifetime `'call`. Since
1808+ /// // the maximum that `&'call mut &'1 mut i32` can be reborrowed is `&'call mut i32`,
1809+ /// // the inner coroutine should capture w/ the lifetime of the coroutine-closure.
1810+ /// };
1811+ /// ```
1812+ ///
1813+ /// If either of these cases apply, then we should capture the borrow with the
1814+ /// lifetime of the parent coroutine-closure's env. Luckily, if this function is
1815+ /// not correct, then the program is not unsound, since we still borrowck and validate
1816+ /// the choices made from this function -- the only side-effect is that the user
1817+ /// may receive unnecessary borrowck errors.
1818+ fn should_reborrow_from_env_of_parent_coroutine_closure < ' tcx > (
1819+ parent_capture : & ty:: CapturedPlace < ' tcx > ,
1820+ child_capture : & ty:: CapturedPlace < ' tcx > ,
1821+ ) -> bool {
1822+ // (1.)
1823+ ( !parent_capture. is_by_ref ( )
1824+ && !matches ! (
1825+ child_capture. place. projections. get( parent_capture. place. projections. len( ) ) ,
1826+ Some ( Projection { kind: ProjectionKind :: Deref , .. } )
1827+ ) )
1828+ // (2.)
1829+ || matches ! ( child_capture. info. capture_kind, UpvarCapture :: ByRef ( ty:: BorrowKind :: MutBorrow ) )
1830+ }
1831+
17641832/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240,
17651833/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
17661834fn restrict_repr_packed_field_ref_capture < ' tcx > (
0 commit comments