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:: { self , BasicBlock , Body , Location , Place } ;
66use rustc_middle:: ty:: RegionVid ;
77use rustc_middle:: ty:: TyCtxt ;
@@ -12,6 +12,7 @@ use rustc_mir_dataflow::{Analysis, Direction, Results};
1212use std:: fmt;
1313
1414use crate :: constraints:: ConstraintSccIndex ;
15+ use crate :: region_infer:: values:: PointIndex ;
1516use crate :: { places_conflict, BorrowSet , PlaceConflictBias , PlaceExt , RegionInferenceContext } ;
1617
1718/// 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! {
121122pub struct Borrows < ' a , ' tcx > {
122123 tcx : TyCtxt < ' tcx > ,
123124 body : & ' a Body < ' tcx > ,
124-
125125 borrow_set : & ' a BorrowSet < ' tcx > ,
126126 borrows_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
127127}
@@ -234,39 +234,63 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
234234 visit_stack : Vec < StackEntry > ,
235235 body : & ' a Body < ' tcx > ,
236236 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+
237244 borrows_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
238- placeholders : FxHashSet < ConstraintSccIndex > ,
239- reachability : BitSet < RegionVid > ,
240- reachability_stack : Vec < RegionVid > ,
241245}
242246
243247impl < ' a , ' tcx > PoloniusOutOfScopePrecomputer < ' a , ' tcx > {
244248 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.
247251 // FIXME: they're probably already available somewhere.
248- let placeholders = regioncx
252+ let sccs_live_at_all_points : FxHashSet < _ > = regioncx
249253 . regions ( )
250254 . filter ( |& r| {
251255 use rustc_infer:: infer:: { NllRegionVariableOrigin , RegionVariableOrigin } ;
252256 let origin = regioncx. var_infos [ r] . origin ;
253- let is_placeholder = matches ! (
257+ let live_at_all_points = matches ! (
254258 origin,
255- RegionVariableOrigin :: Nll ( NllRegionVariableOrigin :: Placeholder ( _) )
259+ RegionVariableOrigin :: Nll (
260+ NllRegionVariableOrigin :: Placeholder ( _)
261+ | NllRegionVariableOrigin :: FreeRegion
262+ )
256263 ) ;
257- is_placeholder
264+ live_at_all_points
258265 } )
259266 . map ( |r| regioncx. constraint_sccs . scc ( r) )
260267 . collect ( ) ;
261268
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+
262285 Self {
263286 visited : BitSet :: new_empty ( body. basic_blocks . len ( ) ) ,
264287 visit_stack : vec ! [ ] ,
265288 body,
266289 regioncx,
267290 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 ( ) ) ,
270294 reachability_stack : vec ! [ ] ,
271295 }
272296 }
@@ -290,13 +314,11 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
290314 let sccs = & self . regioncx . constraint_sccs ;
291315 let member_constraints = & self . regioncx . member_constraints ;
292316
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) ;
299320
321+ while let Some ( scc) = self . reachability_stack . pop ( ) {
300322 // Handle successors of this SCC:
301323 //
302324 // 1. Via member constraints
@@ -308,11 +330,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
308330 // That is to say, if there are member constraints here, the loan escapes the
309331 // function and cannot go out of scope. We can early return.
310332 //
311- // 2. Via placeholders
333+ // 2. Via regions that are live at all points: placeholders and free regions.
312334 //
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
314336 // 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)
316339 {
317340 self . reachability_stack . clear ( ) ;
318341 self . reachability . clear ( ) ;
@@ -321,15 +344,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
321344
322345 // 3. Via outlives successors, which we want to record and traverse, so we add them
323346 // 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) ;
333350 }
334351 }
335352 }
@@ -364,10 +381,19 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
364381
365382 // Check whether the issuing region can reach local regions that are live at this
366383 // 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+ }
371397 }
372398 }
373399
0 commit comments