Skip to content

Commit e489ff2

Browse files
committed
Produce slightly fewer placeholder constraints
This seems to hit the sweet spot between enabling debugging and being efficient.
1 parent 4633c56 commit e489ff2

File tree

3 files changed

+38
-28
lines changed
  • compiler
    • rustc_borrowck/src
    • rustc_data_structures/src/graph/scc

3 files changed

+38
-28
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

+32-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt;
22
use std::ops::Index;
33

4+
use rustc_data_structures::fx::FxHashSet;
45
use rustc_index::{IndexSlice, IndexVec};
56
use rustc_infer::infer::NllRegionVariableOrigin;
67
use rustc_middle::mir::ConstraintCategory;
@@ -134,25 +135,22 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
134135
let fr_static = universal_regions.fr_static;
135136
let sccs = self.compute_sccs(fr_static, definitions);
136137

137-
// Changed to `true` if we added any constraints to `self` and need to
138-
// recompute SCCs.
139-
let mut added_constraints = false;
138+
// Is this SCC already outliving static directly or transitively?
139+
let mut outlives_static = FxHashSet::default();
140140

141-
for scc in sccs.all_sccs() {
142-
let annotation = sccs.annotation(scc);
141+
for (scc, annotation) in sccs.all_annotations() {
142+
if scc == sccs.scc(fr_static) {
143+
// No use adding 'static: 'static.
144+
continue;
145+
}
143146

144147
// If this SCC participates in a universe violation
145148
// e.g. if it reaches a region with a universe smaller than
146149
// the largest region reached, add a requirement that it must
147150
// outlive `'static`. Here we get to know which reachable region
148151
// caused the violation.
149152
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;
153+
outlives_static.insert(scc);
156154
self.add_placeholder_violation_constraint(
157155
annotation.representative,
158156
annotation.representative,
@@ -162,19 +160,29 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
162160
}
163161
}
164162

163+
// Note: it's possible to sort this iterator by SCC and get dependency order,
164+
// which makes it easy to only add only one constraint per future cycle.
165+
// However, this worsens diagnostics and requires iterating over
166+
// all successors to determine if we outlive static transitively,
167+
// a cost you pay even if you have no placeholders.
168+
let placeholders_and_sccs =
169+
definitions.iter_enumerated().filter_map(|(rvid, definition)| {
170+
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder { .. }) {
171+
Some((sccs.scc(rvid), rvid))
172+
} else {
173+
None
174+
}
175+
});
176+
165177
// 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);
178+
for (scc, rvid) in placeholders_and_sccs {
176179
let annotation = sccs.annotation(scc);
177180

181+
if sccs.scc(fr_static) == scc || outlives_static.contains(&scc) {
182+
debug!("{:?} already outlives (or is) static", annotation.representative);
183+
continue;
184+
}
185+
178186
// Unwrap safety: since this is our SCC it must contain us, which is
179187
// at worst min AND max, but it has at least one or there is a bug.
180188
let min = annotation.min_reachable_placeholder.unwrap();
@@ -193,7 +201,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
193201
debug!(
194202
"Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}"
195203
);
196-
added_constraints = true;
204+
outlives_static.insert(scc);
197205
self.add_placeholder_violation_constraint(
198206
annotation.representative,
199207
rvid,
@@ -202,7 +210,8 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
202210
);
203211
}
204212

205-
if added_constraints {
213+
if !outlives_static.is_empty() {
214+
debug!("The following SCCs had :'static constraints added: {:?}", outlives_static);
206215
// We changed the constraint set and so must recompute SCCs.
207216
self.compute_sccs(fr_static, definitions)
208217
} else {

compiler/rustc_borrowck/src/region_infer/mod.rs

-5
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,6 @@ pub struct RegionTracker {
9191

9292
/// The largest reachable placeholder from this SCC (including in it).
9393
pub(crate) max_reachable_placeholder: Option<RegionVid>,
94-
95-
/// Is there at least one placeholder in this SCC?
96-
contains_placeholder: bool,
9794
}
9895

9996
impl scc::Annotation for RegionTracker {
@@ -110,7 +107,6 @@ impl scc::Annotation for RegionTracker {
110107
_ if self.representative <= other.representative => (self, other),
111108
_ => (other, self),
112109
};
113-
shorter.contains_placeholder |= longer.contains_placeholder;
114110
shorter.merge_min_max_seen(&longer);
115111
shorter
116112
}
@@ -148,7 +144,6 @@ impl RegionTracker {
148144
representative_origin,
149145
min_reachable_placeholder: representative_if_placeholder,
150146
max_reachable_placeholder: representative_if_placeholder,
151-
contains_placeholder: rvid_is_placeholder,
152147
}
153148
}
154149

compiler/rustc_data_structures/src/graph/scc/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
137137
(0..self.scc_data.len()).map(S::new)
138138
}
139139

140+
/// Returns an iterator over the SCC annotations in the graph
141+
/// The order is the same as `all_sccs()`, dependency order.
142+
pub fn all_annotations(&self) -> impl Iterator<Item = (S, A)> + use<'_, N, S, A> {
143+
self.all_sccs().map(|scc| (scc, self.annotation(scc)))
144+
}
145+
140146
/// Returns the SCC to which a node `r` belongs.
141147
pub fn scc(&self, r: N) -> S {
142148
self.scc_indices[r]

0 commit comments

Comments
 (0)