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