11#![ deny( rustc:: untranslatable_diagnostic) ]
22#![ deny( rustc:: diagnostic_outside_of_impl) ]
33use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
4- use rustc_index:: bit_set:: BitSet ;
4+ use rustc_index:: bit_set:: { BitSet , SparseBitMatrix } ;
55use rustc_middle:: mir:: {
66 self , BasicBlock , Body , CallReturnPlaces , Location , Place , TerminatorEdges ,
77} ;
@@ -14,6 +14,7 @@ use rustc_mir_dataflow::{Analysis, Direction, Results};
1414use std:: fmt;
1515
1616use crate :: constraints:: ConstraintSccIndex ;
17+ use crate :: region_infer:: values:: PointIndex ;
1718use crate :: { places_conflict, BorrowSet , PlaceConflictBias , PlaceExt , RegionInferenceContext } ;
1819
1920/// 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! {
123124pub struct Borrows < ' a , ' tcx > {
124125 tcx : TyCtxt < ' tcx > ,
125126 body : & ' a Body < ' tcx > ,
126-
127127 borrow_set : & ' a BorrowSet < ' tcx > ,
128128 borrows_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
129129}
@@ -250,39 +250,63 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
250250 visit_stack : Vec < StackEntry > ,
251251 body : & ' a Body < ' tcx > ,
252252 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+
253260 loans_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
254- placeholders : FxHashSet < ConstraintSccIndex > ,
255- reachability : BitSet < RegionVid > ,
256- reachability_stack : Vec < RegionVid > ,
257261}
258262
259263impl < ' a , ' tcx > PoloniusOutOfScopePrecomputer < ' a , ' tcx > {
260264 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.
263267 // FIXME: they're surely already available somewhere.
264- let placeholders = regioncx
268+ let sccs_live_at_all_points : FxHashSet < _ > = regioncx
265269 . regions ( )
266270 . filter ( |& r| {
267271 use rustc_infer:: infer:: { NllRegionVariableOrigin , RegionVariableOrigin } ;
268272 let origin = regioncx. var_infos [ r] . origin ;
269- let is_placeholder = matches ! (
273+ let live_at_all_points = matches ! (
270274 origin,
271- RegionVariableOrigin :: Nll ( NllRegionVariableOrigin :: Placeholder ( _) )
275+ RegionVariableOrigin :: Nll (
276+ NllRegionVariableOrigin :: Placeholder ( _)
277+ | NllRegionVariableOrigin :: FreeRegion
278+ )
272279 ) ;
273- is_placeholder
280+ live_at_all_points
274281 } )
275282 . map ( |r| regioncx. constraint_sccs . scc ( r) )
276283 . collect ( ) ;
277284
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+
278301 Self {
279302 visited : BitSet :: new_empty ( body. basic_blocks . len ( ) ) ,
280303 visit_stack : vec ! [ ] ,
281304 body,
282305 regioncx,
283306 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 ( ) ) ,
286310 reachability_stack : vec ! [ ] ,
287311 }
288312 }
@@ -306,13 +330,11 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
306330 let sccs = & self . regioncx . constraint_sccs ;
307331 let member_constraints = & self . regioncx . member_constraints ;
308332
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) ;
315336
337+ while let Some ( scc) = self . reachability_stack . pop ( ) {
316338 // Handle successors of this SCC:
317339 //
318340 // 1. Via member constraints
@@ -324,11 +346,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
324346 // That is to say, if there are member constraints here, the loan escapes the
325347 // function and cannot go out of scope. We can early return.
326348 //
327- // 2. Via placeholders
349+ // 2. Via regions that are live at all points: placeholders and free regions.
328350 //
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
330352 // 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)
332355 {
333356 self . reachability_stack . clear ( ) ;
334357 self . reachability . clear ( ) ;
@@ -337,15 +360,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
337360
338361 // 3. Via outlives successors, which we want to record and traverse, so we add them
339362 // 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) ;
349366 }
350367 }
351368 }
@@ -380,10 +397,19 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
380397
381398 // Check whether the issuing region can reach local regions that are live at this
382399 // 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+ }
387413 }
388414 }
389415
0 commit comments