@@ -89,6 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
89
89
use rustc_middle:: middle:: region;
90
90
use rustc_middle:: mir:: * ;
91
91
use rustc_middle:: thir:: { ExprId , LintLevel } ;
92
+ use rustc_middle:: ty:: { self , TyCtxt } ;
92
93
use rustc_middle:: { bug, span_bug} ;
93
94
use rustc_session:: lint:: Level ;
94
95
use rustc_span:: source_map:: Spanned ;
@@ -837,22 +838,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
837
838
block. unit ( )
838
839
}
839
840
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
+
840
857
fn leave_top_scope ( & mut self , block : BasicBlock ) -> BasicBlock {
841
858
// If we are emitting a `drop` statement, we need to have the cached
842
859
// diverge cleanup pads ready in case that drop panics.
843
860
let needs_cleanup = self . scopes . scopes . last ( ) . is_some_and ( |scope| scope. needs_cleanup ( ) ) ;
844
861
let is_coroutine = self . coroutine . is_some ( ) ;
845
862
let unwind_to = if needs_cleanup { self . diverge_cleanup ( ) } else { DropIdx :: MAX } ;
846
863
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 } ;
847
868
let scope = self . scopes . scopes . last ( ) . expect ( "leave_top_scope called with no scopes" ) ;
848
869
build_scope_drops (
849
870
& mut self . cfg ,
850
871
& mut self . scopes . unwind_drops ,
872
+ & mut self . scopes . coroutine_drops ,
851
873
scope,
852
874
block,
853
875
unwind_to,
876
+ dropline_to,
854
877
is_coroutine && needs_cleanup,
855
878
self . arg_count ,
879
+ |v : Local | Self :: is_async_drop_impl ( self . tcx , & self . local_decls , self . param_env , v) ,
856
880
)
857
881
. into_block ( )
858
882
}
@@ -1234,22 +1258,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1234
1258
self . scopes . unwind_drops . add_entry_point ( start, next_drop) ;
1235
1259
}
1236
1260
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 {
1243
1271
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"
1249
1274
) ;
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]
1253
1277
. iter ( )
1254
1278
. enumerate ( )
1255
1279
. rev ( )
@@ -1258,13 +1282,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1258
1282
} )
1259
1283
. unwrap_or ( ( 0 , ROOT_NODE ) ) ;
1260
1284
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] {
1262
1290
for drop in & scope. drops {
1263
1291
cached_drop = self . scopes . coroutine_drops . add_drop ( * drop, cached_drop) ;
1264
1292
}
1265
1293
scope. cached_coroutine_drop_block = Some ( cached_drop) ;
1266
1294
}
1267
1295
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 ( ) ;
1268
1313
self . scopes . coroutine_drops . add_entry_point ( yield_block, cached_drop) ;
1269
1314
}
1270
1315
@@ -1349,16 +1394,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1349
1394
}
1350
1395
1351
1396
/// Builds drops for `pop_scope` and `leave_top_scope`.
1352
- fn build_scope_drops < ' tcx > (
1397
+ fn build_scope_drops < ' tcx , F > (
1353
1398
cfg : & mut CFG < ' tcx > ,
1354
1399
unwind_drops : & mut DropTree ,
1400
+ coroutine_drops : & mut DropTree ,
1355
1401
scope : & Scope ,
1356
1402
mut block : BasicBlock ,
1357
1403
mut unwind_to : DropIdx ,
1404
+ mut dropline_to : Option < DropIdx > ,
1358
1405
storage_dead_on_unwind : bool ,
1359
1406
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) ;
1362
1413
1363
1414
// Build up the drops in evaluation order. The end result will
1364
1415
// look like:
@@ -1392,6 +1443,12 @@ fn build_scope_drops<'tcx>(
1392
1443
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
1393
1444
unwind_to = unwind_drops. drops [ unwind_to] . next ;
1394
1445
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
+
1395
1452
// If the operand has been moved, and we are not on an unwind
1396
1453
// path, then don't generate the drop. (We only take this into
1397
1454
// account for non-unwind paths so as not to disturb the
@@ -1401,6 +1458,11 @@ fn build_scope_drops<'tcx>(
1401
1458
}
1402
1459
1403
1460
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
+ }
1404
1466
1405
1467
let next = cfg. start_new_block ( ) ;
1406
1468
cfg. terminate (
@@ -1423,6 +1485,11 @@ fn build_scope_drops<'tcx>(
1423
1485
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
1424
1486
unwind_to = unwind_drops. drops [ unwind_to] . next ;
1425
1487
}
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
+ }
1426
1493
// Only temps and vars need their storage dead.
1427
1494
assert ! ( local. index( ) > arg_count) ;
1428
1495
cfg. push ( block, Statement { source_info, kind : StatementKind :: StorageDead ( local) } ) ;
@@ -1481,6 +1548,39 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1481
1548
}
1482
1549
}
1483
1550
}
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
+ }
1484
1584
blocks[ ROOT_NODE ] . map ( BasicBlock :: unit)
1485
1585
}
1486
1586
@@ -1526,9 +1626,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1526
1626
// to be captured by the coroutine. I'm not sure how important this
1527
1627
// optimization is, but it is here.
1528
1628
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
+ {
1530
1632
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 ) ) ;
1532
1634
}
1533
1635
}
1534
1636
Self :: build_unwind_tree ( cfg, drops, fn_span, resume_block) ;
@@ -1582,6 +1684,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop {
1582
1684
let term = cfg. block_data_mut ( from) . terminator_mut ( ) ;
1583
1685
if let TerminatorKind :: Yield { ref mut drop, .. } = term. kind {
1584
1686
* drop = Some ( to) ;
1687
+ } else if let TerminatorKind :: Drop { ref mut drop, .. } = term. kind {
1688
+ * drop = Some ( to) ;
1585
1689
} else {
1586
1690
span_bug ! (
1587
1691
term. source_info. span,
0 commit comments