Skip to content

Commit b2dd217

Browse files
committed
Auto merge of #140737 - amandasystems:revised-constraint-search, r=lcnr
Region inference: Use outlives-static constraints in constraint search Revise the extra `r: 'static` constraints added upon universe issues to add an explanation, and use that explanation during constraint blame search. This greatly simplifies the region inference logic, which now does not need to reverse-engineer the event that caused a region to outlive `'static`. This cosmetically changes the output of two UI tests. I blessed them i separate commits with separate motivations, but that can of course be squashed as desired. We probably want that. The PR was extracted out of #130227 and consists of one-third of its functional payload. r? lcnr
2 parents 4f808ba + ae622bb commit b2dd217

File tree

13 files changed

+281
-232
lines changed

13 files changed

+281
-232
lines changed

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

Lines changed: 1 addition & 1 deletion
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_types.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
243243
let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
244244

245245
// Find a path between the borrow region and our opaque capture.
246-
if let Some((path, _)) =
247-
self.regioncx.find_constraint_path_between_regions(self.borrow_region, |r| {
248-
r == opaque_region_vid
249-
})
246+
if let Some(path) = self
247+
.regioncx
248+
.constraint_path_between_regions(self.borrow_region, opaque_region_vid)
250249
{
251250
for constraint in path {
252251
// If we find a call in this path, then check if it defines the opaque.

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

Lines changed: 12 additions & 9 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::OutlivesUnnameablePlaceholder(..) => "",
6565
}
6666
}
6767
}
@@ -369,11 +369,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
369369
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
370370

371371
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
372-
let (_, cause) = self.regioncx.find_outlives_blame_span(
373-
longer_fr,
374-
NllRegionVariableOrigin::Placeholder(placeholder),
375-
error_vid,
376-
);
372+
let cause = self
373+
.regioncx
374+
.best_blame_constraint(
375+
longer_fr,
376+
NllRegionVariableOrigin::Placeholder(placeholder),
377+
error_vid,
378+
)
379+
.0
380+
.cause;
377381

378382
let universe = placeholder.universe;
379383
let universe_info = self.regioncx.universe_info(universe);
@@ -429,9 +433,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
429433
) {
430434
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
431435

432-
let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
433-
self.regioncx.provides_universal_region(r, fr, outlived_fr)
434-
});
436+
let (blame_constraint, path) =
437+
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
435438
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
436439

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

compiler/rustc_borrowck/src/handle_placeholders.rs

Lines changed: 147 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Logic for lowering higher-kinded outlives constraints
22
//! (with placeholders and universes) and turn them into regular
33
//! outlives constraints.
4-
54
use rustc_data_structures::frozen::Frozen;
65
use rustc_data_structures::fx::FxIndexMap;
76
use rustc_data_structures::graph::scc;
@@ -10,7 +9,7 @@ use rustc_index::IndexVec;
109
use rustc_infer::infer::RegionVariableOrigin;
1110
use rustc_middle::mir::ConstraintCategory;
1211
use rustc_middle::ty::{RegionVid, UniverseIndex};
13-
use tracing::debug;
12+
use tracing::{debug, trace};
1413

1514
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
1615
use crate::consumers::OutlivesConstraint;
@@ -62,84 +61,145 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
6261
type SccIdx = ConstraintSccIndex;
6362
}
6463

64+
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
65+
enum PlaceholderReachability {
66+
/// This SCC reaches no placeholders.
67+
NoPlaceholders,
68+
/// This SCC reaches at least one placeholder.
69+
Placeholders {
70+
/// The largest-universed placeholder we can reach
71+
max_universe: (UniverseIndex, RegionVid),
72+
73+
/// The placeholder with the smallest ID
74+
min_placeholder: RegionVid,
75+
76+
/// The placeholder with the largest ID
77+
max_placeholder: RegionVid,
78+
},
79+
}
80+
81+
impl PlaceholderReachability {
82+
/// Merge the reachable placeholders of two graph components.
83+
fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
84+
use PlaceholderReachability::*;
85+
match (self, other) {
86+
(NoPlaceholders, NoPlaceholders) => NoPlaceholders,
87+
(NoPlaceholders, p @ Placeholders { .. })
88+
| (p @ Placeholders { .. }, NoPlaceholders) => p,
89+
(
90+
Placeholders {
91+
min_placeholder: min_pl,
92+
max_placeholder: max_pl,
93+
max_universe: max_u,
94+
},
95+
Placeholders { min_placeholder, max_placeholder, max_universe },
96+
) => Placeholders {
97+
min_placeholder: min_pl.min(min_placeholder),
98+
max_placeholder: max_pl.max(max_placeholder),
99+
max_universe: max_u.max(max_universe),
100+
},
101+
}
102+
}
103+
104+
fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
105+
match self {
106+
Self::NoPlaceholders => None,
107+
Self::Placeholders { max_universe, .. } => Some(*max_universe),
108+
}
109+
}
110+
111+
/// If we have reached placeholders, determine if they can
112+
/// be named from this universe.
113+
fn can_be_named_by(&self, from: UniverseIndex) -> bool {
114+
self.max_universe()
115+
.is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
116+
}
117+
}
118+
65119
/// An annotation for region graph SCCs that tracks
66120
/// the values of its elements. This annotates a single SCC.
67121
#[derive(Copy, Debug, Clone)]
68122
pub(crate) struct RegionTracker {
69-
/// The largest universe of a placeholder reached from this SCC.
70-
/// This includes placeholders within this SCC.
71-
max_placeholder_universe_reached: UniverseIndex,
123+
reachable_placeholders: PlaceholderReachability,
72124

73125
/// The largest universe nameable from this SCC.
74126
/// It is the smallest nameable universes of all
75-
/// existential regions reachable from it.
76-
max_nameable_universe: UniverseIndex,
127+
/// existential regions reachable from it. Small Rvids are preferred.
128+
max_nameable_universe: (UniverseIndex, RegionVid),
77129

78130
/// The representative Region Variable Id for this SCC.
79131
pub(crate) representative: Representative,
80132
}
81133

82134
impl RegionTracker {
83135
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
84-
let placeholder_universe =
136+
let reachable_placeholders =
85137
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
86-
definition.universe
138+
PlaceholderReachability::Placeholders {
139+
max_universe: (definition.universe, rvid),
140+
min_placeholder: rvid,
141+
max_placeholder: rvid,
142+
}
87143
} else {
88-
UniverseIndex::ROOT
144+
PlaceholderReachability::NoPlaceholders
89145
};
90146

91147
Self {
92-
max_placeholder_universe_reached: placeholder_universe,
93-
max_nameable_universe: definition.universe,
148+
reachable_placeholders,
149+
max_nameable_universe: (definition.universe, rvid),
94150
representative: Representative::new(rvid, definition),
95151
}
96152
}
97153

98154
/// The largest universe this SCC can name. It's the smallest
99-
/// largest nameable uninverse of any reachable region.
155+
/// largest nameable universe of any reachable region, or
156+
/// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
100157
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
101-
self.max_nameable_universe
158+
self.max_nameable_universe.0
102159
}
103160

104161
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
105-
self.max_placeholder_universe_reached
106-
}
107-
108-
fn merge_min_max_seen(&mut self, other: &Self) {
109-
self.max_placeholder_universe_reached = std::cmp::max(
110-
self.max_placeholder_universe_reached,
111-
other.max_placeholder_universe_reached,
112-
);
113-
114-
self.max_nameable_universe =
115-
std::cmp::min(self.max_nameable_universe, other.max_nameable_universe);
116-
}
117-
118-
/// Returns `true` if during the annotated SCC reaches a placeholder
119-
/// with a universe larger than the smallest nameable universe of any
120-
/// reachable existential region.
121-
pub(crate) fn has_incompatible_universes(&self) -> bool {
122-
self.max_nameable_universe().cannot_name(self.max_placeholder_universe_reached)
162+
if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
163+
universe
164+
} else {
165+
UniverseIndex::ROOT
166+
}
123167
}
124168

125169
/// Determine if the tracked universes of the two SCCs are compatible.
126170
pub(crate) fn universe_compatible_with(&self, other: Self) -> bool {
171+
// HACK: We first check whether we can name the highest existential universe
172+
// of `other`. This only exists to avoid errors in case that scc already
173+
// depends on a placeholder it cannot name itself.
127174
self.max_nameable_universe().can_name(other.max_nameable_universe())
128-
|| self.max_nameable_universe().can_name(other.max_placeholder_universe_reached)
175+
|| other.reachable_placeholders.can_be_named_by(self.max_nameable_universe())
176+
}
177+
178+
/// If this SCC reaches a placeholder it can't name, return it.
179+
fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
180+
self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
181+
!self.max_nameable_universe().can_name(placeholder_universe)
182+
})
129183
}
130184
}
131185

132186
impl scc::Annotation for RegionTracker {
133-
fn merge_scc(mut self, other: Self) -> Self {
134-
self.representative = self.representative.merge_scc(other.representative);
135-
self.merge_min_max_seen(&other);
136-
self
187+
fn merge_scc(self, other: Self) -> Self {
188+
trace!("{:?} << {:?}", self.representative, other.representative);
189+
190+
Self {
191+
representative: self.representative.min(other.representative),
192+
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
193+
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
194+
}
137195
}
138196

139-
fn merge_reached(mut self, other: Self) -> Self {
140-
// No update to in-component values, only add seen values.
141-
self.merge_min_max_seen(&other);
142-
self
197+
fn merge_reached(self, other: Self) -> Self {
198+
Self {
199+
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
200+
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
201+
representative: self.representative,
202+
}
143203
}
144204
}
145205

@@ -310,28 +370,52 @@ fn rewrite_placeholder_outlives<'tcx>(
310370

311371
let annotation = annotations[scc];
312372

313-
// If this SCC participates in a universe violation,
314-
// e.g. if it reaches a region with a universe smaller than
315-
// the largest region reached, add a requirement that it must
316-
// outlive `'static`.
317-
if annotation.has_incompatible_universes() {
318-
// Optimisation opportunity: this will add more constraints than
319-
// needed for correctness, since an SCC upstream of another with
320-
// a universe violation will "infect" its downstream SCCs to also
321-
// outlive static.
322-
let scc_representative_outlives_static = OutlivesConstraint {
323-
sup: annotation.representative.rvid(),
324-
sub: fr_static,
325-
category: ConstraintCategory::IllegalUniverse,
326-
locations: Locations::All(rustc_span::DUMMY_SP),
327-
span: rustc_span::DUMMY_SP,
328-
variance_info: VarianceDiagInfo::None,
329-
from_closure: false,
330-
};
331-
outlives_constraints.push(scc_representative_outlives_static);
332-
added_constraints = true;
333-
debug!("Added {:?}: 'static!", annotation.representative.rvid());
334-
}
373+
let Some((max_u, max_u_rvid)) = annotation.unnameable_placeholder() else {
374+
continue;
375+
};
376+
377+
debug!(
378+
"Placeholder universe {max_u:?} is too large for its SCC, represented by {:?}",
379+
annotation.representative
380+
);
381+
382+
// We only add one `r: 'static` constraint per SCC, where `r` is the SCC representative.
383+
// That constraint is annotated with some placeholder `unnameable` where
384+
// `unnameable` is unnameable from `r` and there is a path in the constraint graph
385+
// between them.
386+
//
387+
// There is one exception; if some other region in this SCC can't name `'r`, then
388+
// we pick the region with the smallest universe in the SCC, so that a path can
389+
// always start in `'r` to find a motivation that isn't cyclic.
390+
let blame_to = if annotation.representative.rvid() == max_u_rvid {
391+
// Assertion: the region that lowered our universe is an existential one and we are a placeholder!
392+
393+
// The SCC's representative is not nameable from some region
394+
// that ends up in the SCC.
395+
let small_universed_rvid = annotation.max_nameable_universe.1;
396+
debug!(
397+
"{small_universed_rvid:?} lowered our universe to {:?}",
398+
annotation.max_nameable_universe()
399+
);
400+
small_universed_rvid
401+
} else {
402+
// `max_u_rvid` is not nameable by the SCC's representative.
403+
max_u_rvid
404+
};
405+
406+
// FIXME: if we can extract a useful blame span here, future error
407+
// reporting and constraint search can be simplified.
408+
409+
added_constraints = true;
410+
outlives_constraints.push(OutlivesConstraint {
411+
sup: annotation.representative.rvid(),
412+
sub: fr_static,
413+
category: ConstraintCategory::OutlivesUnnameablePlaceholder(blame_to),
414+
locations: Locations::All(rustc_span::DUMMY_SP),
415+
span: rustc_span::DUMMY_SP,
416+
variance_info: VarianceDiagInfo::None,
417+
from_closure: false,
418+
});
335419
}
336420
added_constraints
337421
}

compiler/rustc_borrowck/src/region_infer/graphviz.rs

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

1414
fn render_outlives_constraint(constraint: &OutlivesConstraint<'_>) -> String {
15-
match constraint.locations {
16-
Locations::All(_) => "All(...)".to_string(),
17-
Locations::Single(loc) => format!("{loc:?}"),
15+
if let ConstraintCategory::OutlivesUnnameablePlaceholder(unnameable) = constraint.category {
16+
format!("{unnameable:?} unnameable")
17+
} else {
18+
match constraint.locations {
19+
Locations::All(_) => "All(...)".to_string(),
20+
Locations::Single(loc) => format!("{loc:?}"),
21+
}
1822
}
1923
}
2024

0 commit comments

Comments
 (0)