@@ -2,11 +2,16 @@ use std::collections::BTreeMap;
22
33use rustc_index:: bit_set:: SparseBitMatrix ;
44use rustc_index:: interval:: SparseIntervalMatrix ;
5+ use rustc_middle:: mir:: { Body , Location } ;
56use rustc_middle:: ty:: relate:: { self , Relate , RelateResult , TypeRelation } ;
67use rustc_middle:: ty:: { self , RegionVid , Ty , TyCtxt , TypeVisitable } ;
78use 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 ;
1015use crate :: universal_regions:: UniversalRegions ;
1116
1217impl 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.
0 commit comments