rustc_borrowck/constraints/mod.rs
1use std::fmt;
2use std::ops::Index;
3
4use rustc_index::{IndexSlice, IndexVec};
5use rustc_middle::mir::ConstraintCategory;
6use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
7use rustc_span::Span;
8use tracing::{debug, instrument};
9
10use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker};
11use crate::type_check::Locations;
12use crate::universal_regions::UniversalRegions;
13
14pub(crate) mod graph;
15
16/// A set of NLL region constraints. These include "outlives"
17/// constraints of the form `R1: R2`. Each constraint is identified by
18/// a unique `OutlivesConstraintIndex` and you can index into the set
19/// (`constraint_set[i]`) to access the constraint details.
20#[derive(Clone, Debug, Default)]
21pub(crate) struct OutlivesConstraintSet<'tcx> {
22 outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>>,
23}
24
25impl<'tcx> OutlivesConstraintSet<'tcx> {
26 pub(crate) fn push(&mut self, constraint: OutlivesConstraint<'tcx>) {
27 debug!("OutlivesConstraintSet::push({:?})", constraint);
28 if constraint.sup == constraint.sub {
29 // 'a: 'a is pretty uninteresting
30 return;
31 }
32 self.outlives.push(constraint);
33 }
34
35 /// Constructs a "normal" graph from the constraint set; the graph makes it
36 /// easy to find the constraints affecting a particular region.
37 ///
38 /// N.B., this graph contains a "frozen" view of the current
39 /// constraints. Any new constraints added to the `OutlivesConstraintSet`
40 /// after the graph is built will not be present in the graph.
41 pub(crate) fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph {
42 graph::ConstraintGraph::new(graph::Normal, self, num_region_vars)
43 }
44
45 /// Like `graph`, but constraints a reverse graph where `R1: R2`
46 /// represents an edge `R2 -> R1`.
47 pub(crate) fn reverse_graph(&self, num_region_vars: usize) -> graph::ReverseConstraintGraph {
48 graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
49 }
50
51 pub(crate) fn outlives(
52 &self,
53 ) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
54 &self.outlives
55 }
56
57 /// Computes cycles (SCCs) in the graph of regions. In particular,
58 /// find all regions R1, R2 such that R1: R2 and R2: R1 and group
59 /// them into an SCC, and find the relationships between SCCs.
60 pub(crate) fn compute_sccs(
61 &self,
62 static_region: RegionVid,
63 definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
64 ) -> ConstraintSccs {
65 let constraint_graph = self.graph(definitions.len());
66 let region_graph = &constraint_graph.region_graph(self, static_region);
67 ConstraintSccs::new_with_annotation(®ion_graph, |r| {
68 RegionTracker::new(r, &definitions[r])
69 })
70 }
71
72 /// This method handles Universe errors by rewriting the constraint
73 /// graph. For each strongly connected component in the constraint
74 /// graph such that there is a series of constraints
75 /// A: B: C: ... : X where
76 /// A's universe is smaller than X's and A is a placeholder,
77 /// add a constraint that A: 'static. This is a safe upper bound
78 /// in the face of borrow checker/trait solver limitations that will
79 /// eventually go away.
80 ///
81 /// For a more precise definition, see the documentation for
82 /// [`RegionTracker::has_incompatible_universes()`].
83 ///
84 /// This edge case used to be handled during constraint propagation
85 /// by iterating over the strongly connected components in the constraint
86 /// graph while maintaining a set of bookkeeping mappings similar
87 /// to what is stored in `RegionTracker` and manually adding 'sttaic as
88 /// needed.
89 ///
90 /// It was rewritten as part of the Polonius project with the goal of moving
91 /// higher-kindedness concerns out of the path of the borrow checker,
92 /// for two reasons:
93 ///
94 /// 1. Implementing Polonius is difficult enough without also
95 /// handling them.
96 /// 2. The long-term goal is to handle higher-kinded concerns
97 /// in the trait solver, where they belong. This avoids
98 /// logic duplication and allows future trait solvers
99 /// to compute better bounds than for example our
100 /// "must outlive 'static" here.
101 ///
102 /// This code is a stop-gap measure in preparation for the future trait solver.
103 ///
104 /// Every constraint added by this method is an
105 /// internal `IllegalUniverse` constraint.
106 #[instrument(skip(self, universal_regions, definitions))]
107 pub(crate) fn add_outlives_static(
108 &mut self,
109 universal_regions: &UniversalRegions<'tcx>,
110 definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
111 ) -> ConstraintSccs {
112 let fr_static = universal_regions.fr_static;
113 let sccs = self.compute_sccs(fr_static, definitions);
114
115 // Changed to `true` if we added any constraints to `self` and need to
116 // recompute SCCs.
117 let mut added_constraints = false;
118
119 for scc in sccs.all_sccs() {
120 // No point in adding 'static: 'static!
121 // This micro-optimisation makes somewhat sense
122 // because static outlives *everything*.
123 if scc == sccs.scc(fr_static) {
124 continue;
125 }
126
127 let annotation = sccs.annotation(scc);
128
129 // If this SCC participates in a universe violation,
130 // e.g. if it reaches a region with a universe smaller than
131 // the largest region reached, add a requirement that it must
132 // outlive `'static`.
133 if annotation.has_incompatible_universes() {
134 // Optimisation opportunity: this will add more constraints than
135 // needed for correctness, since an SCC upstream of another with
136 // a universe violation will "infect" its downstream SCCs to also
137 // outlive static.
138 added_constraints = true;
139 let scc_representative_outlives_static = OutlivesConstraint {
140 sup: annotation.representative,
141 sub: fr_static,
142 category: ConstraintCategory::IllegalUniverse,
143 locations: Locations::All(rustc_span::DUMMY_SP),
144 span: rustc_span::DUMMY_SP,
145 variance_info: VarianceDiagInfo::None,
146 from_closure: false,
147 };
148 self.push(scc_representative_outlives_static);
149 }
150 }
151
152 if added_constraints {
153 // We changed the constraint set and so must recompute SCCs.
154 self.compute_sccs(fr_static, definitions)
155 } else {
156 // If we didn't add any back-edges; no more work needs doing
157 sccs
158 }
159 }
160}
161
162impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
163 type Output = OutlivesConstraint<'tcx>;
164
165 fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output {
166 &self.outlives[i]
167 }
168}
169
170#[derive(Copy, Clone, PartialEq, Eq)]
171pub struct OutlivesConstraint<'tcx> {
172 // NB. The ordering here is not significant for correctness, but
173 // it is for convenience. Before we dump the constraints in the
174 // debugging logs, we sort them, and we'd like the "super region"
175 // to be first, etc. (In particular, span should remain last.)
176 /// The region SUP must outlive SUB...
177 pub sup: RegionVid,
178
179 /// Region that must be outlived.
180 pub sub: RegionVid,
181
182 /// Where did this constraint arise?
183 pub locations: Locations,
184
185 /// The `Span` associated with the creation of this constraint.
186 /// This should be used in preference to obtaining the span from
187 /// `locations`, since the `locations` may give a poor span
188 /// in some cases (e.g. converting a constraint from a promoted).
189 pub span: Span,
190
191 /// What caused this constraint?
192 pub category: ConstraintCategory<'tcx>,
193
194 /// Variance diagnostic information
195 pub variance_info: VarianceDiagInfo<TyCtxt<'tcx>>,
196
197 /// If this constraint is promoted from closure requirements.
198 pub from_closure: bool,
199}
200
201impl<'tcx> fmt::Debug for OutlivesConstraint<'tcx> {
202 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(
204 formatter,
205 "({:?}: {:?}) due to {:?} ({:?}) ({:?})",
206 self.sup, self.sub, self.locations, self.variance_info, self.category,
207 )
208 }
209}
210
211rustc_index::newtype_index! {
212 #[debug_format = "OutlivesConstraintIndex({})"]
213 pub(crate) struct OutlivesConstraintIndex {}
214}
215
216rustc_index::newtype_index! {
217 #[orderable]
218 #[debug_format = "ConstraintSccIndex({})"]
219 pub struct ConstraintSccIndex {}
220}