Skip to content

Commit 6ab45ce

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 4214d06 commit 6ab45ce

File tree

4 files changed

+109
-60
lines changed

4 files changed

+109
-60
lines changed

Diff for: compiler/rustc_borrowck/src/constraints/graph.rs

+15-5
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<'s, 'tcx, D: ConstraintGraphDirection> {
135137
constraints: &'s 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<'s, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'s, 'tcx, D> {
@@ -153,8 +155,12 @@ impl<'s, 'tcx, D: ConstraintGraphDirection> Iterator for Edges<'s, '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<'s, 'tcx, D: ConstraintGraphDirection> RegionGraph<'s, 'tcx, D> {
194200
/// there exists a constraint `R: R1`.
195201
pub(crate) fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'s, '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
}

Diff for: compiler/rustc_borrowck/src/constraints/mod.rs

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

Diff for: compiler/rustc_borrowck/src/region_infer/graphviz.rs

+3
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:?}"),

Diff for: compiler/rustc_borrowck/src/region_infer/mod.rs

+87-55
Original file line numberDiff line numberDiff line change
@@ -1777,18 +1777,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17771777
}
17781778

17791779
/// Walks the graph of constraints (where `'a: 'b` is considered
1780-
/// an edge `'a -> 'b`) to find all paths from `from_region` to
1781-
/// `to_region`. The paths are accumulated into the vector
1782-
/// `results`. The paths are stored as a series of
1783-
/// `ConstraintIndex` values -- in other words, a list of *edges*.
1784-
///
1780+
/// an edge `'a -> 'b`) to find a path from `from_region` to
1781+
/// the first region `R` for which the predicate function
1782+
/// `target_test` returns `true`.
17851783
/// Returns: a series of constraints as well as the region `R`
17861784
/// that passed the target test.
1785+
/// If `include_static_outlives_all` is `true`, then the synthetic
1786+
/// outlives constraints `'static -> a` for every region `a` are
1787+
/// considered in the search, otherwise they are ignored.
17871788
#[instrument(skip(self, target_test), ret)]
1788-
pub(crate) fn find_constraint_paths_between_regions(
1789+
pub(crate) fn find_constraint_path_to(
17891790
&self,
17901791
from_region: RegionVid,
17911792
target_test: impl Fn(RegionVid) -> bool,
1793+
include_static_outlives_all: bool,
17921794
) -> Option<(Vec<OutlivesConstraint<'tcx>>, RegionVid)> {
17931795
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
17941796
context[from_region] = Trace::StartRegion;
@@ -1801,7 +1803,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18011803

18021804
while let Some(r) = deque.pop_front() {
18031805
debug!(
1804-
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
1806+
"find_constraint_path_to: from_region={:?} r={:?} value={}",
18051807
from_region,
18061808
r,
18071809
self.region_value_str(r),
@@ -1837,7 +1839,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18371839

18381840
// A constraint like `'r: 'x` can come from our constraint
18391841
// graph.
1840-
let fr_static = self.universal_regions.fr_static;
1842+
let fr_static = if include_static_outlives_all {
1843+
Some(self.universal_regions.fr_static)
1844+
} else {
1845+
None
1846+
};
18411847
let outgoing_edges_from_graph =
18421848
self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
18431849

@@ -1883,11 +1889,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18831889
pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
18841890
trace!(scc = ?self.constraint_sccs.scc(fr1));
18851891
trace!(universe = ?self.region_universe(fr1));
1886-
self.find_constraint_paths_between_regions(fr1, |r| {
1892+
self.find_constraint_path_to(fr1, |r| {
18871893
// First look for some `r` such that `fr1: r` and `r` is live at `location`
18881894
trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
18891895
self.liveness_constraints.is_live_at(r, location)
1890-
})
1896+
},
1897+
true
1898+
)
18911899
.map(|(_path, r)| r)
18921900
.unwrap()
18931901
}
@@ -1919,6 +1927,66 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19191927
self.universal_regions.as_ref()
19201928
}
19211929

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

1950-
result
1951-
}
2005+
let (path, new_to_region) = self.path_to_modulo_placeholders(from_region, to_region);
19522006

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,
1959-
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
1960-
// Find all paths
1961-
let (path, target_region) =
1962-
self.find_constraint_paths_between_regions(from_region, |r| r == to_region).unwrap();
19632007
debug!(
19642008
"path={:#?}",
19652009
path.iter()
@@ -1972,24 +2016,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19722016
.collect::<Vec<_>>()
19732017
);
19742018

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

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

20462073
// As noted above, when reporting an error, there is typically a chain of constraints
@@ -2237,6 +2264,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
22372264
fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
22382265
self.constraint_sccs.annotation(scc).representative
22392266
}
2267+
2268+
/// Returns true if `r` is `'static`.
2269+
fn is_static(&self, r: RegionVid) -> bool {
2270+
r == self.universal_regions.fr_static
2271+
}
22402272
}
22412273

22422274
impl<'tcx> RegionDefinition<'tcx> {

0 commit comments

Comments
 (0)