@@ -367,37 +367,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
367
367
ty:: INNERMOST ,
368
368
ty:: BoundRegion { var : ty:: BoundVar :: ZERO , kind : ty:: BoundRegionKind :: BrEnv } ,
369
369
) ;
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
+
370
380
let tupled_upvars_ty_for_borrow = Ty :: new_tup_from_iter (
371
381
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 ;
391
401
// Not all upvars are captured by ref, so use
392
402
// `apply_capture_kind_on_capture_ty` to ensure that we
393
403
// compute the right captured type.
394
- apply_capture_kind_on_capture_ty (
404
+ return apply_capture_kind_on_capture_ty (
395
405
self . tcx ,
396
406
upvar_ty,
397
407
capture,
398
- Some ( closure_env_region) ,
399
- )
400
- } ) ,
408
+ if needs_ref { Some ( closure_env_region) } else { child_capture. region } ,
409
+ ) ;
410
+ } ,
411
+ ) ,
401
412
) ;
402
413
let coroutine_captures_by_ref_ty = Ty :: new_fn_ptr (
403
414
self . tcx ,
@@ -1761,6 +1772,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1761
1772
}
1762
1773
}
1763
1774
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
+
1764
1832
/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240,
1765
1833
/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
1766
1834
fn restrict_repr_packed_field_ref_capture < ' tcx > (
0 commit comments