Skip to content

Commit e38f115

Browse files
authored
Rollup merge of #136299 - lqd:polonius-next-episode-9, r=jackh726
Ignore NLL boring locals in polonius diagnostics Another easy one ``@jackh726`` (the diff is inflated by blessed test expectations don't worry :) NLLs don't compute liveness for boring locals, and therefore cannot find them in causes explaining borrows. In polonius, we don't have this liveness optimization (we may be able to do something partially similar in the future, e.g. for function parameters and the like), so we do encounter these in diagnostics even though we don't want to. This PR: - restructures the polonius context into per-phase data, in spirit as you requested in an earlier review - stores the locals NLLs would consider boring into the errors/diagnostics data - ignores these if a boring local is found when trying to explain borrows This PR fixes around 80 cases of diagnostics differences between `-Zpolonius=next` and NLLs. I've also added explicit revisions to a few polonius tests (both for the in-tree implementation as well as the datalog implementation -- even if we'll eventually remove them). I didn't do this for all the "dead" expectations that were removed from #136112 for that same reason, it's fine. I'll soon/eventually add explicit revisions where they're needed: there's only a handful of tests left to fix. r? ``@jackh726``
2 parents f2b7a29 + a616fa5 commit e38f115

30 files changed

+376
-152
lines changed

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -622,8 +622,25 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
622622
}
623623
}
624624

625+
// NLL doesn't consider boring locals for liveness, and wouldn't encounter a
626+
// `Cause::LiveVar` for such a local. Polonius can't avoid computing liveness for boring
627+
// locals yet, and will encounter them when trying to explain why a borrow contains a given
628+
// point.
629+
//
630+
// We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we
631+
// ensure that we don't emit live boring locals as explanations.
632+
let is_local_boring = |local| {
633+
if let Some(polonius_diagnostics) = self.polonius_diagnostics {
634+
polonius_diagnostics.boring_nll_locals.contains(&local)
635+
} else {
636+
assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled());
637+
638+
// Boring locals are never the cause of a borrow explanation in NLLs.
639+
false
640+
}
641+
};
625642
match find_use::find(body, regioncx, tcx, region_sub, use_location) {
626-
Some(Cause::LiveVar(local, location)) => {
643+
Some(Cause::LiveVar(local, location)) if !is_local_boring(local) => {
627644
let span = body.source_info(location).span;
628645
let spans = self
629646
.move_spans(Place::from(local).as_ref(), location)
@@ -666,7 +683,9 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
666683
}
667684
}
668685

669-
None => {
686+
Some(Cause::LiveVar(..)) | None => {
687+
// Here, under NLL: no cause was found. Under polonius: no cause was found, or a
688+
// boring local was found, which we ignore like NLLs do to match its diagnostics.
670689
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
671690
let (category, from_closure, span, region_name, path) =
672691
self.free_region_constraint_info(borrow_region_vid, region);

compiler/rustc_borrowck/src/lib.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use crate::diagnostics::{
6060
use crate::path_utils::*;
6161
use crate::place_ext::PlaceExt;
6262
use crate::places_conflict::{PlaceConflictBias, places_conflict};
63+
use crate::polonius::PoloniusDiagnosticsContext;
6364
use crate::polonius::legacy::{PoloniusLocationTable, PoloniusOutput};
6465
use crate::prefixes::PrefixSet;
6566
use crate::region_infer::RegionInferenceContext;
@@ -198,7 +199,7 @@ fn do_mir_borrowck<'tcx>(
198199
polonius_output,
199200
opt_closure_req,
200201
nll_errors,
201-
localized_outlives_constraints,
202+
polonius_diagnostics,
202203
} = nll::compute_regions(
203204
&infcx,
204205
free_regions,
@@ -270,6 +271,7 @@ fn do_mir_borrowck<'tcx>(
270271
polonius_output: None,
271272
move_errors: Vec::new(),
272273
diags_buffer,
274+
polonius_diagnostics: polonius_diagnostics.as_ref(),
273275
};
274276
struct MoveVisitor<'a, 'b, 'infcx, 'tcx> {
275277
ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
@@ -308,6 +310,7 @@ fn do_mir_borrowck<'tcx>(
308310
polonius_output,
309311
move_errors: Vec::new(),
310312
diags_buffer,
313+
polonius_diagnostics: polonius_diagnostics.as_ref(),
311314
};
312315

313316
// Compute and report region errors, if any.
@@ -329,7 +332,7 @@ fn do_mir_borrowck<'tcx>(
329332
body,
330333
&regioncx,
331334
&borrow_set,
332-
localized_outlives_constraints,
335+
polonius_diagnostics.as_ref(),
333336
&opt_closure_req,
334337
);
335338

@@ -579,6 +582,9 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
579582

580583
diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
581584
move_errors: Vec<MoveError<'tcx>>,
585+
586+
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
587+
polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
582588
}
583589

584590
// Check that:

compiler/rustc_borrowck/src/nll.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use tracing::{debug, instrument};
2727
use crate::borrow_set::BorrowSet;
2828
use crate::consumers::ConsumerOptions;
2929
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
30-
use crate::polonius::LocalizedOutlivesConstraintSet;
30+
use crate::polonius::PoloniusDiagnosticsContext;
3131
use crate::polonius::legacy::{
3232
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
3333
};
@@ -46,8 +46,9 @@ pub(crate) struct NllOutput<'tcx> {
4646
pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
4747
pub nll_errors: RegionErrors<'tcx>,
4848

49-
/// When using `-Zpolonius=next`: the localized typeck and liveness constraints.
50-
pub localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
49+
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
50+
/// localized typeck and liveness constraints.
51+
pub polonius_diagnostics: Option<PoloniusDiagnosticsContext>,
5152
}
5253

5354
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
@@ -144,7 +145,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
144145

145146
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
146147
// and use them to compute loan liveness.
147-
let localized_outlives_constraints = polonius_context.as_ref().map(|polonius_context| {
148+
let polonius_diagnostics = polonius_context.map(|polonius_context| {
148149
polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
149150
});
150151

@@ -188,7 +189,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
188189
polonius_output,
189190
opt_closure_req: closure_region_requirements,
190191
nll_errors,
191-
localized_outlives_constraints,
192+
polonius_diagnostics,
192193
}
193194
}
194195

compiler/rustc_borrowck/src/polonius/dump.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use rustc_session::config::MirIncludeSpans;
1212

1313
use crate::borrow_set::BorrowSet;
1414
use crate::constraints::OutlivesConstraint;
15-
use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
15+
use crate::polonius::{
16+
LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext,
17+
};
1618
use crate::region_infer::values::LivenessValues;
1719
use crate::type_check::Locations;
1820
use crate::{BorrowckInferCtxt, RegionInferenceContext};
@@ -23,7 +25,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
2325
body: &Body<'tcx>,
2426
regioncx: &RegionInferenceContext<'tcx>,
2527
borrow_set: &BorrowSet<'tcx>,
26-
localized_outlives_constraints: Option<LocalizedOutlivesConstraintSet>,
28+
polonius_diagnostics: Option<&PoloniusDiagnosticsContext>,
2729
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
2830
) {
2931
let tcx = infcx.tcx;
@@ -35,8 +37,8 @@ pub(crate) fn dump_polonius_mir<'tcx>(
3537
return;
3638
}
3739

38-
let localized_outlives_constraints = localized_outlives_constraints
39-
.expect("missing localized constraints with `-Zpolonius=next`");
40+
let polonius_diagnostics =
41+
polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
4042

4143
let _: io::Result<()> = try {
4244
let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
@@ -45,7 +47,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
4547
body,
4648
regioncx,
4749
borrow_set,
48-
localized_outlives_constraints,
50+
&polonius_diagnostics.localized_outlives_constraints,
4951
closure_region_requirements,
5052
&mut file,
5153
)?;
@@ -63,7 +65,7 @@ fn emit_polonius_dump<'tcx>(
6365
body: &Body<'tcx>,
6466
regioncx: &RegionInferenceContext<'tcx>,
6567
borrow_set: &BorrowSet<'tcx>,
66-
localized_outlives_constraints: LocalizedOutlivesConstraintSet,
68+
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
6769
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
6870
out: &mut dyn io::Write,
6971
) -> io::Result<()> {

compiler/rustc_borrowck/src/polonius/liveness_constraints.rs

+2-22
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
use std::collections::BTreeMap;
22

33
use rustc_index::bit_set::SparseBitMatrix;
4-
use rustc_index::interval::SparseIntervalMatrix;
54
use rustc_middle::mir::{Body, Location};
65
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
76
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
87
use rustc_mir_dataflow::points::PointIndex;
98

109
use super::{
1110
ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
12-
PoloniusContext,
11+
PoloniusLivenessContext,
1312
};
1413
use crate::region_infer::values::LivenessValues;
1514
use crate::universal_regions::UniversalRegions;
1615

17-
impl PoloniusContext {
16+
impl PoloniusLivenessContext {
1817
/// Record the variance of each region contained within the given value.
1918
pub(crate) fn record_live_region_variance<'tcx>(
2019
&mut self,
@@ -30,25 +29,6 @@ impl PoloniusContext {
3029
};
3130
extractor.relate(value, value).expect("Can't have a type error relating to itself");
3231
}
33-
34-
/// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
35-
/// need to transpose the "points where each region is live" matrix to a "live regions per point"
36-
/// matrix.
37-
// FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
38-
// borrowck.
39-
pub(crate) fn record_live_regions_per_point(
40-
&mut self,
41-
num_regions: usize,
42-
points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
43-
) {
44-
let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
45-
for region in points_per_live_region.rows() {
46-
for point in points_per_live_region.row(region).unwrap().iter() {
47-
live_regions_per_point.insert(point, region);
48-
}
49-
}
50-
self.live_regions = Some(live_regions_per_point);
51-
}
5232
}
5333

5434
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives

compiler/rustc_borrowck/src/polonius/mod.rs

+71-16
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/>
3333
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/>
3434
//!
35+
//!
36+
//! Data flows like this:
37+
//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the
38+
//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext].
39+
//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to
40+
//! the polonius shape. That's the main [PoloniusContext].
41+
//! 3) during region inference, that data and the NLL outlives constraints are used to create the
42+
//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext].
43+
//! 4) transfer this back to the main borrowck procedure: it handles computing errors and
44+
//! diagnostics, debugging and MIR dumping concerns.
3545
3646
mod constraints;
3747
mod dump;
@@ -42,8 +52,10 @@ mod typeck_constraints;
4252

4353
use std::collections::BTreeMap;
4454

55+
use rustc_data_structures::fx::FxHashSet;
4556
use rustc_index::bit_set::SparseBitMatrix;
46-
use rustc_middle::mir::Body;
57+
use rustc_index::interval::SparseIntervalMatrix;
58+
use rustc_middle::mir::{Body, Local};
4759
use rustc_middle::ty::{RegionVid, TyCtxt};
4860
use rustc_mir_dataflow::points::PointIndex;
4961

@@ -57,15 +69,40 @@ use crate::{BorrowSet, RegionInferenceContext};
5769

5870
pub(crate) type LiveLoans = SparseBitMatrix<PointIndex, BorrowIndex>;
5971

60-
/// This struct holds the data needed to create the Polonius localized constraints.
72+
/// This struct holds the liveness data created during MIR typeck, and which will be used later in
73+
/// the process, to compute the polonius localized constraints.
74+
#[derive(Default)]
75+
pub(crate) struct PoloniusLivenessContext {
76+
/// The expected edge direction per live region: the kind of directed edge we'll create as
77+
/// liveness constraints depends on the variance of types with respect to each contained region.
78+
live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
79+
80+
/// The regions that outlive free regions are used to distinguish relevant live locals from
81+
/// boring locals. A boring local is one whose type contains only such regions. Polonius
82+
/// currently has more boring locals than NLLs so we record the latter to use in errors and
83+
/// diagnostics, to focus on the locals we consider relevant and match NLL diagnostics.
84+
pub(crate) boring_nll_locals: FxHashSet<Local>,
85+
}
86+
87+
/// This struct holds the data needed to create the Polonius localized constraints. Its data is
88+
/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck.
6189
pub(crate) struct PoloniusContext {
90+
/// The liveness data we recorded during MIR typeck.
91+
liveness_context: PoloniusLivenessContext,
92+
6293
/// The set of regions that are live at a given point in the CFG, used to create localized
6394
/// outlives constraints between regions that are live at connected points in the CFG.
64-
live_regions: Option<SparseBitMatrix<PointIndex, RegionVid>>,
95+
live_regions: SparseBitMatrix<PointIndex, RegionVid>,
96+
}
6597

66-
/// The expected edge direction per live region: the kind of directed edge we'll create as
67-
/// liveness constraints depends on the variance of types with respect to each contained region.
68-
live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
98+
/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is
99+
/// computed from the [PoloniusContext] when computing NLL regions.
100+
pub(crate) struct PoloniusDiagnosticsContext {
101+
/// The localized outlives constraints that were computed in the main analysis.
102+
localized_outlives_constraints: LocalizedOutlivesConstraintSet,
103+
104+
/// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals].
105+
pub(crate) boring_nll_locals: FxHashSet<Local>,
69106
}
70107

71108
/// The direction a constraint can flow into. Used to create liveness constraints according to
@@ -83,8 +120,24 @@ enum ConstraintDirection {
83120
}
84121

85122
impl PoloniusContext {
86-
pub(crate) fn new() -> PoloniusContext {
87-
Self { live_region_variances: BTreeMap::new(), live_regions: None }
123+
/// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
124+
/// need to transpose the "points where each region is live" matrix to a "live regions per point"
125+
/// matrix.
126+
// FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
127+
// borrowck.
128+
pub(crate) fn create_from_liveness(
129+
liveness_context: PoloniusLivenessContext,
130+
num_regions: usize,
131+
points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
132+
) -> PoloniusContext {
133+
let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
134+
for region in points_per_live_region.rows() {
135+
for point in points_per_live_region.row(region).unwrap().iter() {
136+
live_regions_per_point.insert(point, region);
137+
}
138+
}
139+
140+
PoloniusContext { live_regions: live_regions_per_point, liveness_context }
88141
}
89142

90143
/// Computes live loans using the set of loans model for `-Zpolonius=next`.
@@ -95,13 +148,18 @@ impl PoloniusContext {
95148
///
96149
/// Then, this graph is traversed, and combined with kills, reachability is recorded as loan
97150
/// liveness, to be used by the loan scope and active loans computations.
151+
///
152+
/// The constraint data will be used to compute errors and diagnostics.
98153
pub(crate) fn compute_loan_liveness<'tcx>(
99-
&self,
154+
self,
100155
tcx: TyCtxt<'tcx>,
101156
regioncx: &mut RegionInferenceContext<'tcx>,
102157
body: &Body<'tcx>,
103158
borrow_set: &BorrowSet<'tcx>,
104-
) -> LocalizedOutlivesConstraintSet {
159+
) -> PoloniusDiagnosticsContext {
160+
let PoloniusLivenessContext { live_region_variances, boring_nll_locals } =
161+
self.liveness_context;
162+
105163
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
106164
convert_typeck_constraints(
107165
tcx,
@@ -112,14 +170,11 @@ impl PoloniusContext {
112170
&mut localized_outlives_constraints,
113171
);
114172

115-
let live_regions = self.live_regions.as_ref().expect(
116-
"live regions per-point data should have been created at the end of MIR typeck",
117-
);
118173
create_liveness_constraints(
119174
body,
120175
regioncx.liveness_constraints(),
121-
live_regions,
122-
&self.live_region_variances,
176+
&self.live_regions,
177+
&live_region_variances,
123178
regioncx.universal_regions(),
124179
&mut localized_outlives_constraints,
125180
);
@@ -136,6 +191,6 @@ impl PoloniusContext {
136191
);
137192
regioncx.record_live_loans(live_loans);
138193

139-
localized_outlives_constraints
194+
PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals }
140195
}
141196
}

0 commit comments

Comments
 (0)