@@ -203,16 +203,31 @@ const ROOT_NODE: DropIdx = DropIdx::from_u32(0);
203
203
/// in `build_mir`.
204
204
#[ derive( Debug ) ]
205
205
struct DropTree {
206
- /// Drops in the tree.
207
- drops : IndexVec < DropIdx , ( DropData , DropIdx ) > ,
208
- /// Map for finding the inverse of the `next_drop` relation:
209
- ///
210
- /// `previous_drops[(drops[i].1, drops[i].0.local, drops[i].0.kind)] == i`
211
- previous_drops : FxHashMap < ( DropIdx , Local , DropKind ) , DropIdx > ,
206
+ /// Nodes in the drop tree, containing drop data and a link to the next node.
207
+ drops : IndexVec < DropIdx , DropNode > ,
208
+ /// Map for finding the index of an existing node, given its contents.
209
+ existing_drops_map : FxHashMap < DropNodeKey , DropIdx > ,
212
210
/// Edges into the `DropTree` that need to be added once it's lowered.
213
211
entry_points : Vec < ( DropIdx , BasicBlock ) > ,
214
212
}
215
213
214
+ /// A single node in the drop tree.
215
+ #[ derive( Debug ) ]
216
+ struct DropNode {
217
+ /// Info about the drop to be performed at this node in the drop tree.
218
+ data : DropData ,
219
+ /// Index of the "next" drop to perform (in drop order, not declaration order).
220
+ next : DropIdx ,
221
+ }
222
+
223
+ /// Subset of [`DropNode`] used for reverse lookup in a hash table.
224
+ #[ derive( Debug , PartialEq , Eq , Hash ) ]
225
+ struct DropNodeKey {
226
+ next : DropIdx ,
227
+ local : Local ,
228
+ kind : DropKind ,
229
+ }
230
+
216
231
impl Scope {
217
232
/// Whether there's anything to do for the cleanup path, that is,
218
233
/// when unwinding through this scope. This includes destructors,
@@ -258,17 +273,22 @@ impl DropTree {
258
273
let fake_source_info = SourceInfo :: outermost ( DUMMY_SP ) ;
259
274
let fake_data =
260
275
DropData { source_info : fake_source_info, local : Local :: MAX , kind : DropKind :: Storage } ;
261
- let drop_idx = DropIdx :: MAX ;
262
- let drops = IndexVec :: from_elem_n ( ( fake_data, drop_idx) , 1 ) ;
263
- Self { drops, entry_points : Vec :: new ( ) , previous_drops : FxHashMap :: default ( ) }
276
+ let drops = IndexVec :: from_raw ( vec ! [ DropNode { data: fake_data, next: DropIdx :: MAX } ] ) ;
277
+ Self { drops, entry_points : Vec :: new ( ) , existing_drops_map : FxHashMap :: default ( ) }
264
278
}
265
279
266
- fn add_drop ( & mut self , drop : DropData , next : DropIdx ) -> DropIdx {
280
+ /// Adds a node to the drop tree, consisting of drop data and the index of
281
+ /// the "next" drop (in drop order), which could be the sentinel [`ROOT_NODE`].
282
+ ///
283
+ /// If there is already an equivalent node in the tree, nothing is added, and
284
+ /// that node's index is returned. Otherwise, the new node's index is returned.
285
+ fn add_drop ( & mut self , data : DropData , next : DropIdx ) -> DropIdx {
267
286
let drops = & mut self . drops ;
268
287
* self
269
- . previous_drops
270
- . entry ( ( next, drop. local , drop. kind ) )
271
- . or_insert_with ( || drops. push ( ( drop, next) ) )
288
+ . existing_drops_map
289
+ . entry ( DropNodeKey { next, local : data. local , kind : data. kind } )
290
+ // Create a new node, and also add its index to the map.
291
+ . or_insert_with ( || drops. push ( DropNode { data, next } ) )
272
292
}
273
293
274
294
/// Registers `from` as an entry point to this drop tree, at `to`.
@@ -330,7 +350,7 @@ impl DropTree {
330
350
let entry_points = & mut self . entry_points ;
331
351
entry_points. sort ( ) ;
332
352
333
- for ( drop_idx, drop_data ) in self . drops . iter_enumerated ( ) . rev ( ) {
353
+ for ( drop_idx, drop_node ) in self . drops . iter_enumerated ( ) . rev ( ) {
334
354
if entry_points. last ( ) . is_some_and ( |entry_point| entry_point. 0 == drop_idx) {
335
355
let block = * blocks[ drop_idx] . get_or_insert_with ( || T :: make_block ( cfg) ) ;
336
356
needs_block[ drop_idx] = Block :: Own ;
@@ -348,10 +368,10 @@ impl DropTree {
348
368
blocks[ drop_idx] = blocks[ pred] ;
349
369
}
350
370
}
351
- if let DropKind :: Value = drop_data . 0 . kind {
352
- needs_block[ drop_data . 1 ] = Block :: Own ;
371
+ if let DropKind :: Value = drop_node . data . kind {
372
+ needs_block[ drop_node . next ] = Block :: Own ;
353
373
} else if drop_idx != ROOT_NODE {
354
- match & mut needs_block[ drop_data . 1 ] {
374
+ match & mut needs_block[ drop_node . next ] {
355
375
pred @ Block :: None => * pred = Block :: Shares ( drop_idx) ,
356
376
pred @ Block :: Shares ( _) => * pred = Block :: Own ,
357
377
Block :: Own => ( ) ,
@@ -368,34 +388,35 @@ impl DropTree {
368
388
cfg : & mut CFG < ' tcx > ,
369
389
blocks : & IndexSlice < DropIdx , Option < BasicBlock > > ,
370
390
) {
371
- for ( drop_idx, drop_data ) in self . drops . iter_enumerated ( ) . rev ( ) {
391
+ for ( drop_idx, drop_node ) in self . drops . iter_enumerated ( ) . rev ( ) {
372
392
let Some ( block) = blocks[ drop_idx] else { continue } ;
373
- match drop_data . 0 . kind {
393
+ match drop_node . data . kind {
374
394
DropKind :: Value => {
375
395
let terminator = TerminatorKind :: Drop {
376
- target : blocks[ drop_data . 1 ] . unwrap ( ) ,
396
+ target : blocks[ drop_node . next ] . unwrap ( ) ,
377
397
// The caller will handle this if needed.
378
398
unwind : UnwindAction :: Terminate ( UnwindTerminateReason :: InCleanup ) ,
379
- place : drop_data . 0 . local . into ( ) ,
399
+ place : drop_node . data . local . into ( ) ,
380
400
replace : false ,
381
401
} ;
382
- cfg. terminate ( block, drop_data . 0 . source_info , terminator) ;
402
+ cfg. terminate ( block, drop_node . data . source_info , terminator) ;
383
403
}
384
404
// Root nodes don't correspond to a drop.
385
405
DropKind :: Storage if drop_idx == ROOT_NODE => { }
386
406
DropKind :: Storage => {
387
407
let stmt = Statement {
388
- source_info : drop_data . 0 . source_info ,
389
- kind : StatementKind :: StorageDead ( drop_data . 0 . local ) ,
408
+ source_info : drop_node . data . source_info ,
409
+ kind : StatementKind :: StorageDead ( drop_node . data . local ) ,
390
410
} ;
391
411
cfg. push ( block, stmt) ;
392
- let target = blocks[ drop_data . 1 ] . unwrap ( ) ;
412
+ let target = blocks[ drop_node . next ] . unwrap ( ) ;
393
413
if target != block {
394
414
// Diagnostics don't use this `Span` but debuginfo
395
415
// might. Since we don't want breakpoints to be placed
396
416
// here, especially when this is on an unwind path, we
397
417
// use `DUMMY_SP`.
398
- let source_info = SourceInfo { span : DUMMY_SP , ..drop_data. 0 . source_info } ;
418
+ let source_info =
419
+ SourceInfo { span : DUMMY_SP , ..drop_node. data . source_info } ;
399
420
let terminator = TerminatorKind :: Goto { target } ;
400
421
cfg. terminate ( block, source_info, terminator) ;
401
422
}
@@ -1277,9 +1298,9 @@ fn build_scope_drops<'tcx>(
1277
1298
// `unwind_to` should drop the value that we're about to
1278
1299
// schedule. If dropping this value panics, then we continue
1279
1300
// with the *next* value on the unwind path.
1280
- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . local, drop_data. local) ;
1281
- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . kind, drop_data. kind) ;
1282
- unwind_to = unwind_drops. drops [ unwind_to] . 1 ;
1301
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . local, drop_data. local) ;
1302
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . kind, drop_data. kind) ;
1303
+ unwind_to = unwind_drops. drops [ unwind_to] . next ;
1283
1304
1284
1305
// If the operand has been moved, and we are not on an unwind
1285
1306
// path, then don't generate the drop. (We only take this into
@@ -1306,9 +1327,9 @@ fn build_scope_drops<'tcx>(
1306
1327
}
1307
1328
DropKind :: Storage => {
1308
1329
if storage_dead_on_unwind {
1309
- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . local, drop_data. local) ;
1310
- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . kind, drop_data. kind) ;
1311
- unwind_to = unwind_drops. drops [ unwind_to] . 1 ;
1330
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . local, drop_data. local) ;
1331
+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . kind, drop_data. kind) ;
1332
+ unwind_to = unwind_drops. drops [ unwind_to] . next ;
1312
1333
}
1313
1334
// Only temps and vars need their storage dead.
1314
1335
assert ! ( local. index( ) > arg_count) ;
@@ -1338,30 +1359,30 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1338
1359
let is_coroutine = self . coroutine . is_some ( ) ;
1339
1360
1340
1361
// Link the exit drop tree to unwind drop tree.
1341
- if drops. drops . iter ( ) . any ( |( drop , _ ) | drop . kind == DropKind :: Value ) {
1362
+ if drops. drops . iter ( ) . any ( |drop_node| drop_node . data . kind == DropKind :: Value ) {
1342
1363
let unwind_target = self . diverge_cleanup_target ( else_scope, span) ;
1343
1364
let mut unwind_indices = IndexVec :: from_elem_n ( unwind_target, 1 ) ;
1344
- for ( drop_idx, drop_data ) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1345
- match drop_data . 0 . kind {
1365
+ for ( drop_idx, drop_node ) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1366
+ match drop_node . data . kind {
1346
1367
DropKind :: Storage => {
1347
1368
if is_coroutine {
1348
1369
let unwind_drop = self
1349
1370
. scopes
1350
1371
. unwind_drops
1351
- . add_drop ( drop_data . 0 , unwind_indices[ drop_data . 1 ] ) ;
1372
+ . add_drop ( drop_node . data , unwind_indices[ drop_node . next ] ) ;
1352
1373
unwind_indices. push ( unwind_drop) ;
1353
1374
} else {
1354
- unwind_indices. push ( unwind_indices[ drop_data . 1 ] ) ;
1375
+ unwind_indices. push ( unwind_indices[ drop_node . next ] ) ;
1355
1376
}
1356
1377
}
1357
1378
DropKind :: Value => {
1358
1379
let unwind_drop = self
1359
1380
. scopes
1360
1381
. unwind_drops
1361
- . add_drop ( drop_data . 0 , unwind_indices[ drop_data . 1 ] ) ;
1382
+ . add_drop ( drop_node . data , unwind_indices[ drop_node . next ] ) ;
1362
1383
self . scopes . unwind_drops . add_entry_point (
1363
1384
blocks[ drop_idx] . unwrap ( ) ,
1364
- unwind_indices[ drop_data . 1 ] ,
1385
+ unwind_indices[ drop_node . next ] ,
1365
1386
) ;
1366
1387
unwind_indices. push ( unwind_drop) ;
1367
1388
}
@@ -1412,10 +1433,10 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1412
1433
// prevent drop elaboration from creating drop flags that would have
1413
1434
// to be captured by the coroutine. I'm not sure how important this
1414
1435
// optimization is, but it is here.
1415
- for ( drop_idx, drop_data ) in drops. drops . iter_enumerated ( ) {
1416
- if let DropKind :: Value = drop_data . 0 . kind {
1417
- debug_assert ! ( drop_data . 1 < drops. drops. next_index( ) ) ;
1418
- drops. entry_points . push ( ( drop_data . 1 , blocks[ drop_idx] . unwrap ( ) ) ) ;
1436
+ for ( drop_idx, drop_node ) in drops. drops . iter_enumerated ( ) {
1437
+ if let DropKind :: Value = drop_node . data . kind {
1438
+ debug_assert ! ( drop_node . next < drops. drops. next_index( ) ) ;
1439
+ drops. entry_points . push ( ( drop_node . next , blocks[ drop_idx] . unwrap ( ) ) ) ;
1419
1440
}
1420
1441
}
1421
1442
Self :: build_unwind_tree ( cfg, drops, fn_span, resume_block) ;
0 commit comments