Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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(crate) enum EdgeDirection {
Out,
In,
}

pub(crate) type ConstraintPair<'tcx> = (Constraint<'tcx>, SubregionOrigin<'tcx>);

/// An index from region variables to their corresponding constraint edges,
/// used on some error paths.
pub(crate) 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(crate) 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.iter() {
let mut push_in_edge = || out_edges[c.sub.as_var()].push(pair);
// Only push out-edges if the source is a var.
match c.kind {
ConstraintKind::VarSubVar => push_in_edge(),
ConstraintKind::RegSubVar => {}
ConstraintKind::VarSubReg => push_in_edge(),
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() {
let mut push_out_edge = || in_edges[c.sup.as_var()].push(pair);
// Only push in-edges if the target is a var.
match c.kind {
ConstraintKind::VarSubVar => push_out_edge(),
ConstraintKind::RegSubVar => push_out_edge(),
ConstraintKind::VarSubReg => {}
ConstraintKind::RegSubReg => {}
}
}

Self { out_edges, in_edges }
}

/// Returns either the out-edges or in-edges of the specified region var,
/// as selected by `dir`.
pub(crate) 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].as_slice()
}
}
100 changes: 27 additions & 73 deletions compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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,
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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<IndexedConstraintEdges<'_, 'tcx>> = None;

for (node_vid, value) in var_data.values.iter_enumerated() {
match *value {
Expand Down Expand Up @@ -662,66 +659,28 @@ 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<RegionVid, Option<RegionVid>>,
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'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;
Expand Down Expand Up @@ -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<RegionVid, Option<RegionVid>>>,
) -> (Vec<RegionAndOrigin<'tcx>>, FxHashSet<RegionVid>, bool) {
struct WalkState<'tcx> {
Expand All @@ -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
Expand All @@ -867,44 +826,39 @@ 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);
}
}

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!(
Expand Down
Loading