1
1
#![ deny( rustc:: untranslatable_diagnostic) ]
2
2
#![ deny( rustc:: diagnostic_outside_of_impl) ]
3
- use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
4
- use rustc_index:: bit_set:: { BitSet , SparseBitMatrix } ;
3
+ use rustc_data_structures:: fx:: FxIndexMap ;
4
+ use rustc_data_structures:: graph:: WithSuccessors ;
5
+ use rustc_index:: bit_set:: BitSet ;
5
6
use rustc_middle:: mir:: {
6
7
self , BasicBlock , Body , CallReturnPlaces , Location , Place , TerminatorEdges ,
7
8
} ;
@@ -13,8 +14,6 @@ use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
13
14
use rustc_mir_dataflow:: { Analysis , Direction , Results } ;
14
15
use std:: fmt;
15
16
16
- use crate :: constraints:: ConstraintSccIndex ;
17
- use crate :: region_infer:: values:: PointIndex ;
18
17
use crate :: { places_conflict, BorrowSet , PlaceConflictBias , PlaceExt , RegionInferenceContext } ;
19
18
20
19
/// A tuple with named fields that can hold either the results or the transient state of the
@@ -246,65 +245,17 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
246
245
body : & ' a Body < ' tcx > ,
247
246
regioncx : & ' a RegionInferenceContext < ' tcx > ,
248
247
249
- sccs_live_at_all_points : FxHashSet < ConstraintSccIndex > ,
250
- live_sccs_per_point : SparseBitMatrix < PointIndex , ConstraintSccIndex > ,
251
-
252
- reachability : BitSet < ConstraintSccIndex > ,
253
- reachability_stack : Vec < ConstraintSccIndex > ,
254
-
255
248
loans_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
256
249
}
257
250
258
251
impl < ' a , ' tcx > PoloniusOutOfScopePrecomputer < ' a , ' tcx > {
259
252
fn new ( body : & ' a Body < ' tcx > , regioncx : & ' a RegionInferenceContext < ' tcx > ) -> Self {
260
- let sccs = regioncx. constraint_sccs ( ) ;
261
- let num_sccs = sccs. num_sccs ( ) ;
262
-
263
- // Compute the list of SCCs that are live at all points, as it will be used for all the
264
- // loan scopes we'll compute.
265
- // FIXME: they're surely already available somewhere.
266
- let sccs_live_at_all_points: FxHashSet < _ > = regioncx
267
- . regions ( )
268
- . filter ( |& r| {
269
- use rustc_infer:: infer:: { NllRegionVariableOrigin , RegionVariableOrigin } ;
270
- let origin = regioncx. var_infos [ r] . origin ;
271
- let live_at_all_points = matches ! (
272
- origin,
273
- RegionVariableOrigin :: Nll (
274
- NllRegionVariableOrigin :: Placeholder ( _)
275
- | NllRegionVariableOrigin :: FreeRegion
276
- )
277
- ) ;
278
- live_at_all_points
279
- } )
280
- . map ( |r| sccs. scc ( r) )
281
- . collect ( ) ;
282
-
283
- // Pre-compute the set of live SCCs per point
284
- let liveness = regioncx. liveness_values ( ) ;
285
- let mut live_sccs_per_point = SparseBitMatrix :: new ( num_sccs) ;
286
-
287
- for region in liveness. rows ( ) {
288
- let scc = sccs. scc ( region) ;
289
- if sccs_live_at_all_points. contains ( & scc) {
290
- continue ;
291
- }
292
- for location in liveness. get_elements ( region) {
293
- let point = liveness. point_from_location ( location) ;
294
- live_sccs_per_point. insert ( point, scc) ;
295
- }
296
- }
297
-
298
253
Self {
299
254
visited : BitSet :: new_empty ( body. basic_blocks . len ( ) ) ,
300
255
visit_stack : vec ! [ ] ,
301
256
body,
302
257
regioncx,
303
258
loans_out_of_scope_at_location : FxIndexMap :: default ( ) ,
304
- sccs_live_at_all_points,
305
- live_sccs_per_point,
306
- reachability : BitSet :: new_empty ( num_sccs) ,
307
- reachability_stack : vec ! [ ] ,
308
259
}
309
260
}
310
261
}
@@ -317,66 +268,50 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
317
268
& mut self ,
318
269
loan_idx : BorrowIndex ,
319
270
issuing_region : RegionVid ,
320
- first_location : Location ,
271
+ loan_issued_at : Location ,
321
272
) {
322
- // Let's precompute the reachability set of the issuing region, via reachability on the
323
- // condensation graph. We can also early return when reaching regions that outlive free
324
- // regions via member constraints. (The `OutOfScopePrecomputer` wouldn't be called on a
325
- // region that outlives free regions via outlives constraints.)
326
-
327
273
let sccs = self . regioncx . constraint_sccs ( ) ;
328
-
329
274
let issuing_region_scc = sccs. scc ( issuing_region) ;
330
- self . reachability_stack . push ( issuing_region_scc) ;
331
- self . reachability . insert ( issuing_region_scc) ;
332
275
333
- while let Some ( scc ) = self . reachability_stack . pop ( ) {
334
- // Handle successors of this SCC:
335
- //
276
+ // We first handle the cases where the loan doesn't go out of scope, depending on the issuing
277
+ // region's successors.
278
+ for scc in sccs . depth_first_search ( issuing_region_scc ) {
336
279
// 1. Via member constraints
337
280
//
338
- // The issuing region can flow into the choice regions here , and they are either:
281
+ // The issuing region can flow into the choice regions, and they are either:
339
282
// - placeholders or free regions themselves,
340
283
// - or also transitively outlive a free region.
341
284
//
342
285
// That is to say, if there are member constraints here, the loan escapes the function
343
286
// and cannot go out of scope. We can early return.
344
- //
287
+ if self . regioncx . scc_has_member_constraints ( scc) {
288
+ return ;
289
+ }
290
+
345
291
// 2. Via regions that are live at all points: placeholders and free regions.
346
292
//
347
293
// If the issuing region outlives such a region, its loan escapes the function and
348
294
// cannot go out of scope. We can early return.
349
- if self . regioncx . scc_has_member_constraints ( scc)
350
- || self . sccs_live_at_all_points . contains ( & scc)
351
- {
352
- self . reachability_stack . clear ( ) ;
353
- self . reachability . clear ( ) ;
295
+ if self . regioncx . scc_is_live_at_all_points ( scc) {
354
296
return ;
355
297
}
356
-
357
- // 3. Via outlives successors, which we want to record and traverse: we add them to the
358
- // worklist stack
359
- for & succ_scc in sccs. successors ( scc) {
360
- if self . reachability . insert ( succ_scc) {
361
- self . reachability_stack . push ( succ_scc) ;
362
- }
363
- }
364
298
}
365
299
366
- let first_block = first_location . block ;
300
+ let first_block = loan_issued_at . block ;
367
301
let first_bb_data = & self . body . basic_blocks [ first_block] ;
368
302
369
303
// The first block we visit is the one where the loan is issued, starting from the statement
370
- // where the loan is issued: at `first_location `.
371
- let first_lo = first_location . statement_index ;
304
+ // where the loan is issued: at `loan_issued_at `.
305
+ let first_lo = loan_issued_at . statement_index ;
372
306
let first_hi = first_bb_data. statements . len ( ) ;
373
307
374
- if let Some ( kill_location) = self . loan_kill_location ( first_block, first_lo, first_hi) {
308
+ if let Some ( kill_location) =
309
+ self . loan_kill_location ( loan_idx, loan_issued_at, first_block, first_lo, first_hi)
310
+ {
375
311
debug ! ( "loan {:?} gets killed at {:?}" , loan_idx, kill_location) ;
376
312
self . loans_out_of_scope_at_location . entry ( kill_location) . or_default ( ) . push ( loan_idx) ;
377
313
378
314
// The loan dies within the first block, we're done and can early return.
379
- self . reachability . clear ( ) ;
380
315
return ;
381
316
}
382
317
@@ -393,7 +328,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
393
328
while let Some ( block) = self . visit_stack . pop ( ) {
394
329
let bb_data = & self . body [ block] ;
395
330
let num_stmts = bb_data. statements . len ( ) ;
396
- if let Some ( kill_location) = self . loan_kill_location ( block, 0 , num_stmts) {
331
+ if let Some ( kill_location) =
332
+ self . loan_kill_location ( loan_idx, loan_issued_at, block, 0 , num_stmts)
333
+ {
397
334
debug ! ( "loan {:?} gets killed at {:?}" , loan_idx, kill_location) ;
398
335
self . loans_out_of_scope_at_location
399
336
. entry ( kill_location)
@@ -413,35 +350,44 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
413
350
}
414
351
415
352
self . visited . clear ( ) ;
416
- self . reachability . clear ( ) ;
417
353
assert ! ( self . visit_stack. is_empty( ) , "visit stack should be empty" ) ;
418
- assert ! ( self . reachability_stack. is_empty( ) , "reachability stack should be empty" ) ;
419
354
}
420
355
421
356
/// Returns the lowest statement in `start..=end`, where the loan goes out of scope, if any.
422
357
/// This is the statement where the issuing region can't reach any of the regions that are live
423
358
/// at this point.
424
- fn loan_kill_location ( & self , block : BasicBlock , start : usize , end : usize ) -> Option < Location > {
359
+ fn loan_kill_location (
360
+ & self ,
361
+ loan_idx : BorrowIndex ,
362
+ loan_issued_at : Location ,
363
+ block : BasicBlock ,
364
+ start : usize ,
365
+ end : usize ,
366
+ ) -> Option < Location > {
425
367
for statement_index in start..=end {
426
368
let location = Location { block, statement_index } ;
427
369
428
- // Check whether the issuing region can reach local regions that are live at this
429
- // point.
370
+ // Check whether the issuing region can reach local regions that are live at this point:
371
+ // - a loan is always live at its issuing location because it can reach the issuing
372
+ // region, which is always live at this location.
373
+ if location == loan_issued_at {
374
+ continue ;
375
+ }
376
+
377
+ // - the loan goes out of scope at `location` if it's not contained within any regions
378
+ // live at this point.
430
379
//
431
- // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and
432
- // `r` is live at point `q`, then it's guaranteed that `i` would reach `r` at point
433
- // `q`. Reachability is location-insensitive, and we could take advantage of
434
- // that, by jumping to a further point than the next statement. We can jump to the
435
- // furthest point within the block where `r` is live.
436
- let point = self . regioncx . liveness_values ( ) . point_from_location ( location) ;
437
- if let Some ( live_sccs) = self . live_sccs_per_point . row ( point) {
438
- if live_sccs. iter ( ) . any ( |live_scc| self . reachability . contains ( live_scc) ) {
439
- continue ;
440
- }
380
+ // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and `r` is
381
+ // live at point `q`, then it's guaranteed that `i` would reach `r` at point `q`.
382
+ // Reachability is location-insensitive, and we could take advantage of that, by jumping
383
+ // to a further point than just the next statement: we can jump to the furthest point
384
+ // within the block where `r` is live.
385
+ if self . regioncx . is_loan_live_at ( loan_idx, location) {
386
+ continue ;
441
387
}
442
388
443
- // No live region is reachable from the issuing region: the loan is killed at this point
444
- // and goes out of scope .
389
+ // No live region is reachable from the issuing region: the loan is killed at this
390
+ // point .
445
391
return Some ( location) ;
446
392
}
447
393
0 commit comments