Skip to content

Commit 3396279

Browse files
authored
Unrolled build for rust-lang#134920
Rollup merge of rust-lang#134920 - lqd:polonius-next-episode-6, r=jackh726 Convert typeck constraints in location-sensitive polonius In this PR, we do a big chunk of the work of localizing regular outlives constraints. The slightly annoying thing is handling effectful statements: usually the subset graph propagates loans at a single point between regions, and liveness propagates loans between points within a single region, but some statements have effects applied on exit. This was also a problem before, in datalog polonius terms and Niko's solution at the time, this is about: the mid-point. The idea was to duplicate all MIR locations into two physical points, and orchestrate the effects with that. Somewhat easier to do, but double the CFG. We've always believed we didn't _need_ midpoints in principle, as we can represent changes on exit as on happening entry to the successor, but there's some difficulty in tracking the position information at sufficient granularity through outlives relation (especially since we also have bidirectional edges and time-traveling now). Now, that is surely what we should be doing in the future. In the mean time, I infer this from the kind of statement/terminator where an outlives constraint arose. It's not particularly complicated but some explanation will help clarify the code. Assignments (in their various forms) are the quintessential example of these crossover cases: loans that would flow into the LHS would not be visible on entry to the point but on exit -- so we'll localize these edges to the successor. Let's look at a real-world example, involving invariance for bidirectional edges: ```rust let mut _1: HashMap<i32, &'7 i32>; let mut _3: &'9 mut HashMap<i32, &'10 i32>; ... /* at bb1[3]: */ _3 = &'3 mut _1; ``` Here, typeck expectedly produces 3 outlives constraints today: 1. `'3 -> '9` 2. `'7 -> '10` 3. `'10 -> '7` And we localize them like so, 1. `'3 -> '9` flows into the LHS and becomes: `3_bb1_3 -> 9_bb1_4` 2. `'7 -> '10` flows into the LHS and becomes: `7_bb1_3 -> 10_bb1_4` 3. `'10 -> '7` flows from the LHS and becomes: `10_bb1_4 -> 7_bb1_3` (time traveling 👌) --- r? ``@jackh726`` To keep you entertained during the holidays I also threw in a couple of small changes removing cruft in the borrow checker. We're actually getting there. The next PR will be the last one needed to get end-to-end tests working.
2 parents 1f81f90 + fc7ee23 commit 3396279

File tree

11 files changed

+309
-117
lines changed

11 files changed

+309
-117
lines changed

compiler/rustc_borrowck/src/borrow_set.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use rustc_mir_dataflow::move_paths::MoveData;
1111
use tracing::debug;
1212

1313
use crate::BorrowIndex;
14-
use crate::path_utils::allow_two_phase_borrow;
1514
use crate::place_ext::PlaceExt;
1615

1716
pub struct BorrowSet<'tcx> {
@@ -350,7 +349,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
350349
start_location, assigned_place, borrow_index,
351350
);
352351

353-
if !allow_two_phase_borrow(kind) {
352+
if !kind.allows_two_phase_borrow() {
354353
debug!(" -> {:?}", start_location);
355354
return;
356355
}

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

+34-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir::intravisit::Visitor;
1010
use rustc_hir::{self as hir, BindingMode, ByRef, Node};
1111
use rustc_middle::bug;
1212
use rustc_middle::hir::place::PlaceBase;
13+
use rustc_middle::mir::visit::PlaceContext;
1314
use rustc_middle::mir::{
1415
self, BindingForm, Local, LocalDecl, LocalInfo, LocalKind, Location, Mutability, Place,
1516
PlaceRef, ProjectionElem,
@@ -22,7 +23,6 @@ use rustc_trait_selection::traits;
2223
use tracing::debug;
2324

2425
use crate::diagnostics::BorrowedContentSource;
25-
use crate::util::FindAssignments;
2626
use crate::{MirBorrowckCtxt, session_diagnostics};
2727

2828
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -1088,6 +1088,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
10881088
}
10891089
}
10901090

1091+
/// Finds all statements that assign directly to local (i.e., X = ...) and returns their
1092+
/// locations.
1093+
fn find_assignments(&self, local: Local) -> Vec<Location> {
1094+
use rustc_middle::mir::visit::Visitor;
1095+
1096+
struct FindLocalAssignmentVisitor {
1097+
needle: Local,
1098+
locations: Vec<Location>,
1099+
}
1100+
1101+
impl<'tcx> Visitor<'tcx> for FindLocalAssignmentVisitor {
1102+
fn visit_local(
1103+
&mut self,
1104+
local: Local,
1105+
place_context: PlaceContext,
1106+
location: Location,
1107+
) {
1108+
if self.needle != local {
1109+
return;
1110+
}
1111+
1112+
if place_context.is_place_assignment() {
1113+
self.locations.push(location);
1114+
}
1115+
}
1116+
}
1117+
1118+
let mut visitor = FindLocalAssignmentVisitor { needle: local, locations: vec![] };
1119+
visitor.visit_body(self.body);
1120+
visitor.locations
1121+
}
1122+
10911123
fn suggest_make_local_mut(&self, err: &mut Diag<'_>, local: Local, name: Symbol) {
10921124
let local_decl = &self.body.local_decls[local];
10931125

@@ -1121,7 +1153,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
11211153
})) => {
11221154
// check if the RHS is from desugaring
11231155
let opt_assignment_rhs_span =
1124-
self.body.find_assignments(local).first().map(|&location| {
1156+
self.find_assignments(local).first().map(|&location| {
11251157
if let Some(mir::Statement {
11261158
source_info: _,
11271159
kind:

compiler/rustc_borrowck/src/lib.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
use std::cell::RefCell;
1919
use std::marker::PhantomData;
20-
use std::ops::Deref;
20+
use std::ops::{ControlFlow, Deref};
2121

2222
use rustc_abi::FieldIdx;
2323
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
@@ -81,7 +81,6 @@ mod session_diagnostics;
8181
mod type_check;
8282
mod universal_regions;
8383
mod used_muts;
84-
mod util;
8584

8685
/// A public API provided for the Rust compiler consumers.
8786
pub mod consumers;
@@ -1054,31 +1053,31 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
10541053
rw,
10551054
(borrow_index, borrow),
10561055
);
1057-
Control::Continue
1056+
ControlFlow::Continue(())
10581057
}
10591058

10601059
(Read(_), BorrowKind::Shared | BorrowKind::Fake(_))
10611060
| (
10621061
Read(ReadKind::Borrow(BorrowKind::Fake(FakeBorrowKind::Shallow))),
10631062
BorrowKind::Mut { .. },
1064-
) => Control::Continue,
1063+
) => ControlFlow::Continue(()),
10651064

10661065
(Reservation(_), BorrowKind::Fake(_) | BorrowKind::Shared) => {
10671066
// This used to be a future compatibility warning (to be
10681067
// disallowed on NLL). See rust-lang/rust#56254
1069-
Control::Continue
1068+
ControlFlow::Continue(())
10701069
}
10711070

10721071
(Write(WriteKind::Move), BorrowKind::Fake(FakeBorrowKind::Shallow)) => {
10731072
// Handled by initialization checks.
1074-
Control::Continue
1073+
ControlFlow::Continue(())
10751074
}
10761075

10771076
(Read(kind), BorrowKind::Mut { .. }) => {
10781077
// Reading from mere reservations of mutable-borrows is OK.
10791078
if !is_active(this.dominators(), borrow, location) {
1080-
assert!(allow_two_phase_borrow(borrow.kind));
1081-
return Control::Continue;
1079+
assert!(borrow.kind.allows_two_phase_borrow());
1080+
return ControlFlow::Continue(());
10821081
}
10831082

10841083
error_reported = true;
@@ -1094,7 +1093,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
10941093
this.buffer_error(err);
10951094
}
10961095
}
1097-
Control::Break
1096+
ControlFlow::Break(())
10981097
}
10991098

11001099
(Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
@@ -1141,7 +1140,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
11411140
this.report_illegal_mutation_of_borrowed(location, place_span, borrow)
11421141
}
11431142
}
1144-
Control::Break
1143+
ControlFlow::Break(())
11451144
}
11461145
},
11471146
);
@@ -1185,7 +1184,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
11851184
}
11861185
BorrowKind::Mut { .. } => {
11871186
let wk = WriteKind::MutableBorrow(bk);
1188-
if allow_two_phase_borrow(bk) {
1187+
if bk.allows_two_phase_borrow() {
11891188
(Deep, Reservation(wk))
11901189
} else {
11911190
(Deep, Write(wk))

compiler/rustc_borrowck/src/nll.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ pub(crate) fn compute_regions<'a, 'tcx>(
142142

143143
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives
144144
// constraints.
145-
let localized_outlives_constraints = polonius_context
146-
.as_mut()
147-
.map(|polonius_context| polonius_context.create_localized_constraints(&mut regioncx, body));
145+
let localized_outlives_constraints = polonius_context.as_mut().map(|polonius_context| {
146+
polonius_context.create_localized_constraints(infcx.tcx, &regioncx, body)
147+
});
148148

149149
// If requested: dump NLL facts, and run legacy polonius analysis.
150150
let polonius_output = all_facts.as_ref().and_then(|all_facts| {

compiler/rustc_borrowck/src/path_utils.rs

+5-17
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
1+
use std::ops::ControlFlow;
2+
13
use rustc_abi::FieldIdx;
24
use rustc_data_structures::graph::dominators::Dominators;
3-
use rustc_middle::mir::{BasicBlock, Body, BorrowKind, Location, Place, PlaceRef, ProjectionElem};
5+
use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
46
use rustc_middle::ty::TyCtxt;
57
use tracing::debug;
68

79
use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
810
use crate::{AccessDepth, BorrowIndex, places_conflict};
911

10-
/// Returns `true` if the borrow represented by `kind` is
11-
/// allowed to be split into separate Reservation and
12-
/// Activation phases.
13-
pub(super) fn allow_two_phase_borrow(kind: BorrowKind) -> bool {
14-
kind.allows_two_phase_borrow()
15-
}
16-
17-
/// Control for the path borrow checking code
18-
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
19-
pub(super) enum Control {
20-
Continue,
21-
Break,
22-
}
23-
2412
/// Encapsulates the idea of iterating over every borrow that involves a particular path
2513
pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
2614
s: &mut S,
@@ -31,7 +19,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
3119
is_candidate: I,
3220
mut op: F,
3321
) where
34-
F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control,
22+
F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> ControlFlow<()>,
3523
I: Fn(BorrowIndex) -> bool,
3624
{
3725
let (access, place) = access_place;
@@ -62,7 +50,7 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
6250
i, borrowed, place, access
6351
);
6452
let ctrl = op(s, i, borrowed);
65-
if ctrl == Control::Break {
53+
if matches!(ctrl, ControlFlow::Break(_)) {
6654
return;
6755
}
6856
}

compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ops::ControlFlow;
2+
13
use rustc_data_structures::graph::dominators::Dominators;
24
use rustc_middle::bug;
35
use rustc_middle::mir::visit::Visitor;
@@ -260,7 +262,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
260262
}
261263
BorrowKind::Mut { .. } => {
262264
let wk = WriteKind::MutableBorrow(bk);
263-
if allow_two_phase_borrow(bk) {
265+
if bk.allows_two_phase_borrow() {
264266
(Deep, Reservation(wk))
265267
} else {
266268
(Deep, Write(wk))
@@ -378,8 +380,8 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
378380
// Reading from mere reservations of mutable-borrows is OK.
379381
if !is_active(this.dominators, borrow, location) {
380382
// If the borrow isn't active yet, reads don't invalidate it
381-
assert!(allow_two_phase_borrow(borrow.kind));
382-
return Control::Continue;
383+
assert!(borrow.kind.allows_two_phase_borrow());
384+
return ControlFlow::Continue(());
383385
}
384386

385387
// Unique and mutable borrows are invalidated by reads from any
@@ -395,7 +397,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
395397
this.emit_loan_invalidated_at(borrow_index, location);
396398
}
397399
}
398-
Control::Continue
400+
ControlFlow::Continue(())
399401
},
400402
);
401403
}

compiler/rustc_borrowck/src/polonius/mod.rs

+7-40
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,20 @@ mod constraints;
3737
mod dump;
3838
pub(crate) mod legacy;
3939
mod liveness_constraints;
40+
mod typeck_constraints;
4041

4142
use std::collections::BTreeMap;
4243

4344
use rustc_index::bit_set::SparseBitMatrix;
44-
use rustc_middle::mir::{Body, Location};
45-
use rustc_middle::ty::RegionVid;
45+
use rustc_middle::mir::Body;
46+
use rustc_middle::ty::{RegionVid, TyCtxt};
4647
use rustc_mir_dataflow::points::PointIndex;
4748

4849
pub(crate) use self::constraints::*;
4950
pub(crate) use self::dump::dump_polonius_mir;
5051
use self::liveness_constraints::create_liveness_constraints;
52+
use self::typeck_constraints::convert_typeck_constraints;
5153
use crate::RegionInferenceContext;
52-
use crate::constraints::OutlivesConstraint;
53-
use crate::region_infer::values::LivenessValues;
54-
use crate::type_check::Locations;
5554

5655
/// This struct holds the data needed to create the Polonius localized constraints.
5756
pub(crate) struct PoloniusContext {
@@ -88,14 +87,17 @@ impl PoloniusContext {
8887
/// - encoding liveness constraints
8988
pub(crate) fn create_localized_constraints<'tcx>(
9089
&self,
90+
tcx: TyCtxt<'tcx>,
9191
regioncx: &RegionInferenceContext<'tcx>,
9292
body: &Body<'tcx>,
9393
) -> LocalizedOutlivesConstraintSet {
9494
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
9595
convert_typeck_constraints(
96+
tcx,
9697
body,
9798
regioncx.liveness_constraints(),
9899
regioncx.outlives_constraints(),
100+
regioncx.universal_regions(),
99101
&mut localized_outlives_constraints,
100102
);
101103

@@ -117,38 +119,3 @@ impl PoloniusContext {
117119
localized_outlives_constraints
118120
}
119121
}
120-
121-
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
122-
/// location where effects start to be visible).
123-
fn convert_typeck_constraints<'tcx>(
124-
body: &Body<'tcx>,
125-
liveness: &LivenessValues,
126-
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
127-
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
128-
) {
129-
for outlives_constraint in outlives_constraints {
130-
match outlives_constraint.locations {
131-
Locations::All(_) => {
132-
// For now, turn logical constraints holding at all points into physical edges at
133-
// every point in the graph.
134-
// FIXME: encode this into *traversal* instead.
135-
for (block, bb) in body.basic_blocks.iter_enumerated() {
136-
let statement_count = bb.statements.len();
137-
for statement_index in 0..=statement_count {
138-
let current_location = Location { block, statement_index };
139-
let current_point = liveness.point_from_location(current_location);
140-
141-
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
142-
source: outlives_constraint.sup,
143-
from: current_point,
144-
target: outlives_constraint.sub,
145-
to: current_point,
146-
});
147-
}
148-
}
149-
}
150-
151-
_ => {}
152-
}
153-
}
154-
}

0 commit comments

Comments
 (0)