1
1
#![ deny( rustc:: untranslatable_diagnostic) ]
2
2
#![ deny( rustc:: diagnostic_outside_of_impl) ]
3
3
use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
4
- use rustc_index:: bit_set:: BitSet ;
4
+ use rustc_index:: bit_set:: { BitSet , SparseBitMatrix } ;
5
5
use rustc_middle:: mir:: { self , BasicBlock , Body , Location , Place } ;
6
6
use rustc_middle:: ty:: RegionVid ;
7
7
use rustc_middle:: ty:: TyCtxt ;
@@ -12,6 +12,7 @@ use rustc_mir_dataflow::{Analysis, Direction, Results};
12
12
use std:: fmt;
13
13
14
14
use crate :: constraints:: ConstraintSccIndex ;
15
+ use crate :: region_infer:: values:: PointIndex ;
15
16
use crate :: { places_conflict, BorrowSet , PlaceConflictBias , PlaceExt , RegionInferenceContext } ;
16
17
17
18
/// A tuple with named fields that can hold either the results or the transient state of the
@@ -121,7 +122,6 @@ rustc_index::newtype_index! {
121
122
pub struct Borrows < ' a , ' tcx > {
122
123
tcx : TyCtxt < ' tcx > ,
123
124
body : & ' a Body < ' tcx > ,
124
-
125
125
borrow_set : & ' a BorrowSet < ' tcx > ,
126
126
borrows_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
127
127
}
@@ -234,39 +234,63 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
234
234
visit_stack : Vec < StackEntry > ,
235
235
body : & ' a Body < ' tcx > ,
236
236
regioncx : & ' a RegionInferenceContext < ' tcx > ,
237
+
238
+ sccs_live_at_all_points : FxHashSet < ConstraintSccIndex > ,
239
+ live_sccs_per_point : SparseBitMatrix < PointIndex , ConstraintSccIndex > ,
240
+
241
+ reachability : BitSet < ConstraintSccIndex > ,
242
+ reachability_stack : Vec < ConstraintSccIndex > ,
243
+
237
244
borrows_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
238
- placeholders : FxHashSet < ConstraintSccIndex > ,
239
- reachability : BitSet < RegionVid > ,
240
- reachability_stack : Vec < RegionVid > ,
241
245
}
242
246
243
247
impl < ' a , ' tcx > PoloniusOutOfScopePrecomputer < ' a , ' tcx > {
244
248
fn new ( body : & ' a Body < ' tcx > , regioncx : & ' a RegionInferenceContext < ' tcx > ) -> Self {
245
- // Compute the placeholder list once , as it will be used for all the loan scopes we'll
246
- // compute.
249
+ // Compute the list of SCCs that are live at all points , as it will be used for all the
250
+ // loan scopes we'll compute.
247
251
// FIXME: they're probably already available somewhere.
248
- let placeholders = regioncx
252
+ let sccs_live_at_all_points : FxHashSet < _ > = regioncx
249
253
. regions ( )
250
254
. filter ( |& r| {
251
255
use rustc_infer:: infer:: { NllRegionVariableOrigin , RegionVariableOrigin } ;
252
256
let origin = regioncx. var_infos [ r] . origin ;
253
- let is_placeholder = matches ! (
257
+ let live_at_all_points = matches ! (
254
258
origin,
255
- RegionVariableOrigin :: Nll ( NllRegionVariableOrigin :: Placeholder ( _) )
259
+ RegionVariableOrigin :: Nll (
260
+ NllRegionVariableOrigin :: Placeholder ( _)
261
+ | NllRegionVariableOrigin :: FreeRegion
262
+ )
256
263
) ;
257
- is_placeholder
264
+ live_at_all_points
258
265
} )
259
266
. map ( |r| regioncx. constraint_sccs . scc ( r) )
260
267
. collect ( ) ;
261
268
269
+ // Pre-compute the set of live SCCs per point
270
+ let liveness = & regioncx. liveness_constraints ;
271
+ let sccs = & regioncx. constraint_sccs ;
272
+ let mut live_sccs_per_point = SparseBitMatrix :: new ( sccs. num_sccs ( ) ) ;
273
+
274
+ for region in liveness. rows ( ) {
275
+ let scc = sccs. scc ( region) ;
276
+ if sccs_live_at_all_points. contains ( & scc) {
277
+ continue ;
278
+ }
279
+ for location in liveness. get_elements ( region) {
280
+ let point = liveness. point_from_location ( location) ;
281
+ live_sccs_per_point. insert ( point, scc) ;
282
+ }
283
+ }
284
+
262
285
Self {
263
286
visited : BitSet :: new_empty ( body. basic_blocks . len ( ) ) ,
264
287
visit_stack : vec ! [ ] ,
265
288
body,
266
289
regioncx,
267
290
borrows_out_of_scope_at_location : FxIndexMap :: default ( ) ,
268
- placeholders,
269
- reachability : BitSet :: new_empty ( regioncx. regions ( ) . count ( ) ) ,
291
+ sccs_live_at_all_points,
292
+ live_sccs_per_point,
293
+ reachability : BitSet :: new_empty ( regioncx. constraint_sccs . num_sccs ( ) ) ,
270
294
reachability_stack : vec ! [ ] ,
271
295
}
272
296
}
@@ -290,13 +314,11 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
290
314
let sccs = & self . regioncx . constraint_sccs ;
291
315
let member_constraints = & self . regioncx . member_constraints ;
292
316
293
- self . reachability_stack . push ( issuing_region) ;
294
- self . reachability . insert ( issuing_region) ;
295
-
296
- let static_region = self . regioncx . universal_regions ( ) . fr_static ;
297
- while let Some ( region) = self . reachability_stack . pop ( ) {
298
- let scc = sccs. scc ( region) ;
317
+ let issuing_region_scc = sccs. scc ( issuing_region) ;
318
+ self . reachability_stack . push ( issuing_region_scc) ;
319
+ self . reachability . insert ( issuing_region_scc) ;
299
320
321
+ while let Some ( scc) = self . reachability_stack . pop ( ) {
300
322
// Handle successors of this SCC:
301
323
//
302
324
// 1. Via member constraints
@@ -308,11 +330,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
308
330
// That is to say, if there are member constraints here, the loan escapes the
309
331
// function and cannot go out of scope. We can early return.
310
332
//
311
- // 2. Via placeholders
333
+ // 2. Via regions that are live at all points: placeholders and free regions.
312
334
//
313
- // If the issuing region outlives placeholders , its loan escapes the function and
335
+ // If the issuing region outlives such a region , its loan escapes the function and
314
336
// cannot go out of scope. We can early return.
315
- if member_constraints. indices ( scc) . next ( ) . is_some ( ) || self . placeholders . contains ( & scc)
337
+ if member_constraints. indices ( scc) . next ( ) . is_some ( )
338
+ || self . sccs_live_at_all_points . contains ( & scc)
316
339
{
317
340
self . reachability_stack . clear ( ) ;
318
341
self . reachability . clear ( ) ;
@@ -321,15 +344,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
321
344
322
345
// 3. Via outlives successors, which we want to record and traverse, so we add them
323
346
// to the worklist stack
324
- let successors = self . regioncx . constraint_graph . outgoing_edges (
325
- region,
326
- & self . regioncx . constraints ,
327
- static_region,
328
- ) ;
329
- for outlives_constraint in successors {
330
- let succ = outlives_constraint. sub ;
331
- if self . reachability . insert ( succ) {
332
- self . reachability_stack . push ( succ) ;
347
+ for & succ_scc in sccs. successors ( scc) {
348
+ if self . reachability . insert ( succ_scc) {
349
+ self . reachability_stack . push ( succ_scc) ;
333
350
}
334
351
}
335
352
}
@@ -364,10 +381,19 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
364
381
365
382
// Check whether the issuing region can reach local regions that are live at this
366
383
// point.
367
- for reachable_region in self . reachability . iter ( ) {
368
- if self . regioncx . liveness_constraints . contains ( reachable_region, location) {
369
- issuing_region_can_reach_live_regions = true ;
370
- break ;
384
+ //
385
+ // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and
386
+ // `r` is live at point `q`, then it's guaranteed that `i` would reach `r` at point
387
+ // `q`. The reachability is location-insensitive, and we could take advantage of
388
+ // that, by jumping to a further point than the next statement. We can jump to the
389
+ // furthest point within the block where `r` is live.
390
+ let point = self . regioncx . liveness_constraints . point_from_location ( location) ;
391
+ if let Some ( live_sccs) = self . live_sccs_per_point . row ( point) {
392
+ for live_scc in live_sccs. iter ( ) {
393
+ if self . reachability . contains ( live_scc) {
394
+ issuing_region_can_reach_live_regions = true ;
395
+ break ;
396
+ }
371
397
}
372
398
}
373
399
0 commit comments