|
1 | 1 | use std::fmt;
|
2 | 2 | use std::ops::Index;
|
3 | 3 |
|
| 4 | +use rustc_data_structures::fx::FxHashSet; |
| 5 | +use rustc_data_structures::graph::scc; |
4 | 6 | use rustc_index::{IndexSlice, IndexVec};
|
5 | 7 | use rustc_infer::infer::NllRegionVariableOrigin;
|
6 | 8 | use rustc_middle::mir::ConstraintCategory;
|
7 | 9 | use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
|
8 | 10 | use rustc_span::Span;
|
9 | 11 | use tracing::{debug, instrument};
|
10 | 12 |
|
11 |
| -use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker}; |
| 13 | +use crate::region_infer::{RegionDefinition, RegionTracker, SccAnnotations}; |
12 | 14 | use crate::type_check::Locations;
|
13 | 15 | use crate::universal_regions::UniversalRegions;
|
14 | 16 |
|
@@ -58,16 +60,18 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
|
58 | 60 | /// Computes cycles (SCCs) in the graph of regions. In particular,
|
59 | 61 | /// find all regions R1, R2 such that R1: R2 and R2: R1 and group
|
60 | 62 | /// them into an SCC, and find the relationships between SCCs.
|
61 |
| - pub(crate) fn compute_sccs( |
| 63 | + pub(crate) fn compute_sccs< |
| 64 | + A: scc::Annotation, |
| 65 | + AA: scc::Annotations<RegionVid, ConstraintSccIndex, A>, |
| 66 | + >( |
62 | 67 | &self,
|
63 | 68 | static_region: RegionVid,
|
64 |
| - definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>, |
65 |
| - ) -> ConstraintSccs { |
66 |
| - let constraint_graph = self.graph(definitions.len()); |
| 69 | + num_region_vars: usize, |
| 70 | + annotations: &mut AA, |
| 71 | + ) -> scc::Sccs<RegionVid, ConstraintSccIndex> { |
| 72 | + let constraint_graph = self.graph(num_region_vars); |
67 | 73 | let region_graph = &constraint_graph.region_graph(self, static_region);
|
68 |
| - ConstraintSccs::new_with_annotation(®ion_graph, |r| { |
69 |
| - RegionTracker::new(r, &definitions[r]) |
70 |
| - }) |
| 74 | + scc::Sccs::new_with_annotation(®ion_graph, annotations) |
71 | 75 | }
|
72 | 76 |
|
73 | 77 | /// There is a placeholder violation; add a requirement
|
@@ -126,88 +130,91 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
|
126 | 130 | /// Every constraint added by this method is an
|
127 | 131 | /// internal `IllegalUniverse` constraint.
|
128 | 132 | #[instrument(skip(self, universal_regions, definitions))]
|
129 |
| - pub(crate) fn add_outlives_static( |
| 133 | + pub(crate) fn add_outlives_static<'d>( |
130 | 134 | &mut self,
|
131 | 135 | universal_regions: &UniversalRegions<'tcx>,
|
132 |
| - definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>, |
133 |
| - ) -> ConstraintSccs { |
| 136 | + definitions: &'d IndexVec<RegionVid, RegionDefinition<'tcx>>, |
| 137 | + ) -> (scc::Sccs<RegionVid, ConstraintSccIndex>, IndexVec<ConstraintSccIndex, RegionTracker>) |
| 138 | + { |
134 | 139 | let fr_static = universal_regions.fr_static;
|
135 |
| - let sccs = self.compute_sccs(fr_static, definitions); |
| 140 | + let mut annotations = SccAnnotations::init(definitions); |
| 141 | + let sccs = self.compute_sccs(fr_static, definitions.len(), &mut annotations); |
136 | 142 |
|
137 |
| - // Changed to `true` if we added any constraints to `self` and need to |
138 |
| - // recompute SCCs. |
139 |
| - let mut added_constraints = false; |
| 143 | + // Is this SCC already outliving static directly or transitively? |
| 144 | + let mut outlives_static = FxHashSet::default(); |
140 | 145 |
|
141 | 146 | for scc in sccs.all_sccs() {
|
142 |
| - let annotation = sccs.annotation(scc); |
| 147 | + let annotation: RegionTracker = annotations.scc_to_annotation[scc]; |
| 148 | + if scc == sccs.scc(fr_static) { |
| 149 | + // No use adding 'static: 'static. |
| 150 | + continue; |
| 151 | + } |
143 | 152 |
|
144 | 153 | // If this SCC participates in a universe violation
|
145 | 154 | // e.g. if it reaches a region with a universe smaller than
|
146 | 155 | // the largest region reached, add a requirement that it must
|
147 | 156 | // outlive `'static`. Here we get to know which reachable region
|
148 | 157 | // caused the violation.
|
149 | 158 | if let Some(to) = annotation.universe_violation() {
|
150 |
| - // Optimisation opportunity: this will potentially add more constraints |
151 |
| - // than needed for correctness, since an SCC upstream of another with |
152 |
| - // a universe violation will "infect" its downstream SCCs to also |
153 |
| - // outlive static. However, some of those may be useful for error |
154 |
| - // reporting. |
155 |
| - added_constraints = true; |
| 159 | + outlives_static.insert(scc); |
156 | 160 | self.add_placeholder_violation_constraint(
|
157 |
| - annotation.representative, |
158 |
| - annotation.representative, |
| 161 | + annotation.representative_rvid(), |
| 162 | + annotation.representative_rvid(), |
159 | 163 | to,
|
160 | 164 | fr_static,
|
161 | 165 | );
|
162 | 166 | }
|
163 | 167 | }
|
164 | 168 |
|
165 |
| - // The second kind of violation: a placeholder reaching another placeholder. |
166 |
| - // OPTIMIZATION: This one is even more optimisable since it adds constraints for every |
167 |
| - // placeholder in an SCC. |
168 |
| - for rvid in definitions.iter_enumerated().filter_map(|(rvid, definition)| { |
169 |
| - if matches!(definition.origin, NllRegionVariableOrigin::Placeholder { .. }) { |
170 |
| - Some(rvid) |
171 |
| - } else { |
172 |
| - None |
173 |
| - } |
174 |
| - }) { |
175 |
| - let scc = sccs.scc(rvid); |
176 |
| - let annotation = sccs.annotation(scc); |
| 169 | + // Note: it's possible to sort this iterator by SCC and get dependency order, |
| 170 | + // which makes it easy to only add only one constraint per future cycle. |
| 171 | + // However, this worsens diagnostics and requires iterating over |
| 172 | + // all successors to determine if we outlive static transitively, |
| 173 | + // a cost you pay even if you have no placeholders. |
| 174 | + let placeholders_and_sccs = |
| 175 | + definitions.iter_enumerated().filter_map(|(rvid, definition)| { |
| 176 | + if matches!(definition.origin, NllRegionVariableOrigin::Placeholder { .. }) { |
| 177 | + Some((sccs.scc(rvid), rvid)) |
| 178 | + } else { |
| 179 | + None |
| 180 | + } |
| 181 | + }); |
177 | 182 |
|
178 |
| - // Unwrap safety: since this is our SCC it must contain us, which is |
179 |
| - // at worst min AND max, but it has at least one or there is a bug. |
180 |
| - let min = annotation.min_reachable_placeholder.unwrap(); |
181 |
| - let max = annotation.max_reachable_placeholder.unwrap(); |
| 183 | + // The second kind of violation: a placeholder reaching another placeholder. |
| 184 | + for (scc, rvid) in placeholders_and_sccs { |
| 185 | + let annotation = annotations.scc_to_annotation[scc]; |
182 | 186 |
|
183 |
| - // Good path: Nothing to see here, at least no other placeholders! |
184 |
| - if min == max { |
| 187 | + if sccs.scc(fr_static) == scc || outlives_static.contains(&scc) { |
| 188 | + debug!("{:?} already outlives (or is) static", annotation.representative_rvid()); |
185 | 189 | continue;
|
186 | 190 | }
|
187 | 191 |
|
188 |
| - // Bad path: figure out who we illegally reached. |
189 |
| - // Note that this will prefer the representative if it is a |
190 |
| - // placeholder, since the representative has the smallest index! |
191 |
| - let other_placeholder = if min != rvid { min } else { max }; |
192 |
| - |
193 |
| - debug!( |
194 |
| - "Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}" |
195 |
| - ); |
196 |
| - added_constraints = true; |
197 |
| - self.add_placeholder_violation_constraint( |
198 |
| - annotation.representative, |
199 |
| - rvid, |
200 |
| - other_placeholder, |
201 |
| - fr_static, |
202 |
| - ); |
| 192 | + if let Some(other_placeholder) = annotation.reaches_other_placeholder(rvid) { |
| 193 | + debug!( |
| 194 | + "Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}" |
| 195 | + ); |
| 196 | + outlives_static.insert(scc); |
| 197 | + self.add_placeholder_violation_constraint( |
| 198 | + annotation.representative_rvid(), |
| 199 | + rvid, |
| 200 | + other_placeholder, |
| 201 | + fr_static, |
| 202 | + ); |
| 203 | + }; |
203 | 204 | }
|
204 | 205 |
|
205 |
| - if added_constraints { |
| 206 | + if !outlives_static.is_empty() { |
| 207 | + debug!("The following SCCs had :'static constraints added: {:?}", outlives_static); |
| 208 | + let mut annotations = SccAnnotations::init(definitions); |
| 209 | + |
206 | 210 | // We changed the constraint set and so must recompute SCCs.
|
207 |
| - self.compute_sccs(fr_static, definitions) |
| 211 | + ( |
| 212 | + self.compute_sccs(fr_static, definitions.len(), &mut annotations), |
| 213 | + annotations.scc_to_annotation, |
| 214 | + ) |
208 | 215 | } else {
|
209 | 216 | // If we didn't add any back-edges; no more work needs doing
|
210 |
| - sccs |
| 217 | + (sccs, annotations.scc_to_annotation) |
211 | 218 | }
|
212 | 219 | }
|
213 | 220 | }
|
|
0 commit comments