@@ -319,34 +319,35 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
319
319
}
320
320
321
321
ty:: Coroutine ( _, args) => {
322
- // rust-lang/rust#49918: types can be constructed, stored
323
- // in the interior, and sit idle when coroutine yields
324
- // (and is subsequently dropped).
322
+ // rust-lang/rust#49918: Locals can be stored across await points in the coroutine,
323
+ // called interior/witness types. Since we do not compute these witnesses until after
324
+ // building MIR, we consider all coroutines to unconditionally require a drop during
325
+ // MIR building. However, considering the coroutine to unconditionally be drop-live
326
+ // here may unnecessarily require its upvars' regions to be live when they don't need
327
+ // to be, leading to borrowck errors: <https://github.com/rust-lang/rust/issues/116242>.
325
328
//
326
- // It would be nice to descend into interior of a
327
- // coroutine to determine what effects dropping it might
328
- // have (by looking at any drop effects associated with
329
- // its interior).
329
+ // Here, we implement a more precise approximation for whether the coroutine is
330
+ // drop-live by considering the drop requirement of the witness and upvar types
331
+ // specifically. Note that this is still an approximation because since the coroutine
332
+ // interior has its regions erased, we must add *all* of the upvars to the set of live
333
+ // types if we find that *any* interior type is live. This is because any of the regions
334
+ // captured in the upvars may be stored in the interior, which then has its regions
335
+ // replaced by a binder (conceptually erasing the regions).
330
336
//
331
- // However, the interior's representation uses things like
332
- // CoroutineWitness that explicitly assume they are not
333
- // traversed in such a manner. So instead, we will
334
- // simplify things for now by treating all coroutines as
335
- // if they were like trait objects, where its upvars must
336
- // all be alive for the coroutine's (potential)
337
- // destructor.
338
- //
339
- // In particular, skipping over `_interior` is safe
340
- // because any side-effects from dropping `_interior` can
341
- // only take place through references with lifetimes
342
- // derived from lifetimes attached to the upvars and resume
343
- // argument, and we *do* incorporate those here.
337
+ // For example, if we capture two upvar references `&'1 (), &'2 ()` and have some type
338
+ // in the interior, `for<'r> { NeedsDrop<'r> }`, we have no way to tell whether the
339
+ // region `'r` came from the `'1` or `'2` region, so we require both are live. This
340
+ // could even be unnecessary if `'r` was actually a `'static` region or some region
341
+ // local to the coroutine! That's why it's an approximation.
344
342
let args = args. as_coroutine ( ) ;
345
343
346
- // While we conservatively assume that all coroutines require drop
347
- // to avoid query cycles during MIR building, we can check the actual
348
- // witness during borrowck to avoid unnecessary liveness constraints.
349
- if args. witness ( ) . needs_drop ( tcx, tcx. erase_regions ( typing_env) ) {
344
+ // Note that we don't care about the the resume type has any drops since this is
345
+ // redundant. There is no storage for the resume type, so if it is actually stored
346
+ // in the interior, we'll already detect the need for a drop by checking the witness.
347
+ let typing_env = tcx. erase_regions ( typing_env) ;
348
+ let needs_drop = args. witness ( ) . needs_drop ( tcx, typing_env)
349
+ || args. upvar_tys ( ) . iter ( ) . any ( |ty| ty. needs_drop ( tcx, typing_env) ) ;
350
+ if needs_drop {
350
351
constraints. outlives . extend ( args. upvar_tys ( ) . iter ( ) . map ( ty:: GenericArg :: from) ) ;
351
352
constraints. outlives . push ( args. resume_ty ( ) . into ( ) ) ;
352
353
}
0 commit comments