diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs new file mode 100644 index 0000000000000..ffc6e54f3cb06 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/indexed_edges.rs @@ -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>>, + in_edges: IndexVec>>, +} + +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] + } +} diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 5134b7b7ca8f1..e99dcd1ef15cb 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -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 rustc_span::Span; 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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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> = None; for (node_vid, value) in var_data.values.iter_enumerated() { match *value { @@ -662,56 +659,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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); - } - } - } - } - - 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. + 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); } } } - - graph } fn collect_error_for_expanding_node( &self, - graph: &RegionGraph<'tcx>, + edges: &IndexedConstraintEdges<'_, 'tcx>, dup_vec: &mut IndexSlice>, node_idx: RegionVid, errors: &mut Vec>, @@ -719,9 +678,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // 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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// 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>>, ) -> (Vec>, FxHashSet, bool) { struct WalkState<'tcx> { @@ -850,7 +809,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, '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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, '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 @@ impl<'cx, 'tcx> LexicalResolver<'cx, '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!(