@@ -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 ( & 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
+
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