Skip to content

Commit 38d5bb5

Browse files
committed
Move handling of placeholders out of the borrow checker
This commit moves all handling of placeholders, including those in type-tests, into a pre-processing step of the borrowcheck. Along the way it 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 rewrites type-tests that have the same behaviour. Error reporting is handled by introducing a new constraint kind that remembers which original outlives constraint caused an outlives-static. These work a lot like a HTTP redirect response during constraint search. `RegionInferenceContext` is now a lot simpler and only tracks opaque regions without caring about their origin. 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. 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.
1 parent 0134651 commit 38d5bb5

33 files changed

+2029
-1116
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

+11-99
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
use std::fmt;
22
use std::ops::Index;
33

4+
use rustc_data_structures::graph::scc;
45
use rustc_index::{IndexSlice, IndexVec};
56
use rustc_middle::mir::ConstraintCategory;
67
use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
78
use rustc_span::Span;
8-
use tracing::{debug, instrument};
9+
use tracing::debug;
910

10-
use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker};
1111
use crate::type_check::Locations;
12-
use crate::universal_regions::UniversalRegions;
1312

1413
pub(crate) mod graph;
1514

@@ -57,105 +56,18 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
5756
/// Computes cycles (SCCs) in the graph of regions. In particular,
5857
/// find all regions R1, R2 such that R1: R2 and R2: R1 and group
5958
/// them into an SCC, and find the relationships between SCCs.
60-
pub(crate) fn compute_sccs(
59+
pub(crate) fn compute_sccs<
60+
A: scc::Annotation,
61+
AA: scc::Annotations<RegionVid, ConstraintSccIndex, A>,
62+
>(
6163
&self,
6264
static_region: RegionVid,
63-
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
64-
) -> ConstraintSccs {
65-
let constraint_graph = self.graph(definitions.len());
65+
num_region_vars: usize,
66+
annotations: &mut AA,
67+
) -> scc::Sccs<RegionVid, ConstraintSccIndex> {
68+
let constraint_graph = self.graph(num_region_vars);
6669
let region_graph = &constraint_graph.region_graph(self, static_region);
67-
ConstraintSccs::new_with_annotation(&region_graph, |r| {
68-
RegionTracker::new(r, &definitions[r])
69-
})
70-
}
71-
72-
/// This method handles Universe errors by rewriting the constraint
73-
/// graph. For each strongly connected component in the constraint
74-
/// graph such that there is a series of constraints
75-
/// A: B: C: ... : X where
76-
/// A's universe is smaller than X's and A is a placeholder,
77-
/// add a constraint that A: 'static. This is a safe upper bound
78-
/// in the face of borrow checker/trait solver limitations that will
79-
/// eventually go away.
80-
///
81-
/// For a more precise definition, see the documentation for
82-
/// [`RegionTracker::has_incompatible_universes()`].
83-
///
84-
/// This edge case used to be handled during constraint propagation
85-
/// by iterating over the strongly connected components in the constraint
86-
/// graph while maintaining a set of bookkeeping mappings similar
87-
/// to what is stored in `RegionTracker` and manually adding 'sttaic as
88-
/// needed.
89-
///
90-
/// It was rewritten as part of the Polonius project with the goal of moving
91-
/// higher-kindedness concerns out of the path of the borrow checker,
92-
/// for two reasons:
93-
///
94-
/// 1. Implementing Polonius is difficult enough without also
95-
/// handling them.
96-
/// 2. The long-term goal is to handle higher-kinded concerns
97-
/// in the trait solver, where they belong. This avoids
98-
/// logic duplication and allows future trait solvers
99-
/// to compute better bounds than for example our
100-
/// "must outlive 'static" here.
101-
///
102-
/// This code is a stop-gap measure in preparation for the future trait solver.
103-
///
104-
/// Every constraint added by this method is an
105-
/// internal `IllegalUniverse` constraint.
106-
#[instrument(skip(self, universal_regions, definitions))]
107-
pub(crate) fn add_outlives_static(
108-
&mut self,
109-
universal_regions: &UniversalRegions<'tcx>,
110-
definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
111-
) -> ConstraintSccs {
112-
let fr_static = universal_regions.fr_static;
113-
let sccs = self.compute_sccs(fr_static, definitions);
114-
115-
// Changed to `true` if we added any constraints to `self` and need to
116-
// recompute SCCs.
117-
let mut added_constraints = false;
118-
119-
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-
127-
let annotation = sccs.annotation(scc);
128-
129-
// If this SCC participates in a universe violation,
130-
// e.g. if it reaches a region with a universe smaller than
131-
// 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
136-
// a universe violation will "infect" its downstream SCCs to also
137-
// outlive static.
138-
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);
149-
}
150-
}
151-
152-
if added_constraints {
153-
// We changed the constraint set and so must recompute SCCs.
154-
self.compute_sccs(fr_static, definitions)
155-
} else {
156-
// If we didn't add any back-edges; no more work needs doing
157-
sccs
158-
}
70+
scc::Sccs::new_with_annotation(&region_graph, annotations)
15971
}
16072
}
16173

compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs

+38-36
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_
2424
use tracing::{debug, instrument};
2525

2626
use crate::MirBorrowckCtxt;
27-
use crate::region_infer::values::RegionElement;
2827
use crate::session_diagnostics::{
2928
HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError,
3029
};
@@ -49,12 +48,13 @@ impl<'tcx> UniverseInfo<'tcx> {
4948
UniverseInfo::RelateTys { expected, found }
5049
}
5150

51+
/// Report an error where an element erroneously made its way into `placeholder`.
5252
pub(crate) fn report_erroneous_element(
5353
&self,
5454
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
5555
placeholder: ty::PlaceholderRegion,
56-
error_element: RegionElement,
5756
cause: ObligationCause<'tcx>,
57+
error_element: Option<ty::PlaceholderRegion>,
5858
) {
5959
match *self {
6060
UniverseInfo::RelateTys { expected, found } => {
@@ -68,7 +68,7 @@ impl<'tcx> UniverseInfo<'tcx> {
6868
mbcx.buffer_error(err);
6969
}
7070
UniverseInfo::TypeOp(ref type_op_info) => {
71-
type_op_info.report_erroneous_element(mbcx, placeholder, error_element, cause);
71+
type_op_info.report_erroneous_element(mbcx, placeholder, cause, error_element);
7272
}
7373
UniverseInfo::Other => {
7474
// FIXME: This error message isn't great, but it doesn't show
@@ -145,52 +145,54 @@ pub(crate) trait TypeOpInfo<'tcx> {
145145
error_region: Option<ty::Region<'tcx>>,
146146
) -> Option<Diag<'infcx>>;
147147

148-
/// Constraints require that `error_element` appear in the
149-
/// values of `placeholder`, but this cannot be proven to
150-
/// hold. Report an error.
148+
/// Turn a placeholder region into a Region with its universe adjusted by
149+
/// the base universe.
150+
fn region_with_adjusted_universe(
151+
&self,
152+
placeholder: ty::PlaceholderRegion,
153+
tcx: TyCtxt<'tcx>,
154+
) -> ty::Region<'tcx> {
155+
let Some(adjusted_universe) =
156+
placeholder.universe.as_u32().checked_sub(self.base_universe().as_u32())
157+
else {
158+
unreachable!(
159+
"Could not adjust universe {:?} of {placeholder:?} by base universe {:?}",
160+
placeholder.universe,
161+
self.base_universe()
162+
);
163+
};
164+
ty::Region::new_placeholder(
165+
tcx,
166+
ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
167+
)
168+
}
169+
170+
/// Report an error where an erroneous element reaches `placeholder`.
171+
/// The erroneous element is either another placeholder that we provide,
172+
/// or we figure out what happened later.
151173
#[instrument(level = "debug", skip(self, mbcx))]
152174
fn report_erroneous_element(
153175
&self,
154176
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
155177
placeholder: ty::PlaceholderRegion,
156-
error_element: RegionElement,
157178
cause: ObligationCause<'tcx>,
179+
error_element: Option<ty::PlaceholderRegion>,
158180
) {
159181
let tcx = mbcx.infcx.tcx;
160-
let base_universe = self.base_universe();
161-
debug!(?base_universe);
162-
163-
let Some(adjusted_universe) =
164-
placeholder.universe.as_u32().checked_sub(base_universe.as_u32())
165-
else {
166-
mbcx.buffer_error(self.fallback_error(tcx, cause.span));
167-
return;
168-
};
169182

170-
let placeholder_region = ty::Region::new_placeholder(
171-
tcx,
172-
ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
173-
);
174-
175-
let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) =
176-
error_element
177-
{
178-
let adjusted_universe =
179-
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
180-
adjusted_universe.map(|adjusted| {
181-
ty::Region::new_placeholder(
182-
tcx,
183-
ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
184-
)
185-
})
186-
} else {
187-
None
188-
};
183+
// FIXME: these adjusted universes are not (always) the same ones as we compute
184+
// earlier. They probably should be, but the logic downstream is complicated,
185+
// and assumes they use whatever this is.
186+
//
187+
// In fact, this function throws away a lot of interesting information that would
188+
// probably allow bypassing lots of logic downstream for a much simpler flow.
189+
let placeholder_region = self.region_with_adjusted_universe(placeholder, tcx);
190+
let error_element = error_element.map(|e| self.region_with_adjusted_universe(e, tcx));
189191

190192
debug!(?placeholder_region);
191193

192194
let span = cause.span;
193-
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_region);
195+
let nice_error = self.nice_error(mbcx, cause, placeholder_region, error_element);
194196

195197
debug!(?nice_error);
196198
mbcx.buffer_error(nice_error.unwrap_or_else(|| self.fallback_error(tcx, span)));

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
565565
let (blame_constraint, path) = self.regioncx.best_blame_constraint(
566566
borrow_region,
567567
NllRegionVariableOrigin::FreeRegion,
568-
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
568+
outlived_region,
569569
);
570570
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
571571

compiler/rustc_borrowck/src/diagnostics/opaque_suggestions.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
174174
let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
175175

176176
// Find a path between the borrow region and our opaque capture.
177-
if let Some((path, _)) =
178-
self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
179-
r == opaque_region_vid
180-
})
177+
if let Some((path, _)) = self
178+
.regioncx
179+
.constraint_path_between_regions(self.borrow_region, opaque_region_vid)
181180
{
182181
for constraint in path {
183182
// If we find a call in this path, then check if it defines the opaque.

0 commit comments

Comments
 (0)