Skip to content

Commit 558bfe0

Browse files
committed
tmp: compute live loans during liveness
1 parent 8659952 commit 558bfe0

File tree

6 files changed

+173
-131
lines changed

6 files changed

+173
-131
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+42-97
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
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;
6+
use rustc_infer::infer::{NllRegionVariableOrigin, RegionVariableOrigin};
57
use rustc_middle::mir::{
68
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
79
};
@@ -13,8 +15,6 @@ use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
1315
use rustc_mir_dataflow::{Analysis, Direction, Results};
1416
use std::fmt;
1517

16-
use crate::constraints::ConstraintSccIndex;
17-
use crate::region_infer::values::PointIndex;
1818
use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
1919

2020
/// A tuple with named fields that can hold either the results or the transient state of the
@@ -255,66 +255,17 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
255255
visit_stack: Vec<StackEntry>,
256256
body: &'a Body<'tcx>,
257257
regioncx: &'a RegionInferenceContext<'tcx>,
258-
259-
sccs_live_at_all_points: FxHashSet<ConstraintSccIndex>,
260-
live_sccs_per_point: SparseBitMatrix<PointIndex, ConstraintSccIndex>,
261-
262-
reachability: BitSet<ConstraintSccIndex>,
263-
reachability_stack: Vec<ConstraintSccIndex>,
264-
265258
loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
266259
}
267260

268261
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
269262
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
270-
let sccs = regioncx.constraint_sccs();
271-
let num_sccs = sccs.num_sccs();
272-
273-
// Compute the list of SCCs that are live at all points, as it will be used for all the
274-
// loan scopes we'll compute.
275-
// FIXME: they're surely already available somewhere.
276-
let sccs_live_at_all_points: FxHashSet<_> = regioncx
277-
.regions()
278-
.filter(|&r| {
279-
use rustc_infer::infer::{NllRegionVariableOrigin, RegionVariableOrigin};
280-
let origin = regioncx.var_infos[r].origin;
281-
let live_at_all_points = matches!(
282-
origin,
283-
RegionVariableOrigin::Nll(
284-
NllRegionVariableOrigin::Placeholder(_)
285-
| NllRegionVariableOrigin::FreeRegion
286-
)
287-
);
288-
live_at_all_points
289-
})
290-
.map(|r| sccs.scc(r))
291-
.collect();
292-
293-
// Pre-compute the set of live SCCs per point
294-
let liveness = regioncx.liveness_values();
295-
let mut live_sccs_per_point = SparseBitMatrix::new(num_sccs);
296-
297-
for region in liveness.rows() {
298-
let scc = sccs.scc(region);
299-
if sccs_live_at_all_points.contains(&scc) {
300-
continue;
301-
}
302-
for location in liveness.get_elements(region) {
303-
let point = liveness.point_from_location(location);
304-
live_sccs_per_point.insert(point, scc);
305-
}
306-
}
307-
308263
Self {
309264
visited: BitSet::new_empty(body.basic_blocks.len()),
310265
visit_stack: vec![],
311266
body,
312267
regioncx,
313268
loans_out_of_scope_at_location: FxIndexMap::default(),
314-
sccs_live_at_all_points,
315-
live_sccs_per_point,
316-
reachability: BitSet::new_empty(num_sccs),
317-
reachability_stack: vec![],
318269
}
319270
}
320271
}
@@ -329,48 +280,44 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
329280
issuing_region: RegionVid,
330281
issued_location: Location,
331282
) {
332-
// Let's precompute the reachability set of the issuing region, via reachability on the
333-
// condensation graph. We can also early return when reaching regions that outlive free
334-
// regions via member constraints. (The `OutOfScopePrecomputer` wouldn't be called on a
335-
// region that outlives free regions via outlives constraints.)
336-
337283
let sccs = self.regioncx.constraint_sccs();
338-
339284
let issuing_region_scc = sccs.scc(issuing_region);
340-
self.reachability_stack.push(issuing_region_scc);
341-
self.reachability.insert(issuing_region_scc);
342285

343-
while let Some(scc) = self.reachability_stack.pop() {
344-
// Handle successors of this SCC:
345-
//
286+
// We first handle the cases where the loan doesn't go out of scope, depending on the issuing
287+
// region's successors.
288+
for scc in sccs.depth_first_search(issuing_region_scc) {
346289
// 1. Via member constraints
347290
//
348-
// The issuing region can flow into the choice regions here, and they are either:
291+
// The issuing region can flow into the choice regions, and they are either:
349292
// - placeholders or free regions themselves,
350293
// - or also transitively outlive a free region.
351294
//
352-
// That is to say, if there are member constraints here, the loan escapes the
353-
// function and cannot go out of scope. We can early return.
354-
//
295+
// That is to say, if there are member constraints here, the loan escapes the function
296+
// and cannot go out of scope. We can early return.
297+
if self.regioncx.scc_has_member_constraints(scc) {
298+
return;
299+
}
300+
355301
// 2. Via regions that are live at all points: placeholders and free regions.
356302
//
357303
// If the issuing region outlives such a region, its loan escapes the function and
358304
// cannot go out of scope. We can early return.
359-
if self.regioncx.scc_has_member_constraints(scc)
360-
|| self.sccs_live_at_all_points.contains(&scc)
361-
{
362-
self.reachability_stack.clear();
363-
self.reachability.clear();
305+
//
306+
// FIXME: there must be a cleaner way to find this information. At least, when
307+
// higher-ranked subtyping is abstracted away from the borrowck main path, we'll only
308+
// need to check whether this is a universal region.
309+
//
310+
let representative = self.regioncx.scc_representatives[scc];
311+
let origin = self.regioncx.var_infos[representative].origin;
312+
let live_at_all_points = matches!(
313+
origin,
314+
RegionVariableOrigin::Nll(
315+
NllRegionVariableOrigin::Placeholder(_) | NllRegionVariableOrigin::FreeRegion
316+
)
317+
);
318+
if live_at_all_points {
364319
return;
365320
}
366-
367-
// 3. Via outlives successors, which we want to record and traverse: we add them
368-
// to the worklist stack
369-
for &succ_scc in sccs.successors(scc) {
370-
if self.reachability.insert(succ_scc) {
371-
self.reachability_stack.push(succ_scc);
372-
}
373-
}
374321
}
375322

376323
// We visit one BB at a time. The complication is that we may start in the
@@ -397,26 +344,26 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
397344
for i in lo..=hi {
398345
let location = Location { block: bb, statement_index: i };
399346

400-
// The loan is out of scope at point `location` if it's not contained within any
401-
// live regions.
347+
// Check whether the issuing region can reach local regions that are live at this
348+
// point:
349+
// - a loan is always live at its issuing location because it can reach the issuing
350+
// region, which is always live at this location.
351+
if location == issued_location {
352+
continue;
353+
}
354+
355+
// - the loan is out of scope at point `location` if it's not contained within any
356+
// regions live at this point.
402357
let mut issuing_region_can_reach_live_regions = false;
403358

404-
// Check whether the issuing region can reach local regions that are live at this
405-
// point.
406-
//
407359
// FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and
408360
// `r` is live at point `q`, then it's guaranteed that `i` would reach `r` at point
409361
// `q`. The reachability is location-insensitive, and we could take advantage of
410362
// that, by jumping to a further point than the next statement. We can jump to the
411363
// furthest point within the block where `r` is live.
412364
let point = self.regioncx.liveness_values().point_from_location(location);
413-
if let Some(live_sccs) = self.live_sccs_per_point.row(point) {
414-
for live_scc in live_sccs.iter() {
415-
if self.reachability.contains(live_scc) {
416-
issuing_region_can_reach_live_regions = true;
417-
break;
418-
}
419-
}
365+
if self.regioncx.live_loans.contains(point, loan_idx) {
366+
issuing_region_can_reach_live_regions = true;
420367
}
421368

422369
// If no live region is reachable from the issuing region, then the loan is
@@ -462,9 +409,7 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
462409
}
463410

464411
self.visited.clear();
465-
self.reachability.clear();
466412
assert!(self.visit_stack.is_empty(), "visit stack should be empty");
467-
assert!(self.reachability_stack.is_empty(), "reachability stack should be empty");
468413
}
469414
}
470415

@@ -495,8 +440,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
495440
}
496441

497442
assert_eq!(
498-
borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
499-
"the loans out of scope must be the same as the borrows out of scope"
443+
polonius_prec.loans_out_of_scope_at_location, borrows_out_of_scope_at_location,
444+
"the loans out of scope are different from the borrows out of scope"
500445
);
501446

502447
borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;

compiler/rustc_borrowck/src/nll.rs

+21-16
Original file line numberDiff line numberDiff line change
@@ -181,22 +181,26 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
181181
let elements = &Rc::new(RegionValueElements::new(&body));
182182

183183
// Run the MIR type-checker.
184-
let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
185-
type_check::type_check(
186-
infcx,
187-
param_env,
188-
body,
189-
promoted,
190-
&universal_regions,
191-
location_table,
192-
borrow_set,
193-
&mut all_facts,
194-
flow_inits,
195-
move_data,
196-
elements,
197-
upvars,
198-
polonius_input,
199-
);
184+
let MirTypeckResults {
185+
constraints,
186+
universal_region_relations,
187+
opaque_type_values,
188+
live_loans,
189+
} = type_check::type_check(
190+
infcx,
191+
param_env,
192+
body,
193+
promoted,
194+
&universal_regions,
195+
location_table,
196+
borrow_set,
197+
&mut all_facts,
198+
flow_inits,
199+
move_data,
200+
elements,
201+
upvars,
202+
polonius_input,
203+
);
200204

201205
if let Some(all_facts) = &mut all_facts {
202206
let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
@@ -274,6 +278,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
274278
type_tests,
275279
liveness_constraints,
276280
elements,
281+
live_loans,
277282
);
278283

279284
// Generate various additional constraints.

compiler/rustc_borrowck/src/region_infer/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use std::collections::VecDeque;
22
use std::rc::Rc;
33

4+
use crate::BorrowIndex;
45
use rustc_data_structures::binary_search_util;
56
use rustc_data_structures::frozen::Frozen;
67
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
78
use rustc_data_structures::graph::scc::Sccs;
89
use rustc_errors::Diagnostic;
910
use rustc_hir::def_id::CRATE_DEF_ID;
11+
use rustc_index::bit_set::SparseBitMatrix;
1012
use rustc_index::{IndexSlice, IndexVec};
1113
use rustc_infer::infer::outlives::test_type_match;
1214
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
@@ -21,6 +23,7 @@ use rustc_middle::traits::ObligationCauseCode;
2123
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
2224
use rustc_span::Span;
2325

26+
use crate::region_infer::values::PointIndex;
2427
use crate::{
2528
constraints::{
2629
graph::NormalConstraintGraph, ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
@@ -102,7 +105,7 @@ pub struct RegionInferenceContext<'tcx> {
102105
/// of its SCC and be sure that -- if they have the same repr --
103106
/// they *must* be equal (though not having the same repr does not
104107
/// mean they are unequal).
105-
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
108+
pub(crate) scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
106109

107110
/// The final inferred values of the region variables; we compute
108111
/// one value per SCC. To get the value for any given *region*,
@@ -119,6 +122,8 @@ pub struct RegionInferenceContext<'tcx> {
119122
/// Information about how the universally quantified regions in
120123
/// scope on this function relate to one another.
121124
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
125+
126+
pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
122127
}
123128

124129
/// Each time that `apply_member_constraint` is successful, it appends
@@ -330,6 +335,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
330335
type_tests: Vec<TypeTest<'tcx>>,
331336
liveness_constraints: LivenessValues<RegionVid>,
332337
elements: &Rc<RegionValueElements>,
338+
live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
333339
) -> Self {
334340
debug!("universal_regions: {:#?}", universal_regions);
335341
debug!("outlives constraints: {:#?}", outlives_constraints);
@@ -383,6 +389,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
383389
type_tests,
384390
universal_regions,
385391
universal_region_relations,
392+
live_loans,
386393
};
387394

388395
result.init_free_and_bound_regions();

compiler/rustc_borrowck/src/type_check/liveness/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use itertools::{Either, Itertools};
22
use rustc_data_structures::fx::FxHashSet;
3+
use rustc_index::bit_set::SparseBitMatrix;
34
use rustc_middle::mir::{Body, Local};
45
use rustc_middle::ty::{RegionVid, TyCtxt};
56
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
67
use rustc_mir_dataflow::move_paths::MoveData;
78
use rustc_mir_dataflow::ResultsCursor;
89
use std::rc::Rc;
910

11+
use crate::dataflow::BorrowIndex;
12+
use crate::region_infer::values::PointIndex;
1013
use crate::{
1114
constraints::OutlivesConstraintSet,
1215
facts::{AllFacts, AllFactsExt},
@@ -37,7 +40,7 @@ pub(super) fn generate<'mir, 'tcx>(
3740
move_data: &MoveData<'tcx>,
3841
location_table: &LocationTable,
3942
use_polonius: bool,
40-
) {
43+
) -> SparseBitMatrix<PointIndex, BorrowIndex> {
4144
debug!("liveness::generate");
4245

4346
let free_regions = regions_that_outlive_free_regions(
@@ -64,7 +67,7 @@ pub(super) fn generate<'mir, 'tcx>(
6467
relevant_live_locals,
6568
boring_locals,
6669
polonius_drop_used,
67-
);
70+
)
6871
}
6972

7073
// The purpose of `compute_relevant_live_locals` is to define the subset of `Local`

0 commit comments

Comments
 (0)