Skip to content

Commit 834fbf1

Browse files
Auto merge of #140737 - amandasystems:revised-constraint-search, r=<try>
Region inference: Use outlives-static constraints in constraint search
2 parents 8365fcb + c11f113 commit 834fbf1

File tree

13 files changed

+264
-223
lines changed

13 files changed

+264
-223
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
@@ -193,10 +193,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindOpaqueRegion<'_, 'tcx> {
193193
let opaque_region_vid = self.regioncx.to_region_vid(opaque_region);
194194

195195
// Find a path between the borrow region and our opaque capture.
196-
if let Some((path, _)) =
197-
self.regioncx.find_constraint_paths_between_regions(self.borrow_region, |r| {
198-
r == opaque_region_vid
199-
})
196+
if let Some(path) = self
197+
.regioncx
198+
.constraint_path_between_regions(self.borrow_region, opaque_region_vid)
200199
{
201200
for constraint in path {
202201
// 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
@@ -62,7 +62,7 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
6262
| ConstraintCategory::Boring
6363
| ConstraintCategory::BoringNoLocation
6464
| ConstraintCategory::Internal
65-
| ConstraintCategory::IllegalUniverse => "",
65+
| ConstraintCategory::OutlivesUnnameablePlaceholder(..) => "",
6666
}
6767
}
6868
}
@@ -410,11 +410,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
410410
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
411411

412412
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
413-
let (_, cause) = self.regioncx.find_outlives_blame_span(
414-
longer_fr,
415-
NllRegionVariableOrigin::Placeholder(placeholder),
416-
error_vid,
417-
);
413+
let cause = self
414+
.regioncx
415+
.best_blame_constraint(
416+
longer_fr,
417+
NllRegionVariableOrigin::Placeholder(placeholder),
418+
error_vid,
419+
)
420+
.0
421+
.cause;
418422

419423
let universe = placeholder.universe;
420424
let universe_info = self.regioncx.universe_info(universe);
@@ -470,9 +474,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
470474
) {
471475
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
472476

473-
let (blame_constraint, path) = self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
474-
self.regioncx.provides_universal_region(r, fr, outlived_fr)
475-
});
477+
let (blame_constraint, path) =
478+
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
476479
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
477480

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

compiler/rustc_borrowck/src/handle_placeholders.rs

Lines changed: 150 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Logic for lowering higher-kinded outlives constraints
22
//! (with placeholders and universes) and turn them into regular
33
//! outlives constraints.
4+
use core::cmp;
45

56
use rustc_data_structures::frozen::Frozen;
67
use rustc_data_structures::fx::FxIndexMap;
@@ -10,7 +11,7 @@ use rustc_index::IndexVec;
1011
use rustc_infer::infer::RegionVariableOrigin;
1112
use rustc_middle::mir::ConstraintCategory;
1213
use rustc_middle::ty::{RegionVid, UniverseIndex};
13-
use tracing::debug;
14+
use tracing::{debug, trace};
1415

1516
use crate::constraints::{ConstraintSccIndex, OutlivesConstraintSet};
1617
use crate::consumers::OutlivesConstraint;
@@ -64,84 +65,147 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
6465
type SccIdx = ConstraintSccIndex;
6566
}
6667

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

75129
/// The largest universe nameable from this SCC.
76130
/// It is the smallest nameable universes of all
77-
/// existential regions reachable from it.
78-
max_nameable_universe: UniverseIndex,
131+
/// existential regions reachable from it. Small Rvids are preferred.
132+
max_nameable_universe: (UniverseIndex, RegionVid),
79133

80134
/// The representative Region Variable Id for this SCC.
81135
pub(crate) representative: Representative,
82136
}
83137

84138
impl RegionTracker {
85139
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
86-
let placeholder_universe =
140+
let reachable_placeholders =
87141
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
88-
definition.universe
142+
PlaceholderReachability::Placeholders {
143+
max_universe: (definition.universe, rvid),
144+
min_placeholder: rvid,
145+
max_placeholder: rvid,
146+
}
89147
} else {
90-
UniverseIndex::ROOT
148+
PlaceholderReachability::NoPlaceholders
91149
};
92150

93151
Self {
94-
max_placeholder_universe_reached: placeholder_universe,
95-
max_nameable_universe: definition.universe,
152+
reachable_placeholders,
153+
max_nameable_universe: (definition.universe, rvid),
96154
representative: Representative::new(rvid, definition),
97155
}
98156
}
99157

100158
/// The largest universe this SCC can name. It's the smallest
101-
/// largest nameable uninverse of any reachable region.
159+
/// largest nameable universe of any reachable region, or
160+
/// `max_nameable(r) = min (max_nameable(r') for r' reachable from r)`
102161
pub(crate) fn max_nameable_universe(self) -> UniverseIndex {
103-
self.max_nameable_universe
162+
self.max_nameable_universe.0
104163
}
105164

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

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

134190
impl scc::Annotation for RegionTracker {
135-
fn merge_scc(mut self, other: Self) -> Self {
136-
self.representative = self.representative.merge_scc(other.representative);
137-
self.merge_min_max_seen(&other);
138-
self
191+
fn merge_scc(self, other: Self) -> Self {
192+
trace!("{:?} << {:?}", self.representative, other.representative);
193+
194+
Self {
195+
representative: self.representative.merge_scc(other.representative),
196+
..self.merge_reached(other)
197+
}
139198
}
140199

141-
fn merge_reached(mut self, other: Self) -> Self {
142-
// No update to in-component values, only add seen values.
143-
self.merge_min_max_seen(&other);
144-
self
200+
fn merge_reached(self, other: Self) -> Self {
201+
Self {
202+
max_nameable_universe: cmp::min(
203+
self.max_nameable_universe,
204+
other.max_nameable_universe,
205+
),
206+
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
207+
representative: self.representative,
208+
}
145209
}
146210
}
147211

@@ -314,28 +378,52 @@ fn rewrite_placeholder_outlives<'tcx>(
314378

315379
let annotation = annotations[scc];
316380

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

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)