Skip to content

Commit e5cd0c5

Browse files
authored
Rollup merge of rust-lang#134980 - lqd:polonius-next-episode-7, r=jackh726
Location-sensitive polonius prototype: endgame This PR sets up the naive location-sensitive analysis end-to-end, and replaces the location-insensitive analysis. It's roughly all the in-progress work I wanted to land for the prototype, modulo cleanups I still want to do after the holidays, or the polonius debugger, and so on. Here, we traverse the localized constraint graph, have to deal with kills and time-traveling (👌), and record that as loan liveness for the existing scope and active loans computations. Then the near future looks like this, especially if the 2025h1 project goal is accepted: - gradually bringing it up to completion - analyzing and fixing the few remaining test failures - going over the *numerous* fixmes in this prototype (one of which is similar to a hang on one test's millions and millions of constraints) - trying to see how to lower the impact of the lack of NLL liveness optimization on diagnostics, and their categorization of local variables and temporaries (the vast majority of blessed expectations differences), as well as the couple ICEs trying to find an NLL constraint to blame for errors. - dealing with the theoretical weakness around kills, conflating reachability for the two TCS, etc that is described ad nauseam in the code. - switching the compare mode to the in-tree implementation, and blessing the diagnostics - apart from the hang, it's not catastrophically slower on our test suite, so then we can try to enable it on CI - checking crater, maybe trying to make it faster :3, etc. I've tried to gradually introduce this PR's work over 4 commits, because it's kind of subtle/annoying, and Niko/I are not completely convinced yet. That one comment explaining the situation is maybe 30% of the PR 😓. Who knew that spacetime reachability and time-traveling could be mind bending. I kinda found this late and the impact on this part of the computation was a bit unexpected to us. A bit more care/thought will be needed here. I've described my plan in the comments though. In any case, I believe we have the current implementation is a conservative approximation that shouldn't result in unsoundness but false positives at worst. So it feels fine for now. r? `@jackh726` --- Fixes rust-lang#127628 -- which was a assertion triggered for a difference in loan computation between NLLs and the location-insensitive analysis. That doesn't exist anymore so I've removed this crash test.
2 parents 89d1865 + 8ac045d commit e5cd0c5

File tree

9 files changed

+386
-185
lines changed

9 files changed

+386
-185
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+45-56
Original file line numberDiff line numberDiff line change
@@ -187,19 +187,28 @@ struct OutOfScopePrecomputer<'a, 'tcx> {
187187
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
188188
}
189189

190-
impl<'a, 'tcx> OutOfScopePrecomputer<'a, 'tcx> {
191-
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
192-
OutOfScopePrecomputer {
190+
impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
191+
fn compute(
192+
body: &Body<'tcx>,
193+
regioncx: &RegionInferenceContext<'tcx>,
194+
borrow_set: &BorrowSet<'tcx>,
195+
) -> FxIndexMap<Location, Vec<BorrowIndex>> {
196+
let mut prec = OutOfScopePrecomputer {
193197
visited: DenseBitSet::new_empty(body.basic_blocks.len()),
194198
visit_stack: vec![],
195199
body,
196200
regioncx,
197201
borrows_out_of_scope_at_location: FxIndexMap::default(),
202+
};
203+
for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
204+
let borrow_region = borrow_data.region;
205+
let location = borrow_data.reserve_location;
206+
prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
198207
}
208+
209+
prec.borrows_out_of_scope_at_location
199210
}
200-
}
201211

202-
impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
203212
fn precompute_borrows_out_of_scope(
204213
&mut self,
205214
borrow_index: BorrowIndex,
@@ -280,15 +289,7 @@ pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
280289
regioncx: &RegionInferenceContext<'tcx>,
281290
borrow_set: &BorrowSet<'tcx>,
282291
) -> FxIndexMap<Location, Vec<BorrowIndex>> {
283-
let mut prec = OutOfScopePrecomputer::new(body, regioncx);
284-
for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
285-
let borrow_region = borrow_data.region;
286-
let location = borrow_data.reserve_location;
287-
288-
prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
289-
}
290-
291-
prec.borrows_out_of_scope_at_location
292+
OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
292293
}
293294

294295
struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
@@ -300,19 +301,30 @@ struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
300301
loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
301302
}
302303

303-
impl<'a, 'tcx> PoloniusOutOfScopePrecomputer<'a, 'tcx> {
304-
fn new(body: &'a Body<'tcx>, regioncx: &'a RegionInferenceContext<'tcx>) -> Self {
305-
Self {
304+
impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
305+
fn compute(
306+
body: &Body<'tcx>,
307+
regioncx: &RegionInferenceContext<'tcx>,
308+
borrow_set: &BorrowSet<'tcx>,
309+
) -> FxIndexMap<Location, Vec<BorrowIndex>> {
310+
// The in-tree polonius analysis computes loans going out of scope using the
311+
// set-of-loans model.
312+
let mut prec = PoloniusOutOfScopePrecomputer {
306313
visited: DenseBitSet::new_empty(body.basic_blocks.len()),
307314
visit_stack: vec![],
308315
body,
309316
regioncx,
310317
loans_out_of_scope_at_location: FxIndexMap::default(),
318+
};
319+
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
320+
let issuing_region = loan_data.region;
321+
let loan_issued_at = loan_data.reserve_location;
322+
prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at);
311323
}
324+
325+
prec.loans_out_of_scope_at_location
312326
}
313-
}
314327

315-
impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
316328
/// Loans are in scope while they are live: whether they are contained within any live region.
317329
/// In the location-insensitive analysis, a loan will be contained in a region if the issuing
318330
/// region can reach it in the subset graph. So this is a reachability problem.
@@ -325,10 +337,17 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
325337
let sccs = self.regioncx.constraint_sccs();
326338
let universal_regions = self.regioncx.universal_regions();
327339

340+
// The loop below was useful for the location-insensitive analysis but shouldn't be
341+
// impactful in the location-sensitive case. It seems that it does, however, as without it a
342+
// handful of tests fail. That likely means some liveness or outlives data related to choice
343+
// regions is missing
344+
// FIXME: investigate the impact of loans traversing applied member constraints and why some
345+
// tests fail otherwise.
346+
//
328347
// We first handle the cases where the loan doesn't go out of scope, depending on the
329348
// issuing region's successors.
330349
for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
331-
// 1. Via applied member constraints
350+
// Via applied member constraints
332351
//
333352
// The issuing region can flow into the choice regions, and they are either:
334353
// - placeholders or free regions themselves,
@@ -346,14 +365,6 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
346365
return;
347366
}
348367
}
349-
350-
// 2. Via regions that are live at all points: placeholders and free regions.
351-
//
352-
// If the issuing region outlives such a region, its loan escapes the function and
353-
// cannot go out of scope. We can early return.
354-
if self.regioncx.is_region_live_at_all_points(successor) {
355-
return;
356-
}
357368
}
358369

359370
let first_block = loan_issued_at.block;
@@ -461,34 +472,12 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
461472
regioncx: &RegionInferenceContext<'tcx>,
462473
borrow_set: &'a BorrowSet<'tcx>,
463474
) -> Self {
464-
let mut borrows_out_of_scope_at_location =
465-
calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set);
466-
467-
// The in-tree polonius analysis computes loans going out of scope using the set-of-loans
468-
// model, and makes sure they're identical to the existing computation of the set-of-points
469-
// model.
470-
if tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
471-
let mut polonius_prec = PoloniusOutOfScopePrecomputer::new(body, regioncx);
472-
for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
473-
let issuing_region = loan_data.region;
474-
let loan_issued_at = loan_data.reserve_location;
475-
476-
polonius_prec.precompute_loans_out_of_scope(
477-
loan_idx,
478-
issuing_region,
479-
loan_issued_at,
480-
);
481-
}
482-
483-
assert_eq!(
484-
borrows_out_of_scope_at_location, polonius_prec.loans_out_of_scope_at_location,
485-
"polonius loan scopes differ from NLL borrow scopes, for body {:?}",
486-
body.span,
487-
);
488-
489-
borrows_out_of_scope_at_location = polonius_prec.loans_out_of_scope_at_location;
490-
}
491-
475+
let borrows_out_of_scope_at_location =
476+
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
477+
calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
478+
} else {
479+
PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
480+
};
492481
Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
493482
}
494483

compiler/rustc_borrowck/src/nll.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
103103
constraints,
104104
universal_region_relations,
105105
opaque_type_values,
106-
mut polonius_context,
106+
polonius_context,
107107
} = type_check::type_check(
108108
infcx,
109109
body,
@@ -142,10 +142,10 @@ pub(crate) fn compute_regions<'a, 'tcx>(
142142
location_map,
143143
);
144144

145-
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives
146-
// constraints.
147-
let localized_outlives_constraints = polonius_context.as_mut().map(|polonius_context| {
148-
polonius_context.create_localized_constraints(infcx.tcx, &regioncx, body)
145+
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
146+
// and use them to compute loan liveness.
147+
let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| {
148+
polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
149149
});
150150

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

0 commit comments

Comments
 (0)