@@ -151,15 +151,13 @@ struct DropData {
151
151
152
152
/// Whether this is a value Drop or a StorageDead.
153
153
kind : DropKind ,
154
-
155
- /// Whether this is a backwards-incompatible drop lint
156
- backwards_incompatible_lint : bool ,
157
154
}
158
155
159
156
#[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
160
157
pub ( crate ) enum DropKind {
161
158
Value ,
162
159
Storage ,
160
+ ForLint ,
163
161
}
164
162
165
163
#[ derive( Debug ) ]
@@ -248,7 +246,7 @@ impl Scope {
248
246
/// use of optimizations in the MIR coroutine transform.
249
247
fn needs_cleanup ( & self ) -> bool {
250
248
self . drops . iter ( ) . any ( |drop| match drop. kind {
251
- DropKind :: Value => true ,
249
+ DropKind :: Value | DropKind :: ForLint => true ,
252
250
DropKind :: Storage => false ,
253
251
} )
254
252
}
@@ -277,12 +275,8 @@ impl DropTree {
277
275
// represents the block in the tree that should be jumped to once all
278
276
// of the required drops have been performed.
279
277
let fake_source_info = SourceInfo :: outermost ( DUMMY_SP ) ;
280
- let fake_data = DropData {
281
- source_info : fake_source_info,
282
- local : Local :: MAX ,
283
- kind : DropKind :: Storage ,
284
- backwards_incompatible_lint : false ,
285
- } ;
278
+ let fake_data =
279
+ DropData { source_info : fake_source_info, local : Local :: MAX , kind : DropKind :: Storage } ;
286
280
let drops = IndexVec :: from_raw ( vec ! [ DropNode { data: fake_data, next: DropIdx :: MAX } ] ) ;
287
281
Self { drops, entry_points : Vec :: new ( ) , existing_drops_map : FxHashMap :: default ( ) }
288
282
}
@@ -411,6 +405,27 @@ impl DropTree {
411
405
} ;
412
406
cfg. terminate ( block, drop_node. data . source_info , terminator) ;
413
407
}
408
+ DropKind :: ForLint => {
409
+ let stmt = Statement {
410
+ source_info : drop_node. data . source_info ,
411
+ kind : StatementKind :: BackwardIncompatibleDropHint {
412
+ place : Box :: new ( drop_node. data . local . into ( ) ) ,
413
+ reason : BackwardIncompatibleDropReason :: Edition2024 ,
414
+ } ,
415
+ } ;
416
+ cfg. push ( block, stmt) ;
417
+ let target = blocks[ drop_node. next ] . unwrap ( ) ;
418
+ if target != block {
419
+ // Diagnostics don't use this `Span` but debuginfo
420
+ // might. Since we don't want breakpoints to be placed
421
+ // here, especially when this is on an unwind path, we
422
+ // use `DUMMY_SP`.
423
+ let source_info =
424
+ SourceInfo { span : DUMMY_SP , ..drop_node. data . source_info } ;
425
+ let terminator = TerminatorKind :: Goto { target } ;
426
+ cfg. terminate ( block, source_info, terminator) ;
427
+ }
428
+ }
414
429
// Root nodes don't correspond to a drop.
415
430
DropKind :: Storage if drop_idx == ROOT_NODE => { }
416
431
DropKind :: Storage => {
@@ -770,12 +785,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
770
785
let local =
771
786
place. as_local ( ) . unwrap_or_else ( || bug ! ( "projection in tail call args" ) ) ;
772
787
773
- Some ( DropData {
774
- source_info,
775
- local,
776
- kind : DropKind :: Value ,
777
- backwards_incompatible_lint : false ,
778
- } )
788
+ Some ( DropData { source_info, local, kind : DropKind :: Value } )
779
789
}
780
790
Operand :: Constant ( _) => None ,
781
791
} )
@@ -822,6 +832,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
822
832
} ) ;
823
833
block = next;
824
834
}
835
+ DropKind :: ForLint => {
836
+ self . cfg . push ( block, Statement {
837
+ source_info,
838
+ kind : StatementKind :: BackwardIncompatibleDropHint {
839
+ place : Box :: new ( local. into ( ) ) ,
840
+ reason : BackwardIncompatibleDropReason :: Edition2024 ,
841
+ } ,
842
+ } ) ;
843
+ }
825
844
DropKind :: Storage => {
826
845
// Only temps and vars need their storage dead.
827
846
assert ! ( local. index( ) > self . arg_count) ;
@@ -1021,7 +1040,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1021
1040
drop_kind : DropKind ,
1022
1041
) {
1023
1042
let needs_drop = match drop_kind {
1024
- DropKind :: Value => {
1043
+ DropKind :: Value | DropKind :: ForLint => {
1025
1044
if !self . local_decls [ local] . ty . needs_drop ( self . tcx , self . typing_env ( ) ) {
1026
1045
return ;
1027
1046
}
@@ -1101,7 +1120,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1101
1120
source_info : SourceInfo { span : scope_end, scope : scope. source_scope } ,
1102
1121
local,
1103
1122
kind : drop_kind,
1104
- backwards_incompatible_lint : false ,
1105
1123
} ) ;
1106
1124
1107
1125
return ;
@@ -1135,8 +1153,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1135
1153
scope. drops . push ( DropData {
1136
1154
source_info : SourceInfo { span : scope_end, scope : scope. source_scope } ,
1137
1155
local,
1138
- kind : DropKind :: Value ,
1139
- backwards_incompatible_lint : true ,
1156
+ kind : DropKind :: ForLint ,
1140
1157
} ) ;
1141
1158
1142
1159
return ;
@@ -1379,12 +1396,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1379
1396
}
1380
1397
1381
1398
/// Builds drops for `pop_scope` and `leave_top_scope`.
1399
+ ///
1400
+ /// # Parameters
1401
+ ///
1402
+ /// * `unwind_drops`, the drop tree data structure storing what needs to be cleaned up if unwind occurs
1403
+ /// * `scope`, describes the drops that will occur on exiting the scope in regular execution
1404
+ /// * `block`, the block to branch to once drops are complete (assuming no unwind occurs)
1405
+ /// * `unwind_to`, describes the drops that would occur at this point in the code if a
1406
+ /// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other
1407
+ /// instructions on unwinding)
1408
+ /// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding
1409
+ /// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those)
1382
1410
fn build_scope_drops < ' tcx > (
1383
1411
cfg : & mut CFG < ' tcx > ,
1384
1412
unwind_drops : & mut DropTree ,
1385
1413
scope : & Scope ,
1386
- mut block : BasicBlock ,
1387
- mut unwind_to : DropIdx ,
1414
+ block : BasicBlock ,
1415
+ unwind_to : DropIdx ,
1388
1416
storage_dead_on_unwind : bool ,
1389
1417
arg_count : usize ,
1390
1418
) -> BlockAnd < ( ) > {
@@ -1409,6 +1437,18 @@ fn build_scope_drops<'tcx>(
1409
1437
// drops for the unwind path should have already been generated by
1410
1438
// `diverge_cleanup_gen`.
1411
1439
1440
+ // `unwind_to` indicates what needs to be dropped should unwinding occur.
1441
+ // This is a subset of what needs to be dropped when exiting the scope.
1442
+ // As we unwind the scope, we will also move `unwind_to` backwards to match,
1443
+ // so that we can use it should a destructor panic.
1444
+ let mut unwind_to = unwind_to;
1445
+
1446
+ // The block that we should jump to after drops complete. We start by building the final drop (`drops[n]`
1447
+ // in the diagram above) and then build the drops (e.g., `drop[1]`, `drop[0]`) that come before it.
1448
+ // block begins as the successor of `drops[n]` and then becomes `drops[n]` so that `drops[n-1]`
1449
+ // will branch to `drops[n]`.
1450
+ let mut block = block;
1451
+
1412
1452
for drop_data in scope. drops . iter ( ) . rev ( ) {
1413
1453
let source_info = drop_data. source_info ;
1414
1454
let local = drop_data. local ;
@@ -1418,6 +1458,9 @@ fn build_scope_drops<'tcx>(
1418
1458
// `unwind_to` should drop the value that we're about to
1419
1459
// schedule. If dropping this value panics, then we continue
1420
1460
// with the *next* value on the unwind path.
1461
+ //
1462
+ // We adjust this BEFORE we create the drop (e.g., `drops[n]`)
1463
+ // because `drops[n]` should unwind to `drops[n-1]`.
1421
1464
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. local, drop_data. local) ;
1422
1465
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
1423
1466
unwind_to = unwind_drops. drops [ unwind_to] . next ;
@@ -1430,27 +1473,50 @@ fn build_scope_drops<'tcx>(
1430
1473
continue ;
1431
1474
}
1432
1475
1433
- if drop_data. backwards_incompatible_lint {
1434
- cfg. push ( block, Statement {
1435
- source_info,
1436
- kind : StatementKind :: BackwardIncompatibleDropHint {
1437
- place : Box :: new ( local. into ( ) ) ,
1438
- reason : BackwardIncompatibleDropReason :: Edition2024 ,
1439
- } ,
1440
- } ) ;
1441
- } else {
1442
- unwind_drops. add_entry_point ( block, unwind_to) ;
1443
- let next = cfg. start_new_block ( ) ;
1444
- cfg. terminate ( block, source_info, TerminatorKind :: Drop {
1445
- place : local. into ( ) ,
1446
- target : next,
1447
- unwind : UnwindAction :: Continue ,
1448
- replace : false ,
1449
- } ) ;
1450
- block = next;
1476
+ unwind_drops. add_entry_point ( block, unwind_to) ;
1477
+ let next = cfg. start_new_block ( ) ;
1478
+ cfg. terminate ( block, source_info, TerminatorKind :: Drop {
1479
+ place : local. into ( ) ,
1480
+ target : next,
1481
+ unwind : UnwindAction :: Continue ,
1482
+ replace : false ,
1483
+ } ) ;
1484
+ block = next;
1485
+ }
1486
+ DropKind :: ForLint => {
1487
+ // As in the `DropKind::Storage` case below:
1488
+ // normally lint-related drops are not emitted for unwind,
1489
+ // so we can just leave `unwind_to` unmodified, but in some
1490
+ // cases we emit things ALSO on the unwind path, so we need to adjust
1491
+ // `unwind_to` in that case.
1492
+ if storage_dead_on_unwind {
1493
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. local, drop_data. local) ;
1494
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
1495
+ unwind_to = unwind_drops. drops [ unwind_to] . next ;
1451
1496
}
1497
+
1498
+ // If the operand has been moved, and we are not on an unwind
1499
+ // path, then don't generate the drop. (We only take this into
1500
+ // account for non-unwind paths so as not to disturb the
1501
+ // caching mechanism.)
1502
+ if scope. moved_locals . iter ( ) . any ( |& o| o == local) {
1503
+ continue ;
1504
+ }
1505
+
1506
+ cfg. push ( block, Statement {
1507
+ source_info,
1508
+ kind : StatementKind :: BackwardIncompatibleDropHint {
1509
+ place : Box :: new ( local. into ( ) ) ,
1510
+ reason : BackwardIncompatibleDropReason :: Edition2024 ,
1511
+ } ,
1512
+ } ) ;
1452
1513
}
1453
1514
DropKind :: Storage => {
1515
+ // Ordinarily, storage-dead nodes are not emitted on unwind, so we don't
1516
+ // need to adjust `unwind_to` on this path. However, in some specific cases
1517
+ // we *do* emit storage-dead nodes on the unwind path, and in that case now that
1518
+ // the storage-dead has completed, we need to adjust the `unwind_to` pointer
1519
+ // so that any future drops we emit will not register storage-dead.
1454
1520
if storage_dead_on_unwind {
1455
1521
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. local, drop_data. local) ;
1456
1522
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
@@ -1489,7 +1555,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1489
1555
let mut unwind_indices = IndexVec :: from_elem_n ( unwind_target, 1 ) ;
1490
1556
for ( drop_idx, drop_node) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1491
1557
match drop_node. data . kind {
1492
- DropKind :: Storage => {
1558
+ DropKind :: Storage | DropKind :: ForLint => {
1493
1559
if is_coroutine {
1494
1560
let unwind_drop = self
1495
1561
. scopes
0 commit comments