Skip to content

Commit bd3a221

Browse files
committed
Auto merge of rust-lang#118175 - lqd:unify-live-loans, r=matthewjasper
Centralize live loans maintenance to fix scope differences due to liveness As found in the recent [polonius crater run](rust-lang#117593 (comment)), NLLs and the location-insensitive polonius computed different scopes on some specific CFG shapes, e.g. the following. ![image](https://github.com/rust-lang/rust/assets/247183/c3649f5e-3058-454e-854e-1a6b336bdd5e) I had missed that liveness data was pushed from different sources than just the liveness computation: there are a few places that do this -- and some of them may be unneeded or at the very least untested, as no tests changed when I tried removing some of them. Here, `_6` is e.g. dead on entry to `bb2[0]` during `liveness::trace`, but its regions will be marked as live later during "constraint generation" (which I plan to refactor away and put in the liveness module soon). This should cause the inflowing loans to be marked live, but they were only computed in `liveness::trace`. Therefore, this PR moves live loan maintenance to `LivenessValues`, so that the various places pushing liveness data will all also update live loans at the same time -- except for promoteds which I don't believe need them, and their liveness handling is already interesting/peculiar. All the regressions I saw in the initial crater run were related to this kind of shapes, and this change did fix all of them on the [next run](rust-lang#117593 (comment)). r? `@matthewjasper` (This will conflict with rust-lang#117880 but whichever lands first is fine by me, the end goal is the same for both)
2 parents 8c2b577 + de2b8b1 commit bd3a221

File tree

7 files changed

+144
-102
lines changed

7 files changed

+144
-102
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,8 @@ impl<'mir, 'tcx> Borrows<'mir, 'tcx> {
432432

433433
assert_eq!(
434434
borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
435-
"the loans out of scope must be the same as the borrows out of scope"
435+
"polonius loan scopes differ from NLL borrow scopes, for body {:?}",
436+
body.span,
436437
);
437438

438439
borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;

compiler/rustc_borrowck/src/nll.rs

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

103103
// Run the MIR type-checker.
104-
let MirTypeckResults {
105-
constraints,
106-
universal_region_relations,
107-
opaque_type_values,
108-
live_loans,
109-
} = type_check::type_check(
110-
infcx,
111-
param_env,
112-
body,
113-
promoted,
114-
&universal_regions,
115-
location_table,
116-
borrow_set,
117-
&mut all_facts,
118-
flow_inits,
119-
move_data,
120-
elements,
121-
upvars,
122-
polonius_input,
123-
);
104+
let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
105+
type_check::type_check(
106+
infcx,
107+
param_env,
108+
body,
109+
promoted,
110+
&universal_regions,
111+
location_table,
112+
borrow_set,
113+
&mut all_facts,
114+
flow_inits,
115+
move_data,
116+
elements,
117+
upvars,
118+
polonius_input,
119+
);
124120

125121
// Create the region inference context, taking ownership of the
126122
// region inference data that was contained in `infcx`, and the
@@ -161,7 +157,6 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
161157
type_tests,
162158
liveness_constraints,
163159
elements,
164-
live_loans,
165160
);
166161

167162
// If requested: dump NLL facts, and run legacy polonius analysis.

compiler/rustc_borrowck/src/region_infer/mod.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
77
use rustc_data_structures::graph::scc::Sccs;
88
use rustc_errors::Diagnostic;
99
use rustc_hir::def_id::CRATE_DEF_ID;
10-
use rustc_index::bit_set::SparseBitMatrix;
1110
use rustc_index::{IndexSlice, IndexVec};
1211
use rustc_infer::infer::outlives::test_type_match;
1312
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
@@ -31,8 +30,8 @@ use crate::{
3130
nll::PoloniusOutput,
3231
region_infer::reverse_sccs::ReverseSccGraph,
3332
region_infer::values::{
34-
LivenessValues, PlaceholderIndices, PointIndex, RegionElement, RegionValueElements,
35-
RegionValues, ToElementIndex,
33+
LivenessValues, PlaceholderIndices, RegionElement, RegionValueElements, RegionValues,
34+
ToElementIndex,
3635
},
3736
type_check::{free_region_relations::UniversalRegionRelations, Locations},
3837
universal_regions::UniversalRegions,
@@ -120,9 +119,6 @@ pub struct RegionInferenceContext<'tcx> {
120119
/// Information about how the universally quantified regions in
121120
/// scope on this function relate to one another.
122121
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
123-
124-
/// The set of loans that are live at a given point in the CFG, when using `-Zpolonius=next`.
125-
live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
126122
}
127123

128124
/// Each time that `apply_member_constraint` is successful, it appends
@@ -335,7 +331,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
335331
type_tests: Vec<TypeTest<'tcx>>,
336332
liveness_constraints: LivenessValues,
337333
elements: &Rc<RegionValueElements>,
338-
live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
339334
) -> Self {
340335
debug!("universal_regions: {:#?}", universal_regions);
341336
debug!("outlives constraints: {:#?}", outlives_constraints);
@@ -389,7 +384,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
389384
type_tests,
390385
universal_regions,
391386
universal_region_relations,
392-
live_loans,
393387
};
394388

395389
result.init_free_and_bound_regions();
@@ -2325,7 +2319,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
23252319
/// Note: for now, the sets of live loans is only available when using `-Zpolonius=next`.
23262320
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, location: Location) -> bool {
23272321
let point = self.liveness_constraints.point_from_location(location);
2328-
self.live_loans.contains(point, loan_idx)
2322+
self.liveness_constraints.is_loan_live_at(loan_idx, point)
23292323
}
23302324
}
23312325

compiler/rustc_borrowck/src/region_infer/values.rs

+61-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use rustc_middle::ty::{self, RegionVid};
1111
use std::fmt::Debug;
1212
use std::rc::Rc;
1313

14+
use crate::dataflow::BorrowIndex;
15+
1416
/// Maps between a `Location` and a `PointIndex` (and vice versa).
1517
pub(crate) struct RegionValueElements {
1618
/// For each basic block, how many points are contained within?
@@ -120,14 +122,45 @@ pub(crate) enum RegionElement {
120122
/// Records the CFG locations where each region is live. When we initially compute liveness, we use
121123
/// an interval matrix storing liveness ranges for each region-vid.
122124
pub(crate) struct LivenessValues {
125+
/// The map from locations to points.
123126
elements: Rc<RegionValueElements>,
127+
128+
/// For each region: the points where it is live.
124129
points: SparseIntervalMatrix<RegionVid, PointIndex>,
130+
131+
/// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at
132+
/// that point.
133+
pub(crate) loans: Option<LiveLoans>,
134+
}
135+
136+
/// Data used to compute the loans that are live at a given point in the CFG, when using
137+
/// `-Zpolonius=next`.
138+
pub(crate) struct LiveLoans {
139+
/// The set of loans that flow into a given region. When individual regions are marked as live
140+
/// in the CFG, these inflowing loans are recorded as live.
141+
pub(crate) inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
142+
143+
/// The set of loans that are live at a given point in the CFG.
144+
pub(crate) live_loans: SparseBitMatrix<PointIndex, BorrowIndex>,
145+
}
146+
147+
impl LiveLoans {
148+
pub(crate) fn new(num_loans: usize) -> Self {
149+
LiveLoans {
150+
live_loans: SparseBitMatrix::new(num_loans),
151+
inflowing_loans: SparseBitMatrix::new(num_loans),
152+
}
153+
}
125154
}
126155

127156
impl LivenessValues {
128157
/// Create an empty map of regions to locations where they're live.
129158
pub(crate) fn new(elements: Rc<RegionValueElements>) -> Self {
130-
Self { points: SparseIntervalMatrix::new(elements.num_points), elements }
159+
LivenessValues {
160+
points: SparseIntervalMatrix::new(elements.num_points),
161+
elements,
162+
loans: None,
163+
}
131164
}
132165

133166
/// Iterate through each region that has a value in this set.
@@ -140,12 +173,30 @@ impl LivenessValues {
140173
debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location);
141174
let point = self.elements.point_from_location(location);
142175
self.points.insert(region, point);
176+
177+
// When available, record the loans flowing into this region as live at the given point.
178+
if let Some(loans) = self.loans.as_mut() {
179+
if let Some(inflowing) = loans.inflowing_loans.row(region) {
180+
loans.live_loans.union_row(point, inflowing);
181+
}
182+
}
143183
}
144184

145185
/// Records `region` as being live at all the given `points`.
146186
pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet<PointIndex>) {
147187
debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points);
148188
self.points.union_row(region, points);
189+
190+
// When available, record the loans flowing into this region as live at the given points.
191+
if let Some(loans) = self.loans.as_mut() {
192+
if let Some(inflowing) = loans.inflowing_loans.row(region) {
193+
if !inflowing.is_empty() {
194+
for point in points.iter() {
195+
loans.live_loans.union_row(point, inflowing);
196+
}
197+
}
198+
}
199+
}
149200
}
150201

151202
/// Records `region` as being live at all the control-flow points.
@@ -185,6 +236,15 @@ impl LivenessValues {
185236
pub(crate) fn point_from_location(&self, location: Location) -> PointIndex {
186237
self.elements.point_from_location(location)
187238
}
239+
240+
/// When using `-Zpolonius=next`, returns whether the `loan_idx` is live at the given `point`.
241+
pub(crate) fn is_loan_live_at(&self, loan_idx: BorrowIndex, point: PointIndex) -> bool {
242+
self.loans
243+
.as_ref()
244+
.expect("Accessing live loans requires `-Zpolonius=next`")
245+
.live_loans
246+
.contains(point, loan_idx)
247+
}
188248
}
189249

190250
/// Maps from `ty::PlaceholderRegion` values that are used in the rest of

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

+15-55
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
22
use rustc_data_structures::graph::WithSuccessors;
3-
use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
3+
use rustc_index::bit_set::HybridBitSet;
44
use rustc_index::interval::IntervalSet;
55
use rustc_infer::infer::canonical::QueryRegionConstraints;
66
use rustc_infer::infer::outlives::for_liveness;
77
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
88
use rustc_middle::traits::query::DropckOutlivesResult;
9-
use rustc_middle::ty::{RegionVid, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
9+
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
1010
use rustc_span::DUMMY_SP;
1111
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
1212
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -16,9 +16,8 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
1616
use rustc_mir_dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex};
1717
use rustc_mir_dataflow::ResultsCursor;
1818

19-
use crate::dataflow::BorrowIndex;
2019
use crate::{
21-
region_infer::values::{self, PointIndex, RegionValueElements},
20+
region_infer::values::{self, LiveLoans, PointIndex, RegionValueElements},
2221
type_check::liveness::local_use_map::LocalUseMap,
2322
type_check::liveness::polonius,
2423
type_check::NormalizeLocation,
@@ -49,22 +48,17 @@ pub(super) fn trace<'mir, 'tcx>(
4948
boring_locals: Vec<Local>,
5049
polonius_drop_used: Option<Vec<(Local, Location)>>,
5150
) {
52-
debug!("trace()");
53-
5451
let local_use_map = &LocalUseMap::build(&relevant_live_locals, elements, body);
5552

5653
// When using `-Zpolonius=next`, compute the set of loans that can reach a given region.
57-
let num_loans = typeck.borrowck_context.borrow_set.len();
58-
let mut inflowing_loans = SparseBitMatrix::new(num_loans);
5954
if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
60-
let borrowck_context = &typeck.borrowck_context;
55+
let borrowck_context = &mut typeck.borrowck_context;
6156
let borrow_set = &borrowck_context.borrow_set;
62-
let constraint_set = &borrowck_context.constraints.outlives_constraints;
63-
64-
let num_region_vars = typeck.infcx.num_region_vars();
65-
let graph = constraint_set.graph(num_region_vars);
57+
let mut live_loans = LiveLoans::new(borrow_set.len());
58+
let outlives_constraints = &borrowck_context.constraints.outlives_constraints;
59+
let graph = outlives_constraints.graph(typeck.infcx.num_region_vars());
6660
let region_graph =
67-
graph.region_graph(constraint_set, borrowck_context.universal_regions.fr_static);
61+
graph.region_graph(outlives_constraints, borrowck_context.universal_regions.fr_static);
6862

6963
// Traverse each issuing region's constraints, and record the loan as flowing into the
7064
// outlived region.
@@ -75,9 +69,13 @@ pub(super) fn trace<'mir, 'tcx>(
7569
continue;
7670
}
7771

78-
inflowing_loans.insert(succ, loan);
72+
live_loans.inflowing_loans.insert(succ, loan);
7973
}
8074
}
75+
76+
// Store the inflowing loans in the liveness constraints: they will be used to compute live
77+
// loans when liveness data is recorded there.
78+
borrowck_context.constraints.liveness_constraints.loans = Some(live_loans);
8179
};
8280

8381
let cx = LivenessContext {
@@ -88,7 +86,6 @@ pub(super) fn trace<'mir, 'tcx>(
8886
local_use_map,
8987
move_data,
9088
drop_data: FxIndexMap::default(),
91-
inflowing_loans,
9289
};
9390

9491
let mut results = LivenessResults::new(cx);
@@ -126,9 +123,6 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
126123
/// Index indicating where each variable is assigned, used, or
127124
/// dropped.
128125
local_use_map: &'me LocalUseMap,
129-
130-
/// Set of loans that flow into a given region, when using `-Zpolonius=next`.
131-
inflowing_loans: SparseBitMatrix<RegionVid, BorrowIndex>,
132126
}
133127

134128
struct DropData<'tcx> {
@@ -519,14 +513,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
519513
live_at: &IntervalSet<PointIndex>,
520514
) {
521515
debug!("add_use_live_facts_for(value={:?})", value);
522-
523-
Self::make_all_regions_live(
524-
self.elements,
525-
self.typeck,
526-
value,
527-
live_at,
528-
&self.inflowing_loans,
529-
);
516+
Self::make_all_regions_live(self.elements, self.typeck, value, live_at);
530517
}
531518

532519
/// Some variable with type `live_ty` is "drop live" at `location`
@@ -577,14 +564,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
577564
// All things in the `outlives` array may be touched by
578565
// the destructor and must be live at this point.
579566
for &kind in &drop_data.dropck_result.kinds {
580-
Self::make_all_regions_live(
581-
self.elements,
582-
self.typeck,
583-
kind,
584-
live_at,
585-
&self.inflowing_loans,
586-
);
587-
567+
Self::make_all_regions_live(self.elements, self.typeck, kind, live_at);
588568
polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind);
589569
}
590570
}
@@ -594,20 +574,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
594574
typeck: &mut TypeChecker<'_, 'tcx>,
595575
value: impl TypeVisitable<TyCtxt<'tcx>>,
596576
live_at: &IntervalSet<PointIndex>,
597-
inflowing_loans: &SparseBitMatrix<RegionVid, BorrowIndex>,
598577
) {
599578
debug!("make_all_regions_live(value={:?})", value);
600579
debug!(
601580
"make_all_regions_live: live_at={}",
602581
values::pretty_print_points(elements, live_at.iter()),
603582
);
604583

605-
// When using `-Zpolonius=next`, we want to record the loans that flow into this value's
606-
// regions as being live at the given `live_at` points: this will be used to compute the
607-
// location where a loan goes out of scope.
608-
let num_loans = typeck.borrowck_context.borrow_set.len();
609-
let value_loans = &mut HybridBitSet::new_empty(num_loans);
610-
611584
value.visit_with(&mut for_liveness::FreeRegionsVisitor {
612585
tcx: typeck.tcx(),
613586
param_env: typeck.param_env,
@@ -619,21 +592,8 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
619592
.constraints
620593
.liveness_constraints
621594
.add_points(live_region_vid, live_at);
622-
623-
// There can only be inflowing loans for this region when we are using
624-
// `-Zpolonius=next`.
625-
if let Some(inflowing) = inflowing_loans.row(live_region_vid) {
626-
value_loans.union(inflowing);
627-
}
628595
},
629596
});
630-
631-
// Record the loans reaching the value.
632-
if !value_loans.is_empty() {
633-
for point in live_at.iter() {
634-
typeck.borrowck_context.live_loans.union_row(point, value_loans);
635-
}
636-
}
637597
}
638598

639599
fn compute_drop_data(

0 commit comments

Comments
 (0)