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

Commit ff6de88

Browse files
committed
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.
1 parent aa4a92e commit ff6de88

File tree

4 files changed

+109
-60
lines changed

4 files changed

+109
-60
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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
132132
// reaches another placeholder, add a requirement that it must
133133
// outlive `'static`.
134134
if let Some(offending_region) = annotation.placeholder_violation(&sccs) {
135+
assert!(
136+
annotation.representative != offending_region,
137+
"Attemtping to blame a constraint for itself!"
138+
);
135139
// Optimisation opportunity: this will add more constraints than
136140
// needed for correctness, since an SCC upstream of another with
137141
// a universe violation will "infect" its downstream SCCs to also

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(p) = constraint.category {
16+
return format!("b/c {p:?}");
17+
}
1518
match constraint.locations {
1619
Locations::All(_) => "All(...)".to_string(),
1720
Locations::Single(loc) => format!("{loc:?}"),

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 87 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,18 +1780,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17801780
}
17811781

17821782
/// Walks the graph of constraints (where `'a: 'b` is considered
1783-
/// an edge `'a -> 'b`) to find all paths from `from_region` to
1784-
/// `to_region`. The paths are accumulated into the vector
1785-
/// `results`. The paths are stored as a series of
1786-
/// `ConstraintIndex` values -- in other words, a list of *edges*.
1787-
///
1783+
/// an edge `'a -> 'b`) to find a path from `from_region` to
1784+
/// the first region `R` for which the predicate function
1785+
/// `target_test` returns `true`.
17881786
/// Returns: a series of constraints as well as the region `R`
17891787
/// that passed the target test.
1788+
/// If `include_static_outlives_all` is `true`, then the synthetic
1789+
/// outlives constraints `'static -> a` for every region `a` are
1790+
/// considered in the search, otherwise they are ignored.
17901791
#[instrument(skip(self, target_test), ret)]
1791-
pub(crate) fn find_constraint_paths_between_regions(
1792+
pub(crate) fn find_constraint_path_to(
17921793
&self,
17931794
from_region: RegionVid,
17941795
target_test: impl Fn(RegionVid) -> bool,
1796+
include_static_outlives_all: bool,
17951797
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
17961798
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
17971799
context[from_region] = Trace::StartRegion;
@@ -1804,7 +1806,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18041806

18051807
while let Some(r) = deque.pop_front() {
18061808
debug!(
1807-
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
1809+
"find_constraint_path_to: from_region={:?} r={:?} value={}",
18081810
from_region,
18091811
r,
18101812
self.region_value_str(r),
@@ -1840,7 +1842,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18401842

18411843
// A constraint like `'r: 'x` can come from our constraint
18421844
// graph.
1843-
let fr_static = self.universal_regions.fr_static;
1845+
let fr_static = if include_static_outlives_all {
1846+
Some(self.universal_regions.fr_static)
1847+
} else {
1848+
None
1849+
};
18441850
let outgoing_edges_from_graph =
18451851
self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
18461852

@@ -1886,11 +1892,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18861892
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
18871893
trace!(scc = ?self.constraint_sccs.scc(fr1));
18881894
trace!(universe = ?self.region_universe(fr1));
1889-
self.find_constraint_paths_between_regions(fr1, |r| {
1895+
self.find_constraint_path_to(fr1, |r| {
18901896
// First look for some `r` such that `fr1: r` and `r` is live at `location`
18911897
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
18921898
self.liveness_constraints.is_live_at(r, location)
1893-
})
1899+
},
1900+
true
1901+
)
18941902
.map(|(_path, r)| r)
18951903
.unwrap()
18961904
}
@@ -1922,6 +1930,66 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19221930
self.universal_regions.as_ref()
19231931
}
19241932

1933+
/// Find a path of outlives constraints from `from` to `to`,
1934+
/// taking placeholder blame constraints into account, e.g.
1935+
/// if there is a relationship where `r1` reaches `r2` and
1936+
/// r2 has a larger universe or if r1 and r2 both come from
1937+
/// placeholder regions.
1938+
///
1939+
/// Returns the path and the target region, which may or may
1940+
/// not be the original `to`. It panics if there is no such
1941+
/// path.
1942+
fn path_to_modulo_placeholders(
1943+
&self,
1944+
from: RegionVid,
1945+
to: RegionVid,
1946+
) -> (Vec<OutlivesConstraint<'tcx>>, RegionVid) {
1947+
let path = self.find_constraint_path_to(from, |r| r == to, true).unwrap().0;
1948+
1949+
// If we are looking for a path to 'static, and we are passing
1950+
// through a constraint synthesised from an illegal placeholder
1951+
// relation, redirect the search to the placeholder to blame.
1952+
if self.is_static(to) {
1953+
for constraint in path.iter() {
1954+
let ConstraintCategory::IllegalPlaceholder(culprit_r) = constraint.category else {
1955+
continue;
1956+
};
1957+
1958+
debug!("{culprit_r:?} is the reason {from:?}: 'static!");
1959+
// FIXME: think: this may be for transitive reasons and
1960+
// we may have to do this arbitrarily many times. Or may we?
1961+
return self.find_constraint_path_to(from, |r| r == culprit_r, false).unwrap();
1962+
}
1963+
}
1964+
// No funny business; just return the path!
1965+
(path, to)
1966+
}
1967+
1968+
/// Find interesting spans from bound placeholders' predicates
1969+
/// from a constraint path.
1970+
fn find_bound_region_predicate_span(
1971+
&self,
1972+
path: &[OutlivesConstraint<'_>],
1973+
) -> Vec<ExtraConstraintInfo> {
1974+
for constraint in path.iter() {
1975+
let outlived = constraint.sub;
1976+
let Some(origin) = self.var_infos.get(outlived) else {
1977+
continue;
1978+
};
1979+
let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin
1980+
else {
1981+
continue;
1982+
};
1983+
debug!(?constraint, ?p);
1984+
let ConstraintCategory::Predicate(span) = constraint.category else {
1985+
continue;
1986+
};
1987+
// We only want to point to one
1988+
return vec![ExtraConstraintInfo::PlaceholderFromPredicate(span)];
1989+
}
1990+
vec![]
1991+
}
1992+
19251993
/// Tries to find the best constraint to blame for the fact that
19261994
/// `to_region: from_region`.
19271995
/// This works by following the constraint graph,
@@ -1935,34 +2003,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19352003
from_region_origin: NllRegionVariableOrigin,
19362004
to_region: RegionVid,
19372005
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
1938-
let result = self.best_blame_constraint_(from_region, from_region_origin, to_region);
1939-
1940-
// We are trying to blame an outlives-static constraint added
1941-
// by an issue with placeholder regions. We figure out why the placeholder
1942-
// region issue happened instead.
1943-
if let ConstraintCategory::IllegalPlaceholder(offending_r) = result.0.category {
1944-
debug!("best_blame_constraint: placeholder issue caused by {offending_r:?}");
1945-
1946-
if to_region == offending_r {
1947-
// We do not want an infinite loop.
1948-
return result;
1949-
}
1950-
return self.best_blame_constraint(from_region, from_region_origin, offending_r);
1951-
}
2006+
assert!(from_region != to_region, "Trying to blame a region for itself!");
19522007

1953-
result
1954-
}
2008+
let (path, new_to_region) = self.path_to_modulo_placeholders(from_region, to_region);
19552009

1956-
#[instrument(level = "debug", skip(self))]
1957-
pub(crate) fn best_blame_constraint_(
1958-
&self,
1959-
from_region: RegionVid,
1960-
from_region_origin: NllRegionVariableOrigin,
1961-
to_region: RegionVid,
1962-
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
1963-
// Find all paths
1964-
let (path, target_region) =
1965-
self.find_constraint_paths_between_regions(from_region, |r| r == to_region).unwrap();
19662010
debug!(
19672011
"path={:#?}",
19682012
path.iter()
@@ -1975,24 +2019,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19752019
.collect::<Vec<_>>()
19762020
);
19772021

1978-
let mut extra_info = vec![];
1979-
for constraint in path.iter() {
1980-
let outlived = constraint.sub;
1981-
let Some(origin) = self.var_infos.get(outlived) else {
1982-
continue;
1983-
};
1984-
let RegionVariableOrigin::Nll(NllRegionVariableOrigin::Placeholder(p)) = origin.origin
1985-
else {
1986-
continue;
1987-
};
1988-
debug!(?constraint, ?p);
1989-
let ConstraintCategory::Predicate(span) = constraint.category else {
1990-
continue;
1991-
};
1992-
extra_info.push(ExtraConstraintInfo::PlaceholderFromPredicate(span));
1993-
// We only want to point to one
1994-
break;
1995-
}
2022+
let extra_info = self.find_bound_region_predicate_span(&path);
19962023

19972024
// We try to avoid reporting a `ConstraintCategory::Predicate` as our best constraint.
19982025
// Instead, we use it to produce an improved `ObligationCauseCode`.
@@ -2043,7 +2070,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
20432070
// most likely to be the point where the value escapes -- but
20442071
// we still want to screen for an "interesting" point to
20452072
// highlight (e.g., a call site or something).
2046-
let target_scc = self.constraint_sccs.scc(target_region);
2073+
let target_scc = self.constraint_sccs.scc(new_to_region);
20472074
let mut range = 0..path.len();
20482075

20492076
// As noted above, when reporting an error, there is typically a chain of constraints
@@ -2240,6 +2267,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
22402267
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
22412268
self.constraint_sccs.annotation(scc).representative
22422269
}
2270+
2271+
/// Returns true if `r` is `'static`.
2272+
fn is_static(&self, r: RegionVid) -> bool {
2273+
r == self.universal_regions.fr_static
2274+
}
22432275
}
22442276

22452277
impl<'tcx> RegionDefinition<'tcx> {

0 commit comments

Comments
 (0)