11#![ deny( rustc:: untranslatable_diagnostic) ]
22#![ 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 ;
56use rustc_middle:: mir:: {
67 self , BasicBlock , Body , CallReturnPlaces , Location , Place , TerminatorEdges ,
78} ;
@@ -13,8 +14,6 @@ use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
1314use rustc_mir_dataflow:: { Analysis , Direction , Results } ;
1415use std:: fmt;
1516
16- use crate :: constraints:: ConstraintSccIndex ;
17- use crate :: region_infer:: values:: PointIndex ;
1817use crate :: { places_conflict, BorrowSet , PlaceConflictBias , PlaceExt , RegionInferenceContext } ;
1918
2019/// 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> {
246245 body : & ' a Body < ' tcx > ,
247246 regioncx : & ' a RegionInferenceContext < ' tcx > ,
248247
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-
255248 loans_out_of_scope_at_location : FxIndexMap < Location , Vec < BorrowIndex > > ,
256249}
257250
258251impl < ' a , ' tcx > PoloniusOutOfScopePrecomputer < ' a , ' tcx > {
259252 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-
298253 Self {
299254 visited : BitSet :: new_empty ( body. basic_blocks . len ( ) ) ,
300255 visit_stack : vec ! [ ] ,
301256 body,
302257 regioncx,
303258 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 ! [ ] ,
308259 }
309260 }
310261}
@@ -317,66 +268,50 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
317268 & mut self ,
318269 loan_idx : BorrowIndex ,
319270 issuing_region : RegionVid ,
320- first_location : Location ,
271+ loan_issued_at : Location ,
321272 ) {
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-
327273 let sccs = self . regioncx . constraint_sccs ( ) ;
328-
329274 let issuing_region_scc = sccs. scc ( issuing_region) ;
330- self . reachability_stack . push ( issuing_region_scc) ;
331- self . reachability . insert ( issuing_region_scc) ;
332275
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 ) {
336279 // 1. Via member constraints
337280 //
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:
339282 // - placeholders or free regions themselves,
340283 // - or also transitively outlive a free region.
341284 //
342285 // That is to say, if there are member constraints here, the loan escapes the function
343286 // and cannot go out of scope. We can early return.
344- //
287+ if self . regioncx . scc_has_member_constraints ( scc) {
288+ return ;
289+ }
290+
345291 // 2. Via regions that are live at all points: placeholders and free regions.
346292 //
347293 // If the issuing region outlives such a region, its loan escapes the function and
348294 // 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) {
354296 return ;
355297 }
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- }
364298 }
365299
366- let first_block = first_location . block ;
300+ let first_block = loan_issued_at . block ;
367301 let first_bb_data = & self . body . basic_blocks [ first_block] ;
368302
369303 // 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 ;
372306 let first_hi = first_bb_data. statements . len ( ) ;
373307
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+ {
375311 debug ! ( "loan {:?} gets killed at {:?}" , loan_idx, kill_location) ;
376312 self . loans_out_of_scope_at_location . entry ( kill_location) . or_default ( ) . push ( loan_idx) ;
377313
378314 // The loan dies within the first block, we're done and can early return.
379- self . reachability . clear ( ) ;
380315 return ;
381316 }
382317
@@ -393,7 +328,9 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
393328 while let Some ( block) = self . visit_stack . pop ( ) {
394329 let bb_data = & self . body [ block] ;
395330 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+ {
397334 debug ! ( "loan {:?} gets killed at {:?}" , loan_idx, kill_location) ;
398335 self . loans_out_of_scope_at_location
399336 . entry ( kill_location)
@@ -413,35 +350,44 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
413350 }
414351
415352 self . visited . clear ( ) ;
416- self . reachability . clear ( ) ;
417353 assert ! ( self . visit_stack. is_empty( ) , "visit stack should be empty" ) ;
418- assert ! ( self . reachability_stack. is_empty( ) , "reachability stack should be empty" ) ;
419354 }
420355
421356 /// Returns the lowest statement in `start..=end`, where the loan goes out of scope, if any.
422357 /// This is the statement where the issuing region can't reach any of the regions that are live
423358 /// 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 > {
425367 for statement_index in start..=end {
426368 let location = Location { block, statement_index } ;
427369
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.
430379 //
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 ;
441387 }
442388
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 .
445391 return Some ( location) ;
446392 }
447393
0 commit comments