@@ -2,11 +2,16 @@ use std::collections::BTreeMap;
2
2
3
3
use rustc_index::bit_set::SparseBitMatrix;
4
4
use rustc_index::interval::SparseIntervalMatrix;
5
+ use rustc_middle::mir::{Body, Location};
5
6
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
6
7
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
7
8
use rustc_mir_dataflow::points::PointIndex;
8
9
9
- use super::{ConstraintDirection, PoloniusContext};
10
+ use super::{
11
+ ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
12
+ PoloniusContext,
13
+ };
14
+ use crate::region_infer::values::LivenessValues;
10
15
use crate::universal_regions::UniversalRegions;
11
16
12
17
impl PoloniusContext {
@@ -46,6 +51,173 @@ impl PoloniusContext {
46
51
}
47
52
}
48
53
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(®ion) {
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
+
49
221
/// Extracts variances for regions contained within types. Follows the same structure as
50
222
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
51
223
/// variances of regions.
0 commit comments