Skip to content

Commit 6edd002

Browse files
committed
introduce reachability to the constraint graph
1 parent 53dc2bb commit 6edd002

File tree

3 files changed

+117
-10
lines changed

3 files changed

+117
-10
lines changed

compiler/rustc_borrowck/src/nll.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
104104
constraints,
105105
universal_region_relations,
106106
opaque_type_values,
107-
mut polonius_context,
107+
polonius_context,
108108
} = type_check::type_check(
109109
infcx,
110110
body,
@@ -143,10 +143,10 @@ pub(crate) fn compute_regions<'a, 'tcx>(
143143
elements,
144144
);
145145

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

152152
// If requested: dump NLL facts, and run legacy polonius analysis.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use either::Either;
2+
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
3+
use rustc_middle::mir::Body;
4+
use rustc_middle::ty::{RegionVid, TyCtxt};
5+
use rustc_mir_dataflow::points::PointIndex;
6+
7+
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
8+
use crate::BorrowSet;
9+
use crate::region_infer::values::LivenessValues;
10+
11+
/// With the full graph of constraints, we can compute loan reachability, and trace loan liveness
12+
/// throughout the CFG.
13+
pub(super) fn compute_loan_liveness<'tcx>(
14+
_tcx: TyCtxt<'tcx>,
15+
_body: &Body<'tcx>,
16+
liveness: &LivenessValues,
17+
borrow_set: &BorrowSet<'tcx>,
18+
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
19+
live_loans: &mut LiveLoans,
20+
) {
21+
let graph = index_constraints(&localized_outlives_constraints);
22+
let mut visited = FxHashSet::default();
23+
let mut stack = Vec::new();
24+
25+
// Compute reachability per loan by traversing each loan's subgraph starting from where it is
26+
// introduced.
27+
for (loan_idx, loan) in borrow_set.iter_enumerated() {
28+
visited.clear();
29+
stack.clear();
30+
31+
let start_node = LocalizedNode {
32+
region: loan.region,
33+
point: liveness.point_from_location(loan.reserve_location),
34+
};
35+
stack.push(start_node);
36+
37+
while let Some(node) = stack.pop() {
38+
if !visited.insert(node) {
39+
continue;
40+
}
41+
42+
// Record the loan as being live on entry to this point.
43+
live_loans.insert(node.point, loan_idx);
44+
45+
for succ in outgoing_edges(&graph, node) {
46+
stack.push(succ);
47+
}
48+
}
49+
}
50+
}
51+
52+
/// The localized constraint graph is currently the per-node map of its physical edges. In the
53+
/// future, we'll add logical edges to model constraints that hold at all points in the CFG.
54+
type LocalizedConstraintGraph = FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>;
55+
56+
/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
57+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
58+
struct LocalizedNode {
59+
region: RegionVid,
60+
point: PointIndex,
61+
}
62+
63+
/// Index the outlives constraints into a graph of edges per node.
64+
fn index_constraints(constraints: &LocalizedOutlivesConstraintSet) -> LocalizedConstraintGraph {
65+
let mut edges = LocalizedConstraintGraph::default();
66+
for constraint in &constraints.outlives {
67+
let source = LocalizedNode { region: constraint.source, point: constraint.from };
68+
let target = LocalizedNode { region: constraint.target, point: constraint.to };
69+
edges.entry(source).or_default().insert(target);
70+
}
71+
72+
edges
73+
}
74+
75+
/// Returns the outgoing edges of a given node, not its transitive closure.
76+
fn outgoing_edges(
77+
graph: &LocalizedConstraintGraph,
78+
node: LocalizedNode,
79+
) -> impl Iterator<Item = LocalizedNode> + use<'_> {
80+
if let Some(edges) = graph.get(&node) {
81+
Either::Left(edges.iter().copied())
82+
} else {
83+
Either::Right(std::iter::empty())
84+
}
85+
}

compiler/rustc_borrowck/src/polonius/mod.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod constraints;
3737
mod dump;
3838
pub(crate) mod legacy;
3939
mod liveness_constraints;
40+
mod loan_liveness;
4041
mod typeck_constraints;
4142

4243
use std::collections::BTreeMap;
@@ -49,8 +50,12 @@ use rustc_mir_dataflow::points::PointIndex;
4950
pub(crate) use self::constraints::*;
5051
pub(crate) use self::dump::dump_polonius_mir;
5152
use self::liveness_constraints::create_liveness_constraints;
53+
use self::loan_liveness::compute_loan_liveness;
5254
use self::typeck_constraints::convert_typeck_constraints;
53-
use crate::RegionInferenceContext;
55+
use crate::dataflow::BorrowIndex;
56+
use crate::{BorrowSet, RegionInferenceContext};
57+
58+
pub(crate) type LiveLoans = SparseBitMatrix<PointIndex, BorrowIndex>;
5459

5560
/// This struct holds the data needed to create the Polonius localized constraints.
5661
pub(crate) struct PoloniusContext {
@@ -82,14 +87,20 @@ impl PoloniusContext {
8287
Self { live_region_variances: BTreeMap::new(), live_regions: None }
8388
}
8489

85-
/// Creates a constraint set for `-Zpolonius=next` by:
90+
/// Computes live loans using the set of loans model for `-Zpolonius=next`.
91+
///
92+
/// First, creates a constraint graph combining regions and CFG points, by:
8693
/// - converting NLL typeck constraints to be localized
8794
/// - encoding liveness constraints
88-
pub(crate) fn create_localized_constraints<'tcx>(
95+
///
96+
/// Then, this graph is traversed, and combined with kills, reachability is recorded as loan
97+
/// liveness, to be used by the loan scope and active loans computations.
98+
pub(crate) fn compute_loan_liveness<'tcx>(
8999
&self,
90100
tcx: TyCtxt<'tcx>,
91101
regioncx: &RegionInferenceContext<'tcx>,
92102
body: &Body<'tcx>,
103+
borrow_set: &BorrowSet<'tcx>,
93104
) -> LocalizedOutlivesConstraintSet {
94105
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
95106
convert_typeck_constraints(
@@ -113,8 +124,19 @@ impl PoloniusContext {
113124
&mut localized_outlives_constraints,
114125
);
115126

116-
// FIXME: here, we can trace loan reachability in the constraint graph and record this as loan
117-
// liveness for the next step in the chain, the NLL loan scope and active loans computations.
127+
// Now that we have a complete graph, we can compute reachability to trace the liveness of
128+
// loans for the next step in the chain, the NLL loan scope and active loans computations.
129+
let mut live_loans = LiveLoans::new(borrow_set.len());
130+
compute_loan_liveness(
131+
tcx,
132+
body,
133+
regioncx.liveness_constraints(),
134+
borrow_set,
135+
&localized_outlives_constraints,
136+
&mut live_loans,
137+
);
138+
// FIXME: record the live loans in the regioncx's liveness constraints, where the
139+
// location-insensitive variant's data is stored.
118140

119141
localized_outlives_constraints
120142
}

0 commit comments

Comments
 (0)