@@ -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