|
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