Skip to content

Commit 4214d06

Browse files
committed
Use annotions on the outlives-static constraints to
help the search for who is to blame.
1 parent 222618c commit 4214d06

File tree

5 files changed

+125
-110
lines changed

5 files changed

+125
-110
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,12 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
125125

126126
let annotation = sccs.annotation(scc);
127127

128-
// If this SCC participates in a universe violation,
128+
// If this SCC participates in a universe violation
129129
// e.g. if it reaches a region with a universe smaller than
130-
// the largest region reached, add a requirement that it must
130+
// the largest region reached, or if this placeholder
131+
// reaches another placeholder, add a requirement that it must
131132
// outlive `'static`.
132-
if annotation.has_incompatible_universes() {
133+
if let Some(offending_region) = annotation.placeholder_violation(&sccs) {
133134
// Optimisation opportunity: this will add more constraints than
134135
// needed for correctness, since an SCC upstream of another with
135136
// a universe violation will "infect" its downstream SCCs to also
@@ -138,7 +139,7 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
138139
let scc_representative_outlives_static = OutlivesConstraint {
139140
sup: annotation.representative,
140141
sub: fr_static,
141-
category: ConstraintCategory::IllegalUniverse,
142+
category: ConstraintCategory::IllegalPlaceholder(offending_region),
142143
locations: Locations::All(rustc_span::DUMMY_SP),
143144
span: rustc_span::DUMMY_SP,
144145
variance_info: VarianceDiagInfo::None,

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
398398
let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
399399
borrow_region,
400400
NllRegionVariableOrigin::FreeRegion,
401-
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
401+
outlived_region,
402402
);
403403
let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
404404

compiler/rustc_borrowck/src/diagnostics/region_errors.rs

+2-4
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::IllegalUniverse => "",
62+
| ConstraintCategory::IllegalPlaceholder(_) => "",
6363
}
6464
}
6565
}
@@ -430,9 +430,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
430430
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
431431

432432
let (blame_constraint, extra_info) =
433-
self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
434-
self.regioncx.provides_universal_region(r, fr, outlived_fr)
435-
});
433+
self.regioncx.best_blame_constraint(fr, fr_origin, outlived_fr);
436434
let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
437435

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

compiler/rustc_borrowck/src/region_infer/mod.rs

+113-99
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,25 @@ enum RepresentativeOrigin {
5454
Placeholder,
5555
FreeRegion,
5656
}
57+
58+
/// A reachable placeholder. Note the lexicographic ordering ensures
59+
/// that they are ordered by:
60+
/// A placeholder is larger than no placeholder, then
61+
/// by universe, then
62+
/// by region ID.
63+
#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
64+
enum ReachablePlaceholder {
65+
Nothing,
66+
Placeholder { universe: UniverseIndex, rvid: RegionVid },
67+
}
5768
/// An annotation for region graph SCCs that tracks
5869
/// the values of its elements.
5970
#[derive(Copy, Debug, Clone)]
6071
pub struct RegionTracker {
6172
/// The largest universe of a placeholder reached from this SCC.
62-
/// This includes placeholders within this SCC.
63-
max_placeholder_universe_reached: UniverseIndex,
73+
/// This includes placeholders within this SCC. Including
74+
/// the unverse's associated placeholder region ID.
75+
max_universe_placeholder_reached: ReachablePlaceholder,
6476

6577
/// The smallest universe index reachable form the nodes of this SCC.
6678
min_reachable_universe: UniverseIndex,
@@ -120,13 +132,16 @@ impl RegionTracker {
120132

121133
let rvid_is_placeholder = representative_origin == Placeholder;
122134

123-
let placeholder_universe =
124-
if rvid_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
135+
let max_universe_placeholder_reached = if rvid_is_placeholder {
136+
ReachablePlaceholder::Placeholder { universe: definition.universe, rvid }
137+
} else {
138+
ReachablePlaceholder::Nothing
139+
};
125140

126141
let representative_if_placeholder = if rvid_is_placeholder { Some(rvid) } else { None };
127142

128143
Self {
129-
max_placeholder_universe_reached: placeholder_universe,
144+
max_universe_placeholder_reached,
130145
min_reachable_universe: definition.universe,
131146
representative: rvid,
132147
representative_origin,
@@ -191,21 +206,67 @@ impl RegionTracker {
191206
fn merge_min_max_seen(&mut self, other: &Self) {
192207
self.merge_reachable_placeholders(other);
193208

194-
self.max_placeholder_universe_reached = std::cmp::max(
195-
self.max_placeholder_universe_reached,
196-
other.max_placeholder_universe_reached,
209+
self.max_universe_placeholder_reached = std::cmp::max(
210+
self.max_universe_placeholder_reached,
211+
other.max_universe_placeholder_reached,
197212
);
198213

199214
self.min_reachable_universe =
200215
std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
201216
}
202217

203-
/// Returns `true` if the annotated SCC reaches a placeholder
218+
/// Returns an offending region if the annotated SCC reaches a placeholder
204219
/// with a universe larger than the smallest reachable one,
205-
/// or if a placeholder reaches another placeholder, `false` otherwise.
206-
pub(crate) fn has_incompatible_universes(&self) -> bool {
207-
self.min_universe().cannot_name(self.max_placeholder_universe_reached)
208-
|| self.placeholder_reaches_placeholder()
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 {
231+
universe: max_reached_universe,
232+
rvid: belonging_to_rvid,
233+
} = 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();
252+
253+
if sccs.scc(min_reachable) != sccs.scc(self.representative) {
254+
return Some(min_reachable);
255+
}
256+
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+
}
268+
269+
None
209270
}
210271
}
211272

@@ -859,7 +920,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
859920

860921
// Otherwise, there can be no placeholder in `b` with a too high
861922
// universe index to name from `a`.
862-
a_universe.can_name(b_annotation.max_placeholder_universe_reached)
923+
if let ReachablePlaceholder::Placeholder { universe, .. } =
924+
b_annotation.max_universe_placeholder_reached
925+
{
926+
a_universe.can_name(universe)
927+
} else {
928+
true
929+
}
863930
}
864931

865932
/// Once regions have been propagated, this method is used to see
@@ -1697,65 +1764,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
16971764
}
16981765
}
16991766

1700-
/// We have a constraint `fr1: fr2` that is not satisfied, where
1701-
/// `fr2` represents some universal region. Here, `r` is some
1702-
/// region where we know that `fr1: r` and this function has the
1703-
/// job of determining whether `r` is "to blame" for the fact that
1704-
/// `fr1: fr2` is required.
1705-
///
1706-
/// This is true under two conditions:
1707-
///
1708-
/// - `r == fr2`
1709-
/// - `fr2` is `'static` and `r` is some placeholder in a universe
1710-
/// that cannot be named by `fr1`; in that case, we will require
1711-
/// that `fr1: 'static` because it is the only way to `fr1: r` to
1712-
/// be satisfied. (See `add_incompatible_universe`.)
1713-
pub(crate) fn provides_universal_region(
1714-
&self,
1715-
r: RegionVid,
1716-
fr1: RegionVid,
1717-
fr2: RegionVid,
1718-
) -> bool {
1719-
debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
1720-
let result = {
1721-
r == fr2 || {
1722-
fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
1723-
}
1724-
};
1725-
debug!("provides_universal_region: result = {:?}", result);
1726-
result
1727-
}
1728-
1729-
/// If `r2` represents a placeholder region, then this returns
1730-
/// `true` if `r1` cannot name that placeholder in its
1731-
/// value; otherwise, returns `false`.
1732-
pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
1733-
match self.definitions[r2].origin {
1734-
NllRegionVariableOrigin::Placeholder(placeholder) => {
1735-
let r1_universe = self.definitions[r1].universe;
1736-
debug!(
1737-
"cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}",
1738-
placeholder
1739-
);
1740-
r1_universe.cannot_name(placeholder.universe)
1741-
}
1742-
1743-
NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => {
1744-
false
1745-
}
1746-
}
1747-
}
1748-
17491767
/// Finds a good `ObligationCause` to blame for the fact that `fr1` outlives `fr2`.
17501768
pub(crate) fn find_outlives_blame_span(
17511769
&self,
17521770
fr1: RegionVid,
17531771
fr1_origin: NllRegionVariableOrigin,
17541772
fr2: RegionVid,
17551773
) -> (ConstraintCategory<'tcx>, ObligationCause<'tcx>) {
1756-
let BlameConstraint { category, cause, .. } = self
1757-
.best_blame_constraint(fr1, fr1_origin, |r| self.provides_universal_region(r, fr1, fr2))
1758-
.0;
1774+
let BlameConstraint { category, cause, .. } =
1775+
self.best_blame_constraint(fr1, fr1_origin, fr2).0;
17591776
(category, cause)
17601777
}
17611778

@@ -1837,10 +1854,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18371854

18381855
// This loop can be hot.
18391856
for constraint in outgoing_edges_from_graph {
1840-
if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
1841-
debug!("Ignoring illegal universe constraint: {constraint:?}");
1842-
continue;
1843-
}
18441857
handle_constraint(constraint);
18451858
}
18461859

@@ -1875,30 +1888,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18751888
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
18761889
self.liveness_constraints.is_live_at(r, location)
18771890
})
1878-
.or_else(|| {
1879-
// If we fail to find that, we may find some `r` such that
1880-
// `fr1: r` and `r` is a placeholder from some universe
1881-
// `fr1` cannot name. This would force `fr1` to be
1882-
// `'static`.
1883-
self.find_constraint_paths_between_regions(fr1, |r| {
1884-
self.cannot_name_placeholder(fr1, r)
1885-
})
1886-
})
1887-
.or_else(|| {
1888-
// If we fail to find THAT, it may be that `fr1` is a
1889-
// placeholder that cannot "fit" into its SCC. In that
1890-
// case, there should be some `r` where `fr1: r` and `fr1` is a
1891-
// placeholder that `r` cannot name. We can blame that
1892-
// edge.
1893-
//
1894-
// Remember that if `R1: R2`, then the universe of R1
1895-
// must be able to name the universe of R2, because R2 will
1896-
// be at least `'empty(Universe(R2))`, and `R1` must be at
1897-
// larger than that.
1898-
self.find_constraint_paths_between_regions(fr1, |r| {
1899-
self.cannot_name_placeholder(r, fr1)
1900-
})
1901-
})
19021891
.map(|(_path, r)| r)
19031892
.unwrap()
19041893
}
@@ -1931,21 +1920,46 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19311920
}
19321921

19331922
/// Tries to find the best constraint to blame for the fact that
1934-
/// `R: from_region`, where `R` is some region that meets
1935-
/// `target_test`. This works by following the constraint graph,
1923+
/// `to_region: from_region`.
1924+
/// This works by following the constraint graph,
19361925
/// creating a constraint path that forces `R` to outlive
19371926
/// `from_region`, and then finding the best choices within that
19381927
/// path to blame.
1939-
#[instrument(level = "debug", skip(self, target_test))]
1928+
#[instrument(level = "debug", skip(self))]
19401929
pub(crate) fn best_blame_constraint(
19411930
&self,
19421931
from_region: RegionVid,
19431932
from_region_origin: NllRegionVariableOrigin,
1944-
target_test: impl Fn(RegionVid) -> bool,
1933+
to_region: RegionVid,
1934+
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
1935+
let result = self.best_blame_constraint_(from_region, from_region_origin, to_region);
1936+
1937+
// We are trying to blame an outlives-static constraint added
1938+
// by an issue with placeholder regions. We figure out why the placeholder
1939+
// region issue happened instead.
1940+
if let ConstraintCategory::IllegalPlaceholder(offending_r) = result.0.category {
1941+
debug!("best_blame_constraint: placeholder issue caused by {offending_r:?}");
1942+
1943+
if to_region == offending_r {
1944+
// We do not want an infinite loop.
1945+
return result;
1946+
}
1947+
return self.best_blame_constraint(from_region, from_region_origin, offending_r);
1948+
}
1949+
1950+
result
1951+
}
1952+
1953+
#[instrument(level = "debug", skip(self))]
1954+
pub(crate) fn best_blame_constraint_(
1955+
&self,
1956+
from_region: RegionVid,
1957+
from_region_origin: NllRegionVariableOrigin,
1958+
to_region: RegionVid,
19451959
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
19461960
// Find all paths
19471961
let (path, target_region) =
1948-
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
1962+
self.find_constraint_paths_between_regions(from_region, |r| r == to_region).unwrap();
19491963
debug!(
19501964
"path={:#?}",
19511965
path.iter()

compiler/rustc_middle/src/mir/query.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisit
1313
use rustc_span::symbol::Symbol;
1414
use rustc_span::Span;
1515
use rustc_target::abi::{FieldIdx, VariantIdx};
16+
use rustc_type_ir::RegionVid;
1617
use smallvec::SmallVec;
1718

1819
use super::{ConstValue, SourceInfo};
@@ -271,8 +272,9 @@ pub enum ConstraintCategory<'tcx> {
271272
/// A constraint that doesn't correspond to anything the user sees.
272273
Internal,
273274

274-
/// An internal constraint derived from an illegal universe relation.
275-
IllegalUniverse,
275+
/// An internal constraint derived from an illegal placeholder relation
276+
/// to this region.
277+
IllegalPlaceholder(RegionVid),
276278
}
277279

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

0 commit comments

Comments
 (0)