@@ -89,6 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
8989use  rustc_middle:: middle:: region; 
9090use  rustc_middle:: mir:: * ; 
9191use  rustc_middle:: thir:: { ExprId ,  LintLevel } ; 
92+ use  rustc_middle:: ty:: { self ,  TyCtxt } ; 
9293use  rustc_middle:: { bug,  span_bug} ; 
9394use  rustc_session:: lint:: Level ; 
9495use  rustc_span:: source_map:: Spanned ; 
@@ -837,22 +838,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
837838        block. unit ( ) 
838839    } 
839840
841+     fn  is_async_drop_impl ( 
842+         tcx :  TyCtxt < ' tcx > , 
843+         local_decls :  & IndexVec < Local ,  LocalDecl < ' tcx > > , 
844+         param_env :  ty:: ParamEnv < ' tcx > , 
845+         local :  Local , 
846+     )  -> bool  { 
847+         let  ty = local_decls[ local] . ty ; 
848+         if  ty. is_async_drop ( tcx,  param_env)  || ty. is_coroutine ( )  { 
849+             return  true ; 
850+         } 
851+         ty. needs_async_drop ( tcx,  param_env) 
852+     } 
853+     fn  is_async_drop ( & self ,  local :  Local )  -> bool  { 
854+         Self :: is_async_drop_impl ( self . tcx ,  & self . local_decls ,  self . param_env ,  local) 
855+     } 
856+ 
840857    fn  leave_top_scope ( & mut  self ,  block :  BasicBlock )  -> BasicBlock  { 
841858        // If we are emitting a `drop` statement, we need to have the cached 
842859        // diverge cleanup pads ready in case that drop panics. 
843860        let  needs_cleanup = self . scopes . scopes . last ( ) . is_some_and ( |scope| scope. needs_cleanup ( ) ) ; 
844861        let  is_coroutine = self . coroutine . is_some ( ) ; 
845862        let  unwind_to = if  needs_cleanup {  self . diverge_cleanup ( )  }  else  {  DropIdx :: MAX  } ; 
846863
864+         let  scope = self . scopes . scopes . last ( ) . expect ( "leave_top_scope called with no scopes" ) ; 
865+         let  has_async_drops = is_coroutine
866+             && scope. drops . iter ( ) . any ( |v| v. kind  == DropKind :: Value  && self . is_async_drop ( v. local ) ) ; 
867+         let  dropline_to = if  has_async_drops {  Some ( self . diverge_dropline ( ) )  }  else  {  None  } ; 
847868        let  scope = self . scopes . scopes . last ( ) . expect ( "leave_top_scope called with no scopes" ) ; 
848869        build_scope_drops ( 
849870            & mut  self . cfg , 
850871            & mut  self . scopes . unwind_drops , 
872+             & mut  self . scopes . coroutine_drops , 
851873            scope, 
852874            block, 
853875            unwind_to, 
876+             dropline_to, 
854877            is_coroutine && needs_cleanup, 
855878            self . arg_count , 
879+             |v :  Local | Self :: is_async_drop_impl ( self . tcx ,  & self . local_decls ,  self . param_env ,  v) , 
856880        ) 
857881        . into_block ( ) 
858882    } 
@@ -1234,22 +1258,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12341258        self . scopes . unwind_drops . add_entry_point ( start,  next_drop) ; 
12351259    } 
12361260
1237-     /// Sets up a path that performs all required cleanup for dropping a 
1238- /// coroutine, starting from the given block that ends in 
1239- /// [TerminatorKind::Yield]. 
1240- /// 
1241- /// This path terminates in CoroutineDrop. 
1242- pub ( crate )  fn  coroutine_drop_cleanup ( & mut  self ,  yield_block :  BasicBlock )  { 
1261+     /// Returns the [DropIdx] for the innermost drop for dropline (coroutine drop path). 
1262+ /// The `DropIdx` will be created if it doesn't already exist. 
1263+ fn  diverge_dropline ( & mut  self )  -> DropIdx  { 
1264+         // It is okay to use dummy span because the getting scope index on the topmost scope 
1265+         // must always succeed. 
1266+         self . diverge_dropline_target ( self . scopes . topmost ( ) ,  DUMMY_SP ) 
1267+     } 
1268+ 
1269+     /// Similar to diverge_cleanup_target, but for dropline (coroutine drop path) 
1270+ fn  diverge_dropline_target ( & mut  self ,  target_scope :  region:: Scope ,  span :  Span )  -> DropIdx  { 
12431271        debug_assert ! ( 
1244-             matches!( 
1245-                 self . cfg. block_data( yield_block) . terminator( ) . kind, 
1246-                 TerminatorKind :: Yield  {  .. } 
1247-             ) , 
1248-             "coroutine_drop_cleanup called on block with non-yield terminator." 
1272+             self . coroutine. is_some( ) , 
1273+             "diverge_dropline_target is valid only for coroutine" 
12491274        ) ; 
1250-         let  ( uncached_scope,  mut  cached_drop)  = self 
1251-             . scopes 
1252-             . scopes 
1275+         let  target = self . scopes . scope_index ( target_scope,  span) ; 
1276+         let  ( uncached_scope,  mut  cached_drop)  = self . scopes . scopes [ ..=target] 
12531277            . iter ( ) 
12541278            . enumerate ( ) 
12551279            . rev ( ) 
@@ -1258,13 +1282,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12581282            } ) 
12591283            . unwrap_or ( ( 0 ,  ROOT_NODE ) ) ; 
12601284
1261-         for  scope in  & mut  self . scopes . scopes [ uncached_scope..]  { 
1285+         if  uncached_scope > target { 
1286+             return  cached_drop; 
1287+         } 
1288+ 
1289+         for  scope in  & mut  self . scopes . scopes [ uncached_scope..=target]  { 
12621290            for  drop in  & scope. drops  { 
12631291                cached_drop = self . scopes . coroutine_drops . add_drop ( * drop,  cached_drop) ; 
12641292            } 
12651293            scope. cached_coroutine_drop_block  = Some ( cached_drop) ; 
12661294        } 
12671295
1296+         cached_drop
1297+     } 
1298+ 
1299+     /// Sets up a path that performs all required cleanup for dropping a 
1300+ /// coroutine, starting from the given block that ends in 
1301+ /// [TerminatorKind::Yield]. 
1302+ /// 
1303+ /// This path terminates in CoroutineDrop. 
1304+ pub ( crate )  fn  coroutine_drop_cleanup ( & mut  self ,  yield_block :  BasicBlock )  { 
1305+         debug_assert ! ( 
1306+             matches!( 
1307+                 self . cfg. block_data( yield_block) . terminator( ) . kind, 
1308+                 TerminatorKind :: Yield  {  .. } 
1309+             ) , 
1310+             "coroutine_drop_cleanup called on block with non-yield terminator." 
1311+         ) ; 
1312+         let  cached_drop = self . diverge_dropline ( ) ; 
12681313        self . scopes . coroutine_drops . add_entry_point ( yield_block,  cached_drop) ; 
12691314    } 
12701315
@@ -1349,16 +1394,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13491394} 
13501395
13511396/// Builds drops for `pop_scope` and `leave_top_scope`. 
1352- fn  build_scope_drops < ' tcx > ( 
1397+ fn  build_scope_drops < ' tcx ,   F > ( 
13531398    cfg :  & mut  CFG < ' tcx > , 
13541399    unwind_drops :  & mut  DropTree , 
1400+     coroutine_drops :  & mut  DropTree , 
13551401    scope :  & Scope , 
13561402    mut  block :  BasicBlock , 
13571403    mut  unwind_to :  DropIdx , 
1404+     mut  dropline_to :  Option < DropIdx > , 
13581405    storage_dead_on_unwind :  bool , 
13591406    arg_count :  usize , 
1360- )  -> BlockAnd < ( ) >  { 
1361-     debug ! ( "build_scope_drops({:?} -> {:?})" ,  block,  scope) ; 
1407+     is_async_drop :  F , 
1408+ )  -> BlockAnd < ( ) > 
1409+ where 
1410+     F :  Fn ( Local )  -> bool , 
1411+ { 
1412+     debug ! ( "build_scope_drops({:?} -> {:?}), dropline_to={:?}" ,  block,  scope,  dropline_to) ; 
13621413
13631414    // Build up the drops in evaluation order. The end result will 
13641415    // look like: 
@@ -1392,6 +1443,12 @@ fn build_scope_drops<'tcx>(
13921443                debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind,  drop_data. kind) ; 
13931444                unwind_to = unwind_drops. drops [ unwind_to] . next ; 
13941445
1446+                 if  let  Some ( idx)  = dropline_to { 
1447+                     debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. local,  drop_data. local) ; 
1448+                     debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. kind,  drop_data. kind) ; 
1449+                     dropline_to = Some ( coroutine_drops. drops [ idx] . next ) ; 
1450+                 } 
1451+ 
13951452                // If the operand has been moved, and we are not on an unwind 
13961453                // path, then don't generate the drop. (We only take this into 
13971454                // account for non-unwind paths so as not to disturb the 
@@ -1401,6 +1458,11 @@ fn build_scope_drops<'tcx>(
14011458                } 
14021459
14031460                unwind_drops. add_entry_point ( block,  unwind_to) ; 
1461+                 if  let  Some ( to)  = dropline_to
1462+                     && is_async_drop ( local) 
1463+                 { 
1464+                     coroutine_drops. add_entry_point ( block,  to) ; 
1465+                 } 
14041466
14051467                let  next = cfg. start_new_block ( ) ; 
14061468                cfg. terminate ( 
@@ -1423,6 +1485,11 @@ fn build_scope_drops<'tcx>(
14231485                    debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind,  drop_data. kind) ; 
14241486                    unwind_to = unwind_drops. drops [ unwind_to] . next ; 
14251487                } 
1488+                 if  let  Some ( idx)  = dropline_to { 
1489+                     debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. local,  drop_data. local) ; 
1490+                     debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. kind,  drop_data. kind) ; 
1491+                     dropline_to = Some ( coroutine_drops. drops [ idx] . next ) ; 
1492+                 } 
14261493                // Only temps and vars need their storage dead. 
14271494                assert ! ( local. index( )  > arg_count) ; 
14281495                cfg. push ( block,  Statement  {  source_info,  kind :  StatementKind :: StorageDead ( local)  } ) ; 
@@ -1481,6 +1548,39 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
14811548                } 
14821549            } 
14831550        } 
1551+         // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops 
1552+         if  is_coroutine
1553+             && drops. drops . iter ( ) . any ( |DropNode  {  data,  next :  _ } | { 
1554+                 data. kind  == DropKind :: Value  && self . is_async_drop ( data. local ) 
1555+             } ) 
1556+         { 
1557+             let  dropline_target = self . diverge_dropline_target ( else_scope,  span) ; 
1558+             let  mut  dropline_indices = IndexVec :: from_elem_n ( dropline_target,  1 ) ; 
1559+             for  ( drop_idx,  drop_data)  in  drops. drops . iter_enumerated ( ) . skip ( 1 )  { 
1560+                 match  drop_data. data . kind  { 
1561+                     DropKind :: Storage  => { 
1562+                         let  coroutine_drop = self 
1563+                             . scopes 
1564+                             . coroutine_drops 
1565+                             . add_drop ( drop_data. data ,  dropline_indices[ drop_data. next ] ) ; 
1566+                         dropline_indices. push ( coroutine_drop) ; 
1567+                     } 
1568+                     DropKind :: Value  => { 
1569+                         let  coroutine_drop = self 
1570+                             . scopes 
1571+                             . coroutine_drops 
1572+                             . add_drop ( drop_data. data ,  dropline_indices[ drop_data. next ] ) ; 
1573+                         if  self . is_async_drop ( drop_data. data . local )  { 
1574+                             self . scopes . coroutine_drops . add_entry_point ( 
1575+                                 blocks[ drop_idx] . unwrap ( ) , 
1576+                                 dropline_indices[ drop_data. next ] , 
1577+                             ) ; 
1578+                         } 
1579+                         dropline_indices. push ( coroutine_drop) ; 
1580+                     } 
1581+                 } 
1582+             } 
1583+         } 
14841584        blocks[ ROOT_NODE ] . map ( BasicBlock :: unit) 
14851585    } 
14861586
@@ -1526,9 +1626,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
15261626        // to be captured by the coroutine. I'm not sure how important this 
15271627        // optimization is, but it is here. 
15281628        for  ( drop_idx,  drop_node)  in  drops. drops . iter_enumerated ( )  { 
1529-             if  let  DropKind :: Value  = drop_node. data . kind  { 
1629+             if  let  DropKind :: Value  = drop_node. data . kind 
1630+                 && let  Some ( bb)  = blocks[ drop_idx] 
1631+             { 
15301632                debug_assert ! ( drop_node. next < drops. drops. next_index( ) ) ; 
1531-                 drops. entry_points . push ( ( drop_node. next ,  blocks [ drop_idx ] . unwrap ( ) ) ) ; 
1633+                 drops. entry_points . push ( ( drop_node. next ,  bb ) ) ; 
15321634            } 
15331635        } 
15341636        Self :: build_unwind_tree ( cfg,  drops,  fn_span,  resume_block) ; 
@@ -1582,6 +1684,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop {
15821684        let  term = cfg. block_data_mut ( from) . terminator_mut ( ) ; 
15831685        if  let  TerminatorKind :: Yield  {  ref  mut  drop,  .. }  = term. kind  { 
15841686            * drop = Some ( to) ; 
1687+         }  else  if  let  TerminatorKind :: Drop  {  ref  mut  drop,  .. }  = term. kind  { 
1688+             * drop = Some ( to) ; 
15851689        }  else  { 
15861690            span_bug ! ( 
15871691                term. source_info. span, 
0 commit comments