Stop using LinkedGraph in lexical_region_resolve

This commit is contained in:
Zalathar
2026-02-23 18:50:43 +11:00
parent 1ed488274b
commit 4b7a05eeee
2 changed files with 95 additions and 73 deletions
@@ -0,0 +1,68 @@
use rustc_index::IndexVec;
use rustc_type_ir::RegionVid;
use crate::infer::SubregionOrigin;
use crate::infer::region_constraints::{Constraint, ConstraintKind, RegionConstraintData};
/// Selects either out-edges or in-edges for [`IndexedConstraintEdges::adjacent_edges`].
#[derive(Clone, Copy, Debug)]
pub(super) enum EdgeDirection {
Out,
In,
}
/// Type alias for the pairs stored in [`RegionConstraintData::constraints`],
/// which we are indexing.
type ConstraintPair<'tcx> = (Constraint<'tcx>, SubregionOrigin<'tcx>);
/// An index from region variables to their corresponding constraint edges,
/// used on some error paths.
pub(super) struct IndexedConstraintEdges<'data, 'tcx> {
out_edges: IndexVec<RegionVid, Vec<&'data ConstraintPair<'tcx>>>,
in_edges: IndexVec<RegionVid, Vec<&'data ConstraintPair<'tcx>>>,
}
impl<'data, 'tcx> IndexedConstraintEdges<'data, 'tcx> {
pub(super) fn build_index(num_vars: usize, data: &'data RegionConstraintData<'tcx>) -> Self {
let mut out_edges = IndexVec::from_fn_n(|_| vec![], num_vars);
let mut in_edges = IndexVec::from_fn_n(|_| vec![], num_vars);
for pair @ (c, _) in &data.constraints {
// Only push a var out-edge for `VarSub...` constraints.
match c.kind {
ConstraintKind::VarSubVar | ConstraintKind::VarSubReg => {
out_edges[c.sub.as_var()].push(pair)
}
ConstraintKind::RegSubVar | ConstraintKind::RegSubReg => {}
}
}
// Index in-edges in reverse order, to match what current tests expect.
// (It's unclear whether this is important or not.)
for pair @ (c, _) in data.constraints.iter().rev() {
// Only push a var in-edge for `...SubVar` constraints.
match c.kind {
ConstraintKind::VarSubVar | ConstraintKind::RegSubVar => {
in_edges[c.sup.as_var()].push(pair)
}
ConstraintKind::VarSubReg | ConstraintKind::RegSubReg => {}
}
}
IndexedConstraintEdges { out_edges, in_edges }
}
/// Returns either the out-edges or in-edges of the specified region var,
/// as selected by `dir`.
pub(super) fn adjacent_edges(
&self,
region_vid: RegionVid,
dir: EdgeDirection,
) -> &[&'data ConstraintPair<'tcx>] {
let edges = match dir {
EdgeDirection::Out => &self.out_edges,
EdgeDirection::In => &self.in_edges,
};
&edges[region_vid]
}
}
@@ -3,9 +3,6 @@
use std::fmt;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::linked_graph::{
Direction, INCOMING, LinkedGraph, NodeIndex, OUTGOING,
};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::unord::UnordSet;
use rustc_index::{IndexSlice, IndexVec};
@@ -18,11 +15,14 @@
use tracing::{debug, instrument};
use super::outlives::test_type_match;
use crate::infer::lexical_region_resolve::indexed_edges::{EdgeDirection, IndexedConstraintEdges};
use crate::infer::region_constraints::{
Constraint, ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound,
ConstraintKind, GenericKind, RegionConstraintData, VarInfos, VerifyBound,
};
use crate::infer::{RegionRelations, RegionVariableOrigin, SubregionOrigin};
mod indexed_edges;
/// This function performs lexical region resolution given a complete
/// set of constraints and variable origins. It performs a fixed-point
/// iteration to find region values which satisfy all constraints,
@@ -118,8 +118,6 @@ struct RegionAndOrigin<'tcx> {
origin: SubregionOrigin<'tcx>,
}
type RegionGraph<'tcx> = LinkedGraph<(), Constraint<'tcx>>;
struct LexicalResolver<'cx, 'tcx> {
region_rels: &'cx RegionRelations<'cx, 'tcx>,
var_infos: VarInfos<'tcx>,
@@ -626,9 +624,8 @@ fn collect_var_errors(
// overlapping locations.
let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars());
// Only construct the graph when necessary, because it's moderately
// expensive.
let mut graph = None;
// Only construct the edge index when necessary, because it's moderately expensive.
let mut edges: Option<IndexedConstraintEdges<'_, 'tcx>> = None;
for (node_vid, value) in var_data.values.iter_enumerated() {
match *value {
@@ -662,56 +659,18 @@ fn collect_var_errors(
// influence the constraints on this value for
// richer diagnostics in `static_impl_trait`.
let g = graph.get_or_insert_with(|| self.construct_graph());
self.collect_error_for_expanding_node(g, &mut dup_vec, node_vid, errors);
let e = edges.get_or_insert_with(|| {
IndexedConstraintEdges::build_index(self.num_vars(), &self.data)
});
self.collect_error_for_expanding_node(e, &mut dup_vec, node_vid, errors);
}
}
}
}
fn construct_graph(&self) -> RegionGraph<'tcx> {
let num_vars = self.num_vars();
let mut graph = LinkedGraph::new();
for _ in 0..num_vars {
graph.add_node(());
}
// Issue #30438: two distinct dummy nodes, one for incoming
// edges (dummy_source) and another for outgoing edges
// (dummy_sink). In `dummy -> a -> b -> dummy`, using one
// dummy node leads one to think (erroneously) there exists a
// path from `b` to `a`. Two dummy nodes sidesteps the issue.
let dummy_source = graph.add_node(());
let dummy_sink = graph.add_node(());
for (c, _) in &self.data.constraints {
match c.kind {
ConstraintKind::VarSubVar => {
let sub_vid = c.sub.as_var();
let sup_vid = c.sup.as_var();
graph.add_edge(NodeIndex(sub_vid.index()), NodeIndex(sup_vid.index()), *c);
}
ConstraintKind::RegSubVar => {
graph.add_edge(dummy_source, NodeIndex(c.sup.as_var().index()), *c);
}
ConstraintKind::VarSubReg => {
graph.add_edge(NodeIndex(c.sub.as_var().index()), dummy_sink, *c);
}
ConstraintKind::RegSubReg => {
// this would be an edge from `dummy_source` to
// `dummy_sink`; just ignore it.
}
}
}
graph
}
fn collect_error_for_expanding_node(
&self,
graph: &RegionGraph<'tcx>,
edges: &IndexedConstraintEdges<'_, 'tcx>,
dup_vec: &mut IndexSlice<RegionVid, Option<RegionVid>>,
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>,
@@ -719,9 +678,9 @@ fn collect_error_for_expanding_node(
// Errors in expanding nodes result from a lower-bound that is
// not contained by an upper-bound.
let (mut lower_bounds, lower_vid_bounds, lower_dup) =
self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec));
self.collect_bounding_regions(edges, node_idx, EdgeDirection::In, Some(dup_vec));
let (mut upper_bounds, _, upper_dup) =
self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec));
self.collect_bounding_regions(edges, node_idx, EdgeDirection::Out, Some(dup_vec));
if lower_dup || upper_dup {
return;
@@ -829,9 +788,9 @@ fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 {
/// those returned by a previous call for another region.
fn collect_bounding_regions(
&self,
graph: &RegionGraph<'tcx>,
edges: &IndexedConstraintEdges<'_, 'tcx>,
orig_node_idx: RegionVid,
dir: Direction,
dir: EdgeDirection,
mut dup_vec: Option<&mut IndexSlice<RegionVid, Option<RegionVid>>>,
) -> (Vec<RegionAndOrigin<'tcx>>, FxHashSet<RegionVid>, bool) {
struct WalkState<'tcx> {
@@ -850,7 +809,7 @@ struct WalkState<'tcx> {
// to start off the process, walk the source node in the
// direction specified
process_edges(&self.data, &mut state, graph, orig_node_idx, dir);
process_edges(&mut state, edges, orig_node_idx, dir);
while let Some(node_idx) = state.stack.pop() {
// check whether we've visited this node on some previous walk
@@ -867,30 +826,25 @@ struct WalkState<'tcx> {
);
}
process_edges(&self.data, &mut state, graph, node_idx, dir);
process_edges(&mut state, edges, node_idx, dir);
}
let WalkState { result, dup_found, set, .. } = state;
return (result, set, dup_found);
fn process_edges<'tcx>(
this: &RegionConstraintData<'tcx>,
state: &mut WalkState<'tcx>,
graph: &RegionGraph<'tcx>,
edges: &IndexedConstraintEdges<'_, 'tcx>,
source_vid: RegionVid,
dir: Direction,
dir: EdgeDirection,
) {
debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir);
let source_node_index = NodeIndex(source_vid.index());
for (_, edge) in graph.adjacent_edges(source_node_index, dir) {
let get_origin =
|| this.constraints.iter().find(|(c, _)| *c == edge.data).unwrap().1.clone();
match edge.data.kind {
for (c, origin) in edges.adjacent_edges(source_vid, dir) {
match c.kind {
ConstraintKind::VarSubVar => {
let from_vid = edge.data.sub.as_var();
let to_vid = edge.data.sup.as_var();
let from_vid = c.sub.as_var();
let to_vid = c.sup.as_var();
let opp_vid = if from_vid == source_vid { to_vid } else { from_vid };
if state.set.insert(opp_vid) {
state.stack.push(opp_vid);
@@ -898,13 +852,13 @@ fn process_edges<'tcx>(
}
ConstraintKind::RegSubVar => {
let origin = get_origin();
state.result.push(RegionAndOrigin { region: edge.data.sub, origin });
let origin = origin.clone();
state.result.push(RegionAndOrigin { region: c.sub, origin });
}
ConstraintKind::VarSubReg => {
let origin = get_origin();
state.result.push(RegionAndOrigin { region: edge.data.sup, origin });
let origin = origin.clone();
state.result.push(RegionAndOrigin { region: c.sup, origin });
}
ConstraintKind::RegSubReg => panic!(