rustc_infer/infer/region_constraints/leak_check.rs
1use rustc_data_structures::fx::FxIndexMap;
2use rustc_data_structures::graph::scc::Sccs;
3use rustc_data_structures::graph::vec_graph::VecGraph;
4use rustc_index::Idx;
5use rustc_middle::span_bug;
6use rustc_middle::ty::error::TypeError;
7use tracing::{debug, instrument};
8
9use super::*;
10use crate::infer::relate::RelateResult;
11use crate::infer::snapshot::CombinedSnapshot;
12
13impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
14 /// Searches new universes created during `snapshot`, looking for
15 /// placeholders that may "leak" out from the universes they are contained
16 /// in. If any leaking placeholders are found, then an `Err` is returned
17 /// (typically leading to the snapshot being reversed). This algorithm
18 /// only looks at placeholders which cannot be named by `outer_universe`,
19 /// as this is the universe we're currently checking for a leak.
20 ///
21 /// The leak check *used* to be the only way we had to handle higher-ranked
22 /// obligations. Now that we have integrated universes into the region
23 /// solvers, this is no longer the case, but we retain the leak check for
24 /// backwards compatibility purposes. In particular, it lets us make "early"
25 /// decisions about whether a region error will be reported that are used in
26 /// coherence and elsewhere -- see #56105 and #59490 for more details. The
27 /// eventual fate of the leak checker is not yet settled.
28 ///
29 /// The leak checker works by searching for the following error patterns:
30 ///
31 /// * P1: P2, where P1 != P2
32 /// * P1: R, where R is in some universe that cannot name P1
33 ///
34 /// The idea here is that each of these patterns represents something that
35 /// the region solver would eventually report as an error, so we can detect
36 /// the error early. There is a fly in the ointment, though, in that this is
37 /// not entirely true. In particular, in the future, we may extend the
38 /// environment with implied bounds or other info about how placeholders
39 /// relate to regions in outer universes. In that case, `P1: R` for example
40 /// might become solvable.
41 ///
42 /// # Summary of the implementation
43 ///
44 /// The leak checks as follows. First, we construct a graph where `R2: R1`
45 /// implies `R2 -> R1`, and we compute the SCCs.
46 ///
47 /// For each SCC S, we compute:
48 ///
49 /// * what placeholder P it must be equal to, if any
50 /// * if there are multiple placeholders that must be equal, report an error because `P1: P2`
51 /// * the minimum universe of its constituents
52 ///
53 /// Then we walk the SCCs in dependency order and compute
54 ///
55 /// * what placeholder they must outlive transitively
56 /// * if they must also be equal to a placeholder, report an error because `P1: P2`
57 /// * minimum universe U of all SCCs they must outlive
58 /// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as
59 /// that indicates `P: R` and `R` is in an incompatible universe
60 ///
61 /// To improve performance and for the old trait solver caching to be sound, this takes
62 /// an optional snapshot in which case we only look at region constraints added in that
63 /// snapshot. If we were to not do that the `leak_check` during evaluation can rely on
64 /// region constraints added outside of that evaluation. As that is not reflected in the
65 /// cache key this would be unsound.
66 ///
67 /// # Historical note
68 ///
69 /// Older variants of the leak check used to report errors for these
70 /// patterns, but we no longer do:
71 ///
72 /// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
73 /// * R: P1, R: P2, as above
74 #[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
75 pub fn leak_check(
76 self,
77 tcx: TyCtxt<'tcx>,
78 outer_universe: ty::UniverseIndex,
79 max_universe: ty::UniverseIndex,
80 only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
81 ) -> RelateResult<'tcx, ()> {
82 if outer_universe == max_universe {
83 return Ok(());
84 }
85
86 let mini_graph = MiniGraph::new(tcx, &self, only_consider_snapshot);
87
88 let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
89 leak_check.assign_placeholder_values()?;
90 leak_check.propagate_scc_value()?;
91 Ok(())
92 }
93}
94
95struct LeakCheck<'a, 'tcx> {
96 tcx: TyCtxt<'tcx>,
97 outer_universe: ty::UniverseIndex,
98 mini_graph: MiniGraph<'tcx>,
99 rcc: RegionConstraintCollector<'a, 'tcx>,
100
101 // Initially, for each SCC S, stores a placeholder `P` such that `S = P`
102 // must hold.
103 //
104 // Later, during the [`LeakCheck::propagate_scc_value`] function, this array
105 // is repurposed to store some placeholder `P` such that the weaker
106 // condition `S: P` must hold. (This is true if `S: S1` transitively and `S1
107 // = P`.)
108 scc_placeholders: IndexVec<LeakCheckScc, Option<ty::PlaceholderRegion>>,
109
110 // For each SCC S, track the minimum universe that flows into it. Note that
111 // this is both the minimum of the universes for every region that is a
112 // member of the SCC, but also if you have `R1: R2`, then the universe of
113 // `R2` must be less than the universe of `R1` (i.e., `R1` flows `R2`). To
114 // see that, imagine that you have `P1: R` -- in that case, `R` must be
115 // either the placeholder `P1` or the empty region in that same universe.
116 //
117 // To detect errors, we look for an SCC S where the values in
118 // `scc_placeholders[S]` (if any) cannot be stored into `scc_universes[S]`.
119 scc_universes: IndexVec<LeakCheckScc, SccUniverse<'tcx>>,
120}
121
122impl<'a, 'tcx> LeakCheck<'a, 'tcx> {
123 fn new(
124 tcx: TyCtxt<'tcx>,
125 outer_universe: ty::UniverseIndex,
126 max_universe: ty::UniverseIndex,
127 mini_graph: MiniGraph<'tcx>,
128 rcc: RegionConstraintCollector<'a, 'tcx>,
129 ) -> Self {
130 let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
131 let num_sccs = mini_graph.sccs.num_sccs();
132 Self {
133 tcx,
134 outer_universe,
135 mini_graph,
136 rcc,
137 scc_placeholders: IndexVec::from_elem_n(None, num_sccs),
138 scc_universes: IndexVec::from_elem_n(dummy_scc_universe, num_sccs),
139 }
140 }
141
142 /// Compute what placeholders (if any) each SCC must be equal to.
143 /// Also compute the minimum universe of all the regions in each SCC.
144 fn assign_placeholder_values(&mut self) -> RelateResult<'tcx, ()> {
145 // First walk: find each placeholder that is from a newly created universe.
146 for (region, leak_check_node) in &self.mini_graph.nodes {
147 let scc = self.mini_graph.sccs.scc(*leak_check_node);
148
149 // Set the universe of each SCC to be the minimum of its constituent universes
150 let universe = self.rcc.universe(*region);
151 debug!(
152 "assign_placeholder_values: scc={:?} universe={:?} region={:?}",
153 scc, universe, region
154 );
155 self.scc_universes[scc].take_min(universe, *region);
156
157 // Detect those SCCs that directly contain a placeholder
158 if let ty::RePlaceholder(placeholder) = **region {
159 if self.outer_universe.cannot_name(placeholder.universe) {
160 // Update `scc_placeholders` to account for the fact that `P: S` must hold.
161 match self.scc_placeholders[scc] {
162 Some(p) => {
163 assert_ne!(p, placeholder);
164 return Err(self.placeholder_error(p, placeholder));
165 }
166 None => {
167 self.scc_placeholders[scc] = Some(placeholder);
168 }
169 }
170 }
171 }
172 }
173
174 Ok(())
175 }
176
177 /// For each SCC S, iterate over each successor S1 where `S: S1`:
178 ///
179 /// * Compute
180 /// Iterate over each SCC `S` and ensure that, for each `S1` where `S1: S`,
181 /// `universe(S) <= universe(S1)`. This executes after
182 /// `assign_placeholder_values`, so `universe(S)` is already the minimum
183 /// universe of any of its direct constituents.
184 fn propagate_scc_value(&mut self) -> RelateResult<'tcx, ()> {
185 // Loop invariants:
186 //
187 // On start of the loop iteration for `scc1`:
188 //
189 // * `scc_universes[scc1]` contains the minimum universe of the
190 // constituents of `scc1`
191 // * `scc_placeholder[scc1]` stores the placeholder that `scc1` must
192 // be equal to (if any)
193 //
194 // For each successor `scc2` where `scc1: scc2`:
195 //
196 // * `scc_placeholder[scc2]` stores some placeholder `P` where
197 // `scc2: P` (if any)
198 // * `scc_universes[scc2]` contains the minimum universe of the
199 // constituents of `scc2` and any of its successors
200 for scc1 in self.mini_graph.sccs.all_sccs() {
201 debug!(
202 "propagate_scc_value: scc={:?} with universe {:?}",
203 scc1, self.scc_universes[scc1]
204 );
205
206 // Walk over each `scc2` such that `scc1: scc2` and compute:
207 //
208 // * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1`
209 // * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there
210 // are multiple, we pick one arbitrarily)
211 let mut scc1_universe = self.scc_universes[scc1];
212 let mut succ_bound = None;
213 for &scc2 in self.mini_graph.sccs.successors(scc1) {
214 let SccUniverse { universe: scc2_universe, region: scc2_region } =
215 self.scc_universes[scc2];
216
217 scc1_universe.take_min(scc2_universe, scc2_region.unwrap());
218
219 if let Some(b) = self.scc_placeholders[scc2] {
220 succ_bound = Some(b);
221 }
222 }
223
224 // Update minimum universe of scc1.
225 self.scc_universes[scc1] = scc1_universe;
226
227 // At this point, `scc_placeholders[scc1]` stores the placeholder that
228 // `scc1` must be equal to, if any.
229 if let Some(scc1_placeholder) = self.scc_placeholders[scc1] {
230 debug!(
231 "propagate_scc_value: scc1={:?} placeholder={:?} scc1_universe={:?}",
232 scc1, scc1_placeholder, scc1_universe
233 );
234
235 // Check if `P1: R` for some `R` in a universe that cannot name
236 // P1. That's an error.
237 if scc1_universe.universe.cannot_name(scc1_placeholder.universe) {
238 return Err(self.error(scc1_placeholder, scc1_universe.region.unwrap()));
239 }
240
241 // Check if we have some placeholder where `S: P2`
242 // (transitively). In that case, since `S = P1`, that implies
243 // `P1: P2`, which is an error condition.
244 if let Some(scc2_placeholder) = succ_bound {
245 assert_ne!(scc1_placeholder, scc2_placeholder);
246 return Err(self.placeholder_error(scc1_placeholder, scc2_placeholder));
247 }
248 } else {
249 // Otherwise, we can reach a placeholder if some successor can.
250 self.scc_placeholders[scc1] = succ_bound;
251 }
252
253 // At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must
254 // outlive (if any).
255 }
256 Ok(())
257 }
258
259 fn placeholder_error(
260 &self,
261 placeholder1: ty::PlaceholderRegion,
262 placeholder2: ty::PlaceholderRegion,
263 ) -> TypeError<'tcx> {
264 self.error(placeholder1, ty::Region::new_placeholder(self.tcx, placeholder2))
265 }
266
267 fn error(
268 &self,
269 placeholder: ty::PlaceholderRegion,
270 other_region: ty::Region<'tcx>,
271 ) -> TypeError<'tcx> {
272 debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
273 TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound, other_region)
274 }
275}
276
277// States we need to distinguish:
278//
279// * must be equal to a placeholder (i.e., a placeholder is in the SCC)
280// * it could conflict with some other regions in the SCC in different universes
281// * or a different placeholder
282// * `P1: S` and `S` must be equal to a placeholder
283// * `P1: S` and `S` is in an incompatible universe
284//
285// So if we
286//
287// (a) compute which placeholder (if any) each SCC must be equal to
288// (b) compute its minimum universe
289// (c) compute *some* placeholder where `S: P1` (any one will do)
290//
291// then we get an error if:
292//
293// - it must be equal to a placeholder `P1` and minimum universe cannot name `P1`
294// - `S: P1` and minimum universe cannot name `P1`
295// - `S: P1` and we must be equal to `P2`
296//
297// So we want to track:
298//
299// * Equal placeholder (if any)
300// * Some bounding placeholder (if any)
301// * Minimum universe
302//
303// * We compute equal placeholder + minimum universe of constituents in first pass
304// * Then we walk in order and compute from our dependencies `S1` where `S: S1` (`S -> S1`)
305// * bounding placeholder (if any)
306// * minimum universe
307// * And if we must be equal to a placeholder then we check it against
308// * minimum universe
309// * no bounding placeholder
310
311/// Tracks the "minimum universe" for each SCC, along with some region that
312/// caused it to change.
313#[derive(Copy, Clone, Debug)]
314struct SccUniverse<'tcx> {
315 /// For some SCC S, the minimum universe of:
316 ///
317 /// * each region R in S
318 /// * each SCC S1 such that S: S1
319 universe: ty::UniverseIndex,
320
321 /// Some region that caused `universe` to be what it is.
322 region: Option<ty::Region<'tcx>>,
323}
324
325impl<'tcx> SccUniverse<'tcx> {
326 /// If `universe` is less than our current universe, then update
327 /// `self.universe` and `self.region`.
328 fn take_min(&mut self, universe: ty::UniverseIndex, region: ty::Region<'tcx>) {
329 if universe < self.universe || self.region.is_none() {
330 self.universe = universe;
331 self.region = Some(region);
332 }
333 }
334}
335
336rustc_index::newtype_index! {
337 #[orderable]
338 #[debug_format = "LeakCheckNode({})"]
339 struct LeakCheckNode {}
340}
341
342rustc_index::newtype_index! {
343 #[orderable]
344 #[debug_format = "LeakCheckScc({})"]
345 struct LeakCheckScc {}
346}
347
348/// Represents the graph of constraints. For each `R1: R2` constraint we create
349/// an edge `R1 -> R2` in the graph.
350struct MiniGraph<'tcx> {
351 /// Map from a region to the index of the node in the graph.
352 nodes: FxIndexMap<ty::Region<'tcx>, LeakCheckNode>,
353
354 /// Map from node index to SCC, and stores the successors of each SCC. All
355 /// the regions in the same SCC are equal to one another, and if `S1 -> S2`,
356 /// then `S1: S2`.
357 sccs: Sccs<LeakCheckNode, LeakCheckScc>,
358}
359
360impl<'tcx> MiniGraph<'tcx> {
361 fn new(
362 tcx: TyCtxt<'tcx>,
363 region_constraints: &RegionConstraintCollector<'_, 'tcx>,
364 only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
365 ) -> Self {
366 let mut nodes = FxIndexMap::default();
367 let mut edges = Vec::new();
368
369 // Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
370 Self::iterate_region_constraints(
371 tcx,
372 region_constraints,
373 only_consider_snapshot,
374 |target, source| {
375 let source_node = Self::add_node(&mut nodes, source);
376 let target_node = Self::add_node(&mut nodes, target);
377 edges.push((source_node, target_node));
378 },
379 );
380 let graph = VecGraph::<_, false>::new(nodes.len(), edges);
381 let sccs = Sccs::new(&graph);
382 Self { nodes, sccs }
383 }
384
385 /// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
386 fn iterate_region_constraints(
387 tcx: TyCtxt<'tcx>,
388 region_constraints: &RegionConstraintCollector<'_, 'tcx>,
389 only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
390 mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
391 ) {
392 let mut each_constraint = |constraint| match constraint {
393 &Constraint::VarSubVar(a, b) => {
394 each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
395 }
396 &Constraint::RegSubVar(a, b) => {
397 each_edge(a, ty::Region::new_var(tcx, b));
398 }
399 &Constraint::VarSubReg(a, b) => {
400 each_edge(ty::Region::new_var(tcx, a), b);
401 }
402 &Constraint::RegSubReg(a, b) => {
403 each_edge(a, b);
404 }
405 };
406
407 if let Some(snapshot) = only_consider_snapshot {
408 for undo_entry in
409 region_constraints.undo_log.region_constraints_in_snapshot(&snapshot.undo_snapshot)
410 {
411 match undo_entry {
412 &AddConstraint(i) => {
413 each_constraint(®ion_constraints.data().constraints[i].0);
414 }
415 &AddVerify(i) => span_bug!(
416 region_constraints.data().verifys[i].origin.span(),
417 "we never add verifications while doing higher-ranked things",
418 ),
419 &AddCombination(..) | &AddVar(..) => {}
420 }
421 }
422 } else {
423 region_constraints
424 .data()
425 .constraints
426 .iter()
427 .for_each(|(constraint, _)| each_constraint(constraint));
428 }
429 }
430
431 fn add_node(
432 nodes: &mut FxIndexMap<ty::Region<'tcx>, LeakCheckNode>,
433 r: ty::Region<'tcx>,
434 ) -> LeakCheckNode {
435 let l = nodes.len();
436 *nodes.entry(r).or_insert(LeakCheckNode::new(l))
437 }
438}