Skip to content

Commit 93527d2

Browse files
committed
liveness constraints: draw the rest of the owl
1 parent 6e88db9 commit 93527d2

File tree

2 files changed

+180
-71
lines changed

2 files changed

+180
-71
lines changed

compiler/rustc_borrowck/src/polonius/liveness_constraints.rs

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

33
use rustc_index::bit_set::SparseBitMatrix;
44
use rustc_index::interval::SparseIntervalMatrix;
5+
use rustc_middle::mir::{Body, Location};
56
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
67
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
78
use rustc_mir_dataflow::points::PointIndex;
89

9-
use super::{ConstraintDirection, PoloniusContext};
10+
use super::{
11+
ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
12+
PoloniusContext,
13+
};
14+
use crate::region_infer::values::LivenessValues;
1015
use crate::universal_regions::UniversalRegions;
1116

1217
impl PoloniusContext {
@@ -46,6 +51,173 @@ impl PoloniusContext {
4651
}
4752
}
4853

54+
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
55+
/// constraints for loans that are propagated to the next statements.
56+
pub(super) fn create_liveness_constraints<'tcx>(
57+
body: &Body<'tcx>,
58+
liveness: &LivenessValues,
59+
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
60+
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
61+
universal_regions: &UniversalRegions<'tcx>,
62+
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
63+
) {
64+
for (block, bb) in body.basic_blocks.iter_enumerated() {
65+
let statement_count = bb.statements.len();
66+
for statement_index in 0..=statement_count {
67+
let current_location = Location { block, statement_index };
68+
let current_point = liveness.point_from_location(current_location);
69+
70+
if statement_index < statement_count {
71+
// Intra-block edges, straight line constraints from each point to its successor
72+
// within the same block.
73+
let next_location = Location { block, statement_index: statement_index + 1 };
74+
let next_point = liveness.point_from_location(next_location);
75+
propagate_loans_between_points(
76+
current_point,
77+
next_point,
78+
live_regions,
79+
live_region_variances,
80+
universal_regions,
81+
localized_outlives_constraints,
82+
);
83+
} else {
84+
// Inter-block edges, from the block's terminator to each successor block's entry
85+
// point.
86+
for successor_block in bb.terminator().successors() {
87+
let next_location = Location { block: successor_block, statement_index: 0 };
88+
let next_point = liveness.point_from_location(next_location);
89+
propagate_loans_between_points(
90+
current_point,
91+
next_point,
92+
live_regions,
93+
live_region_variances,
94+
universal_regions,
95+
localized_outlives_constraints,
96+
);
97+
}
98+
}
99+
}
100+
}
101+
}
102+
103+
/// Propagate loans within a region between two points in the CFG, if that region is live at both
104+
/// the source and target points.
105+
fn propagate_loans_between_points(
106+
current_point: PointIndex,
107+
next_point: PointIndex,
108+
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
109+
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
110+
universal_regions: &UniversalRegions<'_>,
111+
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
112+
) {
113+
// Universal regions are semantically live at all points.
114+
// Note: we always have universal regions but they're not always (or often) involved in the
115+
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
116+
// will be disconnected from the rest of the graph and thus, unnecessary.
117+
//
118+
// FIXME: only emit the edges of universal regions that existential regions can reach.
119+
for region in universal_regions.universal_regions_iter() {
120+
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
121+
source: region,
122+
from: current_point,
123+
target: region,
124+
to: next_point,
125+
});
126+
}
127+
128+
let Some(current_live_regions) = live_regions.row(current_point) else {
129+
// There are no constraints to add: there are no live regions at the current point.
130+
return;
131+
};
132+
let Some(next_live_regions) = live_regions.row(next_point) else {
133+
// There are no constraints to add: there are no live regions at the next point.
134+
return;
135+
};
136+
137+
for region in next_live_regions.iter() {
138+
if !current_live_regions.contains(region) {
139+
continue;
140+
}
141+
142+
// `region` is indeed live at both points, add a constraint between them, according to
143+
// variance.
144+
if let Some(&direction) = live_region_variances.get(&region) {
145+
add_liveness_constraint(
146+
region,
147+
current_point,
148+
next_point,
149+
direction,
150+
localized_outlives_constraints,
151+
);
152+
} else {
153+
// Note: there currently are cases related to promoted and const generics, where we
154+
// don't yet have variance information (possibly about temporary regions created when
155+
// typeck sanitizes the promoteds). Until that is done, we conservatively fallback to
156+
// maximizing reachability by adding a bidirectional edge here. This will not limit
157+
// traversal whatsoever, and thus propagate liveness when needed.
158+
//
159+
// FIXME: add the missing variance information and remove this fallback bidirectional
160+
// edge.
161+
let fallback = ConstraintDirection::Bidirectional;
162+
add_liveness_constraint(
163+
region,
164+
current_point,
165+
next_point,
166+
fallback,
167+
localized_outlives_constraints,
168+
);
169+
}
170+
}
171+
}
172+
173+
/// Adds `LocalizedOutlivesConstraint`s between two connected points, according to the given edge
174+
/// direction.
175+
fn add_liveness_constraint(
176+
region: RegionVid,
177+
current_point: PointIndex,
178+
next_point: PointIndex,
179+
direction: ConstraintDirection,
180+
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
181+
) {
182+
match direction {
183+
ConstraintDirection::Forward => {
184+
// Covariant cases: loans flow in the regular direction, from the current point to the
185+
// next point.
186+
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
187+
source: region,
188+
from: current_point,
189+
target: region,
190+
to: next_point,
191+
});
192+
}
193+
ConstraintDirection::Backward => {
194+
// Contravariant cases: loans flow in the inverse direction, from the next point to the
195+
// current point.
196+
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
197+
source: region,
198+
from: next_point,
199+
target: region,
200+
to: current_point,
201+
});
202+
}
203+
ConstraintDirection::Bidirectional => {
204+
// For invariant cases, loans can flow in both directions: we add both edges.
205+
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
206+
source: region,
207+
from: current_point,
208+
target: region,
209+
to: next_point,
210+
});
211+
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
212+
source: region,
213+
from: next_point,
214+
target: region,
215+
to: current_point,
216+
});
217+
}
218+
}
219+
}
220+
49221
/// Extracts variances for regions contained within types. Follows the same structure as
50222
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
51223
/// variances of regions.

compiler/rustc_borrowck/src/polonius/mod.rs

+7-70
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ use rustc_mir_dataflow::points::PointIndex;
4747

4848
pub(crate) use self::constraints::*;
4949
pub(crate) use self::dump::dump_polonius_mir;
50+
use self::liveness_constraints::create_liveness_constraints;
5051
use crate::RegionInferenceContext;
5152
use crate::constraints::OutlivesConstraint;
5253
use crate::region_infer::values::LivenessValues;
5354
use crate::type_check::Locations;
54-
use crate::universal_regions::UniversalRegions;
5555

5656
/// This struct holds the data needed to create the Polonius localized constraints.
5757
pub(crate) struct PoloniusContext {
@@ -98,9 +98,15 @@ impl PoloniusContext {
9898
regioncx.outlives_constraints(),
9999
&mut localized_outlives_constraints,
100100
);
101+
102+
let live_regions = self.live_regions.as_ref().expect(
103+
"live regions per-point data should have been created at the end of MIR typeck",
104+
);
101105
create_liveness_constraints(
102106
body,
103107
regioncx.liveness_constraints(),
108+
live_regions,
109+
&self.live_region_variances,
104110
regioncx.universal_regions(),
105111
&mut localized_outlives_constraints,
106112
);
@@ -146,72 +152,3 @@ fn convert_typeck_constraints<'tcx>(
146152
}
147153
}
148154
}
149-
150-
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
151-
/// constraints for loans that are propagated to the next statements.
152-
pub(crate) fn create_liveness_constraints<'tcx>(
153-
body: &Body<'tcx>,
154-
liveness: &LivenessValues,
155-
universal_regions: &UniversalRegions<'tcx>,
156-
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
157-
) {
158-
for (block, bb) in body.basic_blocks.iter_enumerated() {
159-
let statement_count = bb.statements.len();
160-
for statement_index in 0..=statement_count {
161-
let current_location = Location { block, statement_index };
162-
let current_point = liveness.point_from_location(current_location);
163-
164-
if statement_index < statement_count {
165-
// Intra-block edges, straight line constraints from each point to its successor
166-
// within the same block.
167-
let next_location = Location { block, statement_index: statement_index + 1 };
168-
let next_point = liveness.point_from_location(next_location);
169-
propagate_loans_between_points(
170-
current_point,
171-
next_point,
172-
liveness,
173-
universal_regions,
174-
localized_outlives_constraints,
175-
);
176-
} else {
177-
// Inter-block edges, from the block's terminator to each successor block's entry
178-
// point.
179-
for successor_block in bb.terminator().successors() {
180-
let next_location = Location { block: successor_block, statement_index: 0 };
181-
let next_point = liveness.point_from_location(next_location);
182-
propagate_loans_between_points(
183-
current_point,
184-
next_point,
185-
liveness,
186-
universal_regions,
187-
localized_outlives_constraints,
188-
);
189-
}
190-
}
191-
}
192-
}
193-
}
194-
195-
/// Propagate loans within a region between two points in the CFG, if that region is live at both
196-
/// the source and target points.
197-
fn propagate_loans_between_points(
198-
current_point: PointIndex,
199-
next_point: PointIndex,
200-
_liveness: &LivenessValues,
201-
universal_regions: &UniversalRegions<'_>,
202-
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
203-
) {
204-
// Universal regions are semantically live at all points.
205-
// Note: we always have universal regions but they're not always (or often) involved in the
206-
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
207-
// will be disconnected from the rest of the graph and thus, unnecessary.
208-
// FIXME: only emit the edges of universal regions that existential regions can reach.
209-
for region in universal_regions.universal_regions_iter() {
210-
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
211-
source: region,
212-
from: current_point,
213-
target: region,
214-
to: next_point,
215-
});
216-
}
217-
}

0 commit comments

Comments
 (0)