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

Commit 54bcafb

Browse files
committed
Preliminary attempt at removing placeholders out of the borrow checker.
This version is maximally naive, and currently unsound. Handle more placeholder errors. This update extends the SCC metadata tracking a lot and uses the extra information to add p: 'static for any placeholder that reaches another placeholder. It also inlines the few measly bits of `init_free_and_bound_regions()` that still remain as relevant. This increases the constructor for `RegionInferenceContext`s somewhat, but I still think it's readable. The documentation for `init_free_and_bound_regions()` was out of date, and the correct, up to date version is available in the various places where the logic was moved. Use annotions on the outlives-static constraints to help the search for who is to blame. Invalid placeholder relation constraints are redirects This commit makes the search for blamable outlives constraints treat an added `x: 'static` edge as a redirect to figure out why it reached an invalid placeholder. As a drive-by it also refactors the blame search somewhat, renames a few methods, and allows iterating over outgoing constraints without the implied edges from 'static. 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. Remove some stuff we don't need or can't use anymore Figure out better names for opaque types! Fix tidy error Fix most of the cases of missing "implied 'static" warnings Fix documentation that build-failed
1 parent 4d215e2 commit 54bcafb

File tree

12 files changed

+462
-484
lines changed

12 files changed

+462
-484
lines changed

compiler/rustc_borrowck/src/constraints/graph.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,17 @@ impl<D: ConstraintGraphDirection> ConstraintGraph<D> {
106106
}
107107

108108
/// Given a region `R`, iterate over all constraints `R: R1`.
109+
/// if `static_region` is `None`, do not yield implicit
110+
/// `'static -> a` edges.
109111
pub(crate) fn outgoing_edges<'a, 'tcx>(
110112
&'a self,
111113
region_sup: RegionVid,
112114
constraints: &'a OutlivesConstraintSet<'tcx>,
113-
static_region: RegionVid,
115+
static_region: Option<RegionVid>,
114116
) -> Edges<'a, 'tcx, D> {
115117
//if this is the `'static` region and the graph's direction is normal,
116118
//then setup the Edges iterator to return all regions #53178
117-
if region_sup == static_region && D::is_normal() {
119+
if Some(region_sup) == static_region && D::is_normal() {
118120
Edges {
119121
graph: self,
120122
constraints,
@@ -135,7 +137,7 @@ pub(crate) struct Edges<'a, 'tcx, D: ConstraintGraphDirection> {
135137
constraints: &'a OutlivesConstraintSet<'tcx>,
136138
pointer: Option<OutlivesConstraintIndex>,
137139
next_static_idx: Option<usize>,
138-
static_region: RegionVid,
140+
static_region: Option<RegionVid>,
139141
}
140142

141143
impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> {
@@ -153,8 +155,12 @@ impl<'a, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'a, 'tcx, D> {
153155
Some(next_static_idx + 1)
154156
};
155157

158+
let Some(static_region) = self.static_region else {
159+
return None;
160+
};
161+
156162
Some(OutlivesConstraint {
157-
sup: self.static_region,
163+
sup: static_region,
158164
sub: next_static_idx.into(),
159165
locations: Locations::All(DUMMY_SP),
160166
span: DUMMY_SP,
@@ -194,7 +200,11 @@ impl<'a, 'tcx, D: ConstraintGraphDirection> RegionGraph<'a, 'tcx, D> {
194200
/// there exists a constraint `R: R1`.
195201
pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'a, 'tcx, D> {
196202
Successors {
197-
edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
203+
edges: self.constraint_graph.outgoing_edges(
204+
region_sup,
205+
self.set,
206+
Some(self.static_region),
207+
),
198208
}
199209
}
200210
}

compiler/rustc_borrowck/src/constraints/mod.rs

Lines changed: 77 additions & 24 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
@@ -79,7 +101,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
79101
/// eventually go away.
80102
///
81103
/// For a more precise definition, see the documentation for
82-
/// [`RegionTracker::has_incompatible_universes()`].
104+
/// [`RegionTracker`] and its methods!.
83105
///
84106
/// This edge case used to be handled during constraint propagation
85107
/// by iterating over the strongly connected components in the constraint
@@ -117,36 +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

129-
// If this SCC participates in a universe violation,
144+
// If this SCC participates in a universe violation
130145
// e.g. if it reaches a region with a universe smaller than
131146
// the largest region reached, add a requirement that it must
132-
// outlive `'static`.
133-
if annotation.has_incompatible_universes() {
134-
// Optimisation opportunity: this will add more constraints than
135-
// needed for correctness, since an SCC upstream of another with
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
136152
// a universe violation will "infect" its downstream SCCs to also
137-
// outlive static.
153+
// outlive static. However, some of those may be useful for error
154+
// reporting.
138155
added_constraints = true;
139-
let scc_representative_outlives_static = OutlivesConstraint {
140-
sup: annotation.representative,
141-
sub: fr_static,
142-
category: ConstraintCategory::IllegalUniverse,
143-
locations: Locations::All(rustc_span::DUMMY_SP),
144-
span: rustc_span::DUMMY_SP,
145-
variance_info: VarianceDiagInfo::None,
146-
from_closure: false,
147-
};
148-
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
173+
}
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;
149186
}
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+
);
150203
}
151204

152205
if added_constraints {

compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,24 +161,12 @@ pub(crate) trait TypeOpInfo<'tcx> {
161161
bound: placeholder.bound,
162162
});
163163

164-
let error_region =
165-
if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
166-
let adjusted_universe =
167-
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
168-
adjusted_universe.map(|adjusted| {
169-
ty::Region::new_placeholder(tcx, ty::Placeholder {
170-
universe: adjusted.into(),
171-
bound: error_placeholder.bound,
172-
})
173-
})
174-
} else {
175-
None
176-
};
177-
178164
debug!(?placeholder_region);
179165

166+
// FIXME: this is obviously weird; this whole argument now does nothing and maybe
167+
// it should?
180168
let span = cause.span;
181-
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
169+
let nice_error = self.nice_error(mbcx, cause, placeholder_region, None);
182170

183171
debug!(?nice_error);
184172

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
491491
let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
492492
borrow_region,
493493
NllRegionVariableOrigin::FreeRegion,
494-
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
494+
outlived_region,
495495
);
496496
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
497497

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Lines changed: 25 additions & 43 deletions
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::IllegalUniverse => "",
64+
| ConstraintCategory::IllegalPlaceholder(..) => "",
6565
}
6666
}
6767
}
@@ -206,52 +206,35 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
206206
&self,
207207
diag: &mut Diag<'_>,
208208
lower_bound: RegionVid,
209-
) {
209+
) -> Option<()> {
210210
let mut suggestions = vec![];
211211
let hir = self.infcx.tcx.hir();
212212

213-
// find generic associated types in the given region 'lower_bound'
214-
let gat_id_and_generics = self
215-
.regioncx
216-
.placeholders_contained_in(lower_bound)
217-
.map(|placeholder| {
218-
if let Some(id) = placeholder.bound.kind.get_id()
219-
&& let Some(placeholder_id) = id.as_local()
220-
&& let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id)
221-
&& let Some(generics_impl) = self
222-
.infcx
223-
.tcx
224-
.parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id))
225-
.generics()
226-
{
227-
Some((gat_hir_id, generics_impl))
228-
} else {
229-
None
230-
}
231-
})
232-
.collect::<Vec<_>>();
233-
debug!(?gat_id_and_generics);
234-
235213
// find higher-ranked trait bounds bounded to the generic associated types
214+
let scc = self.regioncx.constraint_sccs().scc(lower_bound);
215+
let placeholder: ty::PlaceholderRegion = self.regioncx.placeholder_representative(scc)?;
216+
let placeholder_id = placeholder.bound.kind.get_id()?.as_local()?;
217+
let gat_hir_id = self.infcx.tcx.local_def_id_to_hir_id(placeholder_id);
218+
let generics_impl =
219+
self.infcx.tcx.parent_hir_node(self.infcx.tcx.parent_hir_id(gat_hir_id)).generics()?;
220+
236221
let mut hrtb_bounds = vec![];
237-
gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
238-
for pred in generics.predicates {
239-
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred
240-
else {
241-
continue;
242-
};
243-
if bound_generic_params
244-
.iter()
245-
.rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
246-
.is_some()
247-
{
248-
for bound in *bounds {
249-
hrtb_bounds.push(bound);
250-
}
222+
223+
for pred in generics_impl.predicates {
224+
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred
225+
else {
226+
continue;
227+
};
228+
if bound_generic_params
229+
.iter()
230+
.rfind(|bgp| self.infcx.tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
231+
.is_some()
232+
{
233+
for bound in *bounds {
234+
hrtb_bounds.push(bound);
251235
}
252236
}
253-
});
254-
debug!(?hrtb_bounds);
237+
}
255238

256239
hrtb_bounds.iter().for_each(|bound| {
257240
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
@@ -300,6 +283,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
300283
Applicability::MaybeIncorrect,
301284
);
302285
}
286+
Some(())
303287
}
304288

305289
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
@@ -446,9 +430,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
446430
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
447431

448432
let (blame_constraint, extra_info) =
449-
self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
450-
self.regioncx.provides_universal_region(r, fr, outlived_fr)
451-
});
433+
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
452434
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
453435

454436
debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);

compiler/rustc_borrowck/src/nll.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,13 @@ pub(crate) fn compute_regions<'a, 'tcx>(
122122
// base constraints generated by the type-check.
123123
let var_origins = infcx.get_region_var_origins();
124124
let MirTypeckRegionConstraints {
125-
placeholder_indices,
126-
placeholder_index_to_region: _,
127125
liveness_constraints,
128126
mut outlives_constraints,
129127
mut member_constraints,
130128
universe_causes,
131129
type_tests,
130+
..
132131
} = constraints;
133-
let placeholder_indices = Rc::new(placeholder_indices);
134132

135133
// If requested, emit legacy polonius facts.
136134
polonius::emit_facts(
@@ -158,7 +156,6 @@ pub(crate) fn compute_regions<'a, 'tcx>(
158156
infcx,
159157
var_origins,
160158
universal_regions,
161-
placeholder_indices,
162159
universal_region_relations,
163160
outlives_constraints,
164161
member_constraints,

compiler/rustc_borrowck/src/region_infer/graphviz.rs

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

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

0 commit comments

Comments
 (0)