Skip to content

Commit 72a267a

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 6ab45ce commit 72a267a

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;
@@ -68,6 +69,27 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
6869
})
6970
}
7071

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

118140
for scc in sccs.all_sccs() {
119-
// No point in adding 'static: 'static!
120-
// This micro-optimisation makes somewhat sense
121-
// because static outlives *everything*.
122-
if scc == sccs.scc(fr_static) {
123-
continue;
124-
}
125-
126141
let annotation = sccs.annotation(scc);
127142

128143
// If this SCC participates in a universe violation
129144
// e.g. if it reaches a region with a universe smaller than
130-
// the largest region reached, or if this placeholder
131-
// reaches another placeholder, add a requirement that it must
132-
// outlive `'static`.
133-
if let Some(offending_region) = annotation.placeholder_violation(&sccs) {
134-
assert!(
135-
annotation.representative != offending_region,
136-
"Attemtping to blame a constraint for itself!"
137-
);
138-
// Optimisation opportunity: this will add more constraints than
139-
// needed for correctness, since an SCC upstream of another with
145+
// the largest region reached, add a requirement that it must
146+
// outlive `'static`. Here we get to know which reachable region
147+
// caused the violation.
148+
if let Some(to) = annotation.universe_violation() {
149+
// Optimisation opportunity: this will potentially add more constraints
150+
// than needed for correctness, since an SCC upstream of another with
140151
// a universe violation will "infect" its downstream SCCs to also
141-
// outlive static.
152+
// outlive static. However, some of those may be useful for error
153+
// reporting.
142154
added_constraints = true;
143-
let scc_representative_outlives_static = OutlivesConstraint {
144-
sup: annotation.representative,
145-
sub: fr_static,
146-
category: ConstraintCategory::IllegalPlaceholder(offending_region),
147-
locations: Locations::All(rustc_span::DUMMY_SP),
148-
span: rustc_span::DUMMY_SP,
149-
variance_info: VarianceDiagInfo::None,
150-
from_closure: false,
151-
};
152-
self.push(scc_representative_outlives_static);
155+
self.add_placeholder_violation_constraint(
156+
annotation.representative,
157+
annotation.representative,
158+
to,
159+
fr_static,
160+
);
161+
}
162+
}
163+
164+
// The second kind of violation: a placeholder reaching another placeholder.
165+
// OPTIMIZATION: This one is even more optimisable since it adds constraints for every
166+
// placeholder in an SCC.
167+
for rvid in definitions.iter_enumerated().filter_map(|(rvid, definition)| {
168+
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder { .. }) {
169+
Some(rvid)
170+
} else {
171+
None
153172
}
173+
}) {
174+
let scc = sccs.scc(rvid);
175+
let annotation = sccs.annotation(scc);
176+
177+
// Unwrap safety: since this is our SCC it must contain us, which is
178+
// at worst min AND max, but it has at least one or there is a bug.
179+
let min = annotation.min_reachable_placeholder.unwrap();
180+
let max = annotation.max_reachable_placeholder.unwrap();
181+
182+
// Good path: Nothing to see here, at least no other placeholders!
183+
if min == max {
184+
continue;
185+
}
186+
187+
// Bad path: figure out who we illegally reached.
188+
// Note that this will prefer the representative if it is a
189+
// placeholder, since the representative has the smallest index!
190+
let other_placeholder = if min != rvid { min } else { max };
191+
192+
debug!(
193+
"Placeholder {rvid:?} of SCC {scc:?} reaches other placeholder {other_placeholder:?}"
194+
);
195+
added_constraints = true;
196+
self.add_placeholder_violation_constraint(
197+
annotation.representative,
198+
rvid,
199+
other_placeholder,
200+
fr_static,
201+
);
154202
}
155203

156204
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
@@ -59,7 +59,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
5959
| ConstraintCategory::Boring
6060
| ConstraintCategory::BoringNoLocation
6161
| ConstraintCategory::Internal
62-
| ConstraintCategory::IllegalPlaceholder(_) => "",
62+
| ConstraintCategory::IllegalPlaceholder(..) => "",
6363
}
6464
}
6565
}

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
@@ -75,7 +75,7 @@ pub struct RegionTracker {
7575
max_universe_placeholder_reached: ReachablePlaceholder,
7676

7777
/// The smallest universe index reachable form the nodes of this SCC.
78-
min_reachable_universe: UniverseIndex,
78+
min_reachable_universe: (UniverseIndex, RegionVid),
7979

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

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

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

9494
/// Is there at least one placeholder in this SCC?
9595
contains_placeholder: bool,
@@ -142,7 +142,7 @@ impl RegionTracker {
142142

143143
Self {
144144
max_universe_placeholder_reached,
145-
min_reachable_universe: definition.universe,
145+
min_reachable_universe: (definition.universe, rvid),
146146
representative: rvid,
147147
representative_origin,
148148
min_reachable_placeholder: representative_if_placeholder,
@@ -151,24 +151,6 @@ impl RegionTracker {
151151
}
152152
}
153153

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

182164
/// The smallest-indexed universe reachable from and/or in this SCC.
183165
fn min_universe(self) -> UniverseIndex {
184-
self.min_reachable_universe
166+
self.min_reachable_universe.0
185167
}
186168

187169
fn merge_reachable_placeholders(&mut self, other: &Self) {
@@ -215,58 +197,37 @@ impl RegionTracker {
215197
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
216198
}
217199

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

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

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

269-
None
230+
Some(to_blame)
270231
}
271232
}
272233

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

1955-
debug!("{culprit_r:?} is the reason {from:?}: 'static!");
1918+
debug!("{culprit_from:?}: {culprit_to:?} is the reason {from:?}: 'static!");
19561919
// FIXME: think: this may be for transitive reasons and
19571920
// we may have to do this arbitrarily many times. Or may we?
1958-
return self.find_constraint_path_to(from, |r| r == culprit_r, false).unwrap();
1921+
return self
1922+
.find_constraint_path_to(culprit_from, |r| r == culprit_to, false)
1923+
.unwrap();
19591924
}
19601925
}
19611926
// 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
@@ -273,8 +273,9 @@ pub enum ConstraintCategory<'tcx> {
273273
Internal,
274274

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

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

0 commit comments

Comments
 (0)