Skip to content

Commit 0c978bc

Browse files
committed
introduce reachability to the constraint graph
1 parent 5055864 commit 0c978bc

File tree

3 files changed

+112
-10
lines changed

3 files changed

+112
-10
lines changed

compiler/rustc_borrowck/src/nll.rs

Lines changed: 5 additions & 5 deletions
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, &regioncx, body, borrow_set)
149149
});
150150

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

compiler/rustc_borrowck/src/polonius/mod.rs

Lines changed: 25 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,17 @@ 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 _live_loans = compute_loan_liveness(
130+
tcx,
131+
body,
132+
regioncx.liveness_constraints(),
133+
borrow_set,
134+
&localized_outlives_constraints,
135+
);
136+
// FIXME: record the live loans in the regioncx's liveness constraints, where the
137+
// location-insensitive variant's data is stored.
118138

119139
localized_outlives_constraints
120140
}

0 commit comments

Comments
 (0)