Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 49781db

Browse files
committed
This version mostly works!
I've switched to encoding our new outlives-static constraints with two extra parameters: the source and drain of a path in the original constraint graph that caused a placeholder issue. This handles all of the soundness issues, leaving 16 failing diagnostics.
1 parent 16b6040 commit 49781db

File tree

5 files changed

+120
-106
lines changed

5 files changed

+120
-106
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::fmt;
22
use std::ops::Index;
33

44
use rustc_index::{IndexSlice, IndexVec};
5+
use rustc_infer::infer::NllRegionVariableOrigin;
56
use rustc_middle::mir::ConstraintCategory;
67
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
78
use rustc_span::Span;
@@ -69,6 +70,27 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
6970
})
7071
}
7172

73+
/// There is a placeholder violation; add a requirement
74+
/// that some SCC outlive static and explain which region
75+
/// reaching which other region caused that.
76+
fn add_placeholder_violation_constraint(
77+
&mut self,
78+
outlives_static: RegionVid,
79+
blame_from: RegionVid,
80+
blame_to: RegionVid,
81+
fr_static: RegionVid,
82+
) {
83+
self.push(OutlivesConstraint {
84+
sup: outlives_static,
85+
sub: fr_static,
86+
category: ConstraintCategory::IllegalPlaceholder(blame_from, blame_to),
87+
locations: Locations::All(rustc_span::DUMMY_SP),
88+
span: rustc_span::DUMMY_SP,
89+
variance_info: VarianceDiagInfo::None,
90+
from_closure: false,
91+
});
92+
}
93+
7294
/// This method handles Universe errors by rewriting the constraint
7395
/// graph. For each strongly connected component in the constraint
7496
/// graph such that there is a series of constraints
@@ -117,41 +139,67 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
117139
let mut added_constraints = false;
118140

119141
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-
127142
let annotation = sccs.annotation(scc);
128143

129144
// If this SCC participates in a universe violation
130145
// e.g. if it reaches a region with a universe smaller than
131-
// the largest region reached, or if this placeholder
132-
// reaches another placeholder, add a requirement that it must
133-
// outlive `'static`.
134-
if let Some(offending_region) = annotation.placeholder_violation(&sccs) {
135-
assert!(
136-
annotation.representative != offending_region,
137-
"Attemtping to blame a constraint for itself!"
138-
);
139-
// Optimisation opportunity: this will add more constraints than
140-
// needed for correctness, since an SCC upstream of another with
146+
// the largest region reached, add a requirement that it must
147+
// outlive `'static`. Here we get to know which reachable region
148+
// caused the violation.
149+
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
141152
// a universe violation will "infect" its downstream SCCs to also
142-
// outlive static.
153+
// outlive static. However, some of those may be useful for error
154+
// reporting.
143155
added_constraints = true;
144-
let scc_representative_outlives_static = OutlivesConstraint {
145-
sup: annotation.representative,
146-
sub: fr_static,
147-
category: ConstraintCategory::IllegalPlaceholder(offending_region),
148-
locations: Locations::All(rustc_span::DUMMY_SP),
149-
span: rustc_span::DUMMY_SP,
150-
variance_info: VarianceDiagInfo::None,
151-
from_closure: false,
152-
};
153-
self.push(scc_representative_outlives_static);
156+
self.add_placeholder_violation_constraint(
157+
annotation.representative,
158+
annotation.representative,
159+
to,
160+
fr_static,
161+
);
162+
}
163+
}
164+
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
154173
}
174+
}) {
175+
let scc = sccs.scc(rvid);
176+
let annotation = sccs.annotation(scc);
177+
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();
182+
183+
// Good path: Nothing to see here, at least no other placeholders!
184+
if min == max {
185+
continue;
186+
}
187+
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+
);
155203
}
156204

157205
if added_constraints {

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
6161
| ConstraintCategory::Boring
6262
| ConstraintCategory::BoringNoLocation
6363
| ConstraintCategory::Internal
64-
| ConstraintCategory::IllegalPlaceholder(_) => "",
64+
| ConstraintCategory::IllegalPlaceholder(..) => "",
6565
}
6666
}
6767
}

compiler/rustc_borrowck/src/region_infer/graphviz.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use rustc_middle::ty::UniverseIndex;
1212
use super::*;
1313

1414
fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String {
15-
if let ConstraintCategory::IllegalPlaceholder(p) = constraint.category {
16-
return format!("b/c {p:?}");
15+
if let ConstraintCategory::IllegalPlaceholder(from, to) = constraint.category {
16+
return format!("b/c {from:?}: {to:?}");
1717
}
1818
match constraint.locations {
1919
Locations::All(_) => "All(...)".to_string(),

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 38 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub struct RegionTracker {
7676
max_universe_placeholder_reached: ReachablePlaceholder,
7777

7878
/// The smallest universe index reachable form the nodes of this SCC.
79-
min_reachable_universe: UniverseIndex,
79+
min_reachable_universe: (UniverseIndex, RegionVid),
8080

8181
/// The representative Region Variable Id for this SCC. We prefer
8282
/// placeholders over existentially quantified variables, otherwise
@@ -87,10 +87,10 @@ pub struct RegionTracker {
8787
representative_origin: RepresentativeOrigin,
8888

8989
/// The smallest reachable placeholder from this SCC (including in it).
90-
min_reachable_placeholder: Option<RegionVid>,
90+
pub(crate) min_reachable_placeholder: Option<RegionVid>,
9191

9292
/// The largest reachable placeholder from this SCC (including in it).
93-
max_reachable_placeholder: Option<RegionVid>,
93+
pub(crate) max_reachable_placeholder: Option<RegionVid>,
9494

9595
/// Is there at least one placeholder in this SCC?
9696
contains_placeholder: bool,
@@ -143,7 +143,7 @@ impl RegionTracker {
143143

144144
Self {
145145
max_universe_placeholder_reached,
146-
min_reachable_universe: definition.universe,
146+
min_reachable_universe: (definition.universe, rvid),
147147
representative: rvid,
148148
representative_origin,
149149
min_reachable_placeholder: representative_if_placeholder,
@@ -152,24 +152,6 @@ impl RegionTracker {
152152
}
153153
}
154154

155-
/// Return true if this SCC contains a placeholder that
156-
/// reaches another placeholder, through other SCCs or within
157-
/// it.
158-
fn placeholder_reaches_placeholder(&self) -> bool {
159-
// If min and max are different then at least two placeholders
160-
// must be reachable from us. It remains to determine if and
161-
// whose problem that is.
162-
//
163-
// If we are not a placeholder
164-
// we are seeing upstream placeholders, which may be fine, or
165-
// if it is a problem it's the problem for other placeholders.
166-
//
167-
// If we *are* a placeholder, we are reaching at least one other
168-
// placeholder upstream.
169-
self.contains_placeholder
170-
&& self.min_reachable_placeholder != self.max_reachable_placeholder
171-
}
172-
173155
/// If the representative is a placeholder, return it,
174156
/// otherwise return None.
175157
fn placeholder_representative(&self) -> Option<RegionVid> {
@@ -182,7 +164,7 @@ impl RegionTracker {
182164

183165
/// The smallest-indexed universe reachable from and/or in this SCC.
184166
fn min_universe(self) -> UniverseIndex {
185-
self.min_reachable_universe
167+
self.min_reachable_universe.0
186168
}
187169

188170
fn merge_reachable_placeholders(&mut self, other: &Self) {
@@ -216,58 +198,37 @@ impl RegionTracker {
216198
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
217199
}
218200

219-
/// Returns an offending region if the annotated SCC reaches a placeholder
220-
/// with a universe larger than the smallest reachable one,
221-
/// or if a placeholder reaches another placeholder, `None` otherwise.
222-
pub(crate) fn placeholder_violation(
223-
&self,
224-
sccs: &Sccs<RegionVid, ConstraintSccIndex, Self>,
225-
) -> Option<RegionVid> {
226-
// Note: we arbitrarily prefer universe violations
227-
// to placeholder-reaches-placeholder violations.
228-
// violations.
229-
230-
// Case 1: a universe violation
231-
if let ReachablePlaceholder::Placeholder {
201+
/// Figure out if there is a universe violation going on.
202+
/// This can happen in two cases: either one of our placeholders
203+
/// had its universe lowered from reaching a region with a lower universe,
204+
/// (in which case we blame the lower universe's region), or because we reached
205+
/// a larger universe (in which case we blame the larger universe's region).
206+
pub(crate) fn universe_violation(&self) -> Option<RegionVid> {
207+
let ReachablePlaceholder::Placeholder {
232208
universe: max_reached_universe,
233-
rvid: belonging_to_rvid,
209+
rvid: large_u_rvid,
234210
} = self.max_universe_placeholder_reached
235-
{
236-
if self.min_universe().cannot_name(max_reached_universe) {
237-
return Some(belonging_to_rvid);
238-
}
239-
}
240-
241-
// Case 2: a placeholder (in our SCC) reaches another placeholder
242-
if self.placeholder_reaches_placeholder() {
243-
// We know that this SCC contains at least one placeholder
244-
// and that at least two placeholders are reachable from
245-
// this SCC.
246-
//
247-
// We try to pick one that isn't in our SCC, if possible.
248-
// We *always* pick one that is not equal to the representative.
249-
250-
// Unwrap safety: we know both these values are Some, since
251-
// there are two reachable placeholders at least.
252-
let min_reachable = self.min_reachable_placeholder.unwrap();
211+
else {
212+
return None;
213+
};
253214

254-
if sccs.scc(min_reachable) != sccs.scc(self.representative) {
255-
return Some(min_reachable);
256-
}
215+
if !self.min_universe().cannot_name(max_reached_universe) {
216+
return None;
217+
};
257218

258-
// Either the largest reachable placeholder is outside our SCC,
259-
// or we *must* blame a placeholder in our SCC since the violation
260-
// happens inside of it. It's slightly easier to always arbitrarily
261-
// pick the largest one, so we do. This also nicely guarantees that
262-
// we don't pick the representative, since the representative is the
263-
// smallest placeholder by index in the SCC if it is a placeholder
264-
// so in order for it to also be the largest reachable min would
265-
// have to be equal to max, but then we would only have reached one
266-
// placeholder.
267-
return Some(self.max_reachable_placeholder.unwrap());
268-
}
219+
debug!("Universe {max_reached_universe:?} is too large for its SCC!");
220+
// We originally had a large enough universe to fit all our reachable
221+
// placeholders, but had it lowered because we also reached something
222+
// small-universed. In this case, that's to blame!
223+
let to_blame = if self.representative == large_u_rvid {
224+
debug!("{:?} lowered our universe!", self.min_reachable_universe);
225+
self.min_reachable_universe.1
226+
} else {
227+
// The problem is that we, who have a small universe, reach a large one.
228+
large_u_rvid
229+
};
269230

270-
None
231+
Some(to_blame)
271232
}
272233
}
273234

@@ -1949,14 +1910,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19491910
// relation, redirect the search to the placeholder to blame.
19501911
if self.is_static(to) {
19511912
for constraint in path.iter() {
1952-
let ConstraintCategory::IllegalPlaceholder(culprit_r) = constraint.category else {
1913+
let ConstraintCategory::IllegalPlaceholder(culprit_from, culprit_to) =
1914+
constraint.category
1915+
else {
19531916
continue;
19541917
};
19551918

1956-
debug!("{culprit_r:?} is the reason {from:?}: 'static!");
1919+
debug!("{culprit_from:?}: {culprit_to:?} is the reason {from:?}: 'static!");
19571920
// FIXME: think: this may be for transitive reasons and
19581921
// we may have to do this arbitrarily many times. Or may we?
1959-
return self.find_constraint_path_to(from, |r| r == culprit_r, false).unwrap();
1922+
return self
1923+
.find_constraint_path_to(culprit_from, |r| r == culprit_to, false)
1924+
.unwrap();
19601925
}
19611926
}
19621927
// No funny business; just return the path!

compiler/rustc_middle/src/mir/query.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,9 @@ pub enum ConstraintCategory<'tcx> {
275275
Internal,
276276

277277
/// An internal constraint derived from an illegal placeholder relation
278-
/// to this region.
279-
IllegalPlaceholder(RegionVid),
278+
/// to this region. The arguments are a source -> drain of a path
279+
/// that caused the problem, used when reporting errors.
280+
IllegalPlaceholder(RegionVid, RegionVid),
280281
}
281282

282283
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]

0 commit comments

Comments
 (0)