1use std::collections::BTreeMap;
2
3use rustc_index::bit_set::SparseBitMatrix;
4use rustc_middle::mir::{Body, Location};
5use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
6use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
7use rustc_mir_dataflow::points::PointIndex;
8
9use super::{
10 ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
11 PoloniusLivenessContext,
12};
13use crate::region_infer::values::LivenessValues;
14use crate::universal_regions::UniversalRegions;
15
16impl PoloniusLivenessContext {
17 pub(crate) fn record_live_region_variance<'tcx>(
19 &mut self,
20 tcx: TyCtxt<'tcx>,
21 universal_regions: &UniversalRegions<'tcx>,
22 value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
23 ) {
24 let mut extractor = VarianceExtractor {
25 tcx,
26 ambient_variance: ty::Variance::Covariant,
27 directions: &mut self.live_region_variances,
28 universal_regions,
29 };
30 extractor.relate(value, value).expect("Can't have a type error relating to itself");
31 }
32}
33
34pub(super) fn create_liveness_constraints<'tcx>(
37 body: &Body<'tcx>,
38 liveness: &LivenessValues,
39 live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
40 live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
41 universal_regions: &UniversalRegions<'tcx>,
42 localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
43) {
44 for (block, bb) in body.basic_blocks.iter_enumerated() {
45 let statement_count = bb.statements.len();
46 for statement_index in 0..=statement_count {
47 let current_location = Location { block, statement_index };
48 let current_point = liveness.point_from_location(current_location);
49
50 if statement_index < statement_count {
51 let next_location = Location { block, statement_index: statement_index + 1 };
54 let next_point = liveness.point_from_location(next_location);
55 propagate_loans_between_points(
56 current_point,
57 next_point,
58 live_regions,
59 live_region_variances,
60 universal_regions,
61 localized_outlives_constraints,
62 );
63 } else {
64 for successor_block in bb.terminator().successors() {
67 let next_location = Location { block: successor_block, statement_index: 0 };
68 let next_point = liveness.point_from_location(next_location);
69 propagate_loans_between_points(
70 current_point,
71 next_point,
72 live_regions,
73 live_region_variances,
74 universal_regions,
75 localized_outlives_constraints,
76 );
77 }
78 }
79 }
80 }
81}
82
83fn propagate_loans_between_points(
86 current_point: PointIndex,
87 next_point: PointIndex,
88 live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
89 live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
90 universal_regions: &UniversalRegions<'_>,
91 localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
92) {
93 for region in universal_regions.universal_regions_iter() {
100 localized_outlives_constraints.push(LocalizedOutlivesConstraint {
101 source: region,
102 from: current_point,
103 target: region,
104 to: next_point,
105 });
106 }
107
108 let Some(current_live_regions) = live_regions.row(current_point) else {
109 return;
111 };
112 let Some(next_live_regions) = live_regions.row(next_point) else {
113 return;
115 };
116
117 for region in next_live_regions.iter() {
118 if !current_live_regions.contains(region) {
119 continue;
120 }
121
122 if let Some(&direction) = live_region_variances.get(®ion) {
125 add_liveness_constraint(
126 region,
127 current_point,
128 next_point,
129 direction,
130 localized_outlives_constraints,
131 );
132 } else {
133 let fallback = ConstraintDirection::Bidirectional;
142 add_liveness_constraint(
143 region,
144 current_point,
145 next_point,
146 fallback,
147 localized_outlives_constraints,
148 );
149 }
150 }
151}
152
153fn add_liveness_constraint(
156 region: RegionVid,
157 current_point: PointIndex,
158 next_point: PointIndex,
159 direction: ConstraintDirection,
160 localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
161) {
162 match direction {
163 ConstraintDirection::Forward => {
164 localized_outlives_constraints.push(LocalizedOutlivesConstraint {
167 source: region,
168 from: current_point,
169 target: region,
170 to: next_point,
171 });
172 }
173 ConstraintDirection::Backward => {
174 localized_outlives_constraints.push(LocalizedOutlivesConstraint {
177 source: region,
178 from: next_point,
179 target: region,
180 to: current_point,
181 });
182 }
183 ConstraintDirection::Bidirectional => {
184 localized_outlives_constraints.push(LocalizedOutlivesConstraint {
186 source: region,
187 from: current_point,
188 target: region,
189 to: next_point,
190 });
191 localized_outlives_constraints.push(LocalizedOutlivesConstraint {
192 source: region,
193 from: next_point,
194 target: region,
195 to: current_point,
196 });
197 }
198 }
199}
200
201struct VarianceExtractor<'a, 'tcx> {
205 tcx: TyCtxt<'tcx>,
206 ambient_variance: ty::Variance,
207 directions: &'a mut BTreeMap<RegionVid, ConstraintDirection>,
208 universal_regions: &'a UniversalRegions<'tcx>,
209}
210
211impl<'tcx> VarianceExtractor<'_, 'tcx> {
212 fn record_variance(&mut self, region: ty::Region<'tcx>, variance: ty::Variance) {
213 if region.is_bound() {
223 return;
225 }
226
227 if region.is_erased() {
228 return;
235 }
236
237 let direction = match variance {
238 ty::Covariant => ConstraintDirection::Forward,
239 ty::Contravariant => ConstraintDirection::Backward,
240 ty::Invariant => ConstraintDirection::Bidirectional,
241 ty::Bivariant => {
242 return;
244 }
245 };
246
247 let region = self.universal_regions.to_region_vid(region);
248 self.directions
249 .entry(region)
250 .and_modify(|entry| {
251 if entry != &direction {
255 *entry = ConstraintDirection::Bidirectional;
256 }
257 })
258 .or_insert(direction);
259 }
260}
261
262impl<'tcx> TypeRelation<TyCtxt<'tcx>> for VarianceExtractor<'_, 'tcx> {
263 fn cx(&self) -> TyCtxt<'tcx> {
264 self.tcx
265 }
266
267 fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
268 &mut self,
269 variance: ty::Variance,
270 _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
271 a: T,
272 b: T,
273 ) -> RelateResult<'tcx, T> {
274 let old_ambient_variance = self.ambient_variance;
275 self.ambient_variance = self.ambient_variance.xform(variance);
276 let r = self.relate(a, b)?;
277 self.ambient_variance = old_ambient_variance;
278 Ok(r)
279 }
280
281 fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
282 assert_eq!(a, b); relate::structurally_relate_tys(self, a, b)
284 }
285
286 fn regions(
287 &mut self,
288 a: ty::Region<'tcx>,
289 b: ty::Region<'tcx>,
290 ) -> RelateResult<'tcx, ty::Region<'tcx>> {
291 assert_eq!(a, b); self.record_variance(a, self.ambient_variance);
293 Ok(a)
294 }
295
296 fn consts(
297 &mut self,
298 a: ty::Const<'tcx>,
299 b: ty::Const<'tcx>,
300 ) -> RelateResult<'tcx, ty::Const<'tcx>> {
301 assert_eq!(a, b); relate::structurally_relate_consts(self, a, b)
303 }
304
305 fn binders<T>(
306 &mut self,
307 a: ty::Binder<'tcx, T>,
308 _: ty::Binder<'tcx, T>,
309 ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
310 where
311 T: Relate<TyCtxt<'tcx>>,
312 {
313 self.relate(a.skip_binder(), a.skip_binder())?;
314 Ok(a)
315 }
316}