1use std::ops::{Index, IndexMut};
2
3use fxhash::FxHashMap;
4use petgraph::{
5 graph::DiGraph,
6 visit::EdgeRef,
7 Direction::{self, Incoming, Outgoing},
8};
9
10use crate::{
11 graph_idx,
12 items::{ENodeIdx, EqGivenIdx},
13 BoxSlice, NonMaxU32,
14};
15
16use super::{
17 analysis::reconnect::{BwdReachableAnalysis, ReachVisible, ReconnectAnalysis},
18 raw::{EdgeKind, NodeKind},
19 InstGraph, RawEdgeIndex, RawNodeIndex,
20};
21
22graph_idx!(visible_idx, VisibleNodeIndex, VisibleEdgeIndex, VisibleIx);
23
24#[derive(Default)]
25pub struct VisibleInstGraph {
26 pub graph: DiGraph<VisibleNode, VisibleEdge, VisibleIx>,
27 reverse: FxHashMap<RawNodeIndex, VisibleNodeIndex>,
28 pub generation: u32,
29}
30
31impl InstGraph {
32 pub fn to_visible(&self) -> VisibleInstGraph {
33 let mut reach = BwdReachableAnalysis::<ReachVisible>::default();
34 let bwd_vis_reachable = self.topo_analysis(&mut reach);
35 let mut reconnect = self.topo_analysis(&mut ReconnectAnalysis(bwd_vis_reachable));
36 let (mut nodes, mut edges) = (0, 0);
37 for (idx, data) in reconnect.iter_mut_enumerated() {
38 if !self.raw[idx].visible() {
39 data.above.clear();
40 continue;
41 }
42 nodes += 1;
43 edges += data.above.len();
44 }
45
46 let mut graph: DiGraph<VisibleNode, VisibleEdge, VisibleIx> =
47 DiGraph::with_capacity(nodes, edges);
48
49 for idx in self.raw.node_indices() {
50 if !self.raw[idx].visible() {
51 continue;
52 }
53 let hidden_parents = self
54 .raw
55 .neighbors_directed(idx, Incoming, &self.analysis.reach)
56 .count_hidden();
57 let hidden_children = self
58 .raw
59 .neighbors_directed(idx, Outgoing, &self.analysis.reach)
60 .count_hidden();
61 let v_node = VisibleNode {
62 idx,
63 hidden_parents: hidden_parents as u32,
64 hidden_children: hidden_children as u32,
65 };
66 graph.add_node(v_node);
67 }
68
69 let reverse: FxHashMap<_, _> = graph
70 .node_indices()
71 .map(VisibleNodeIndex)
72 .map(|idx| (graph[idx.0].idx, idx))
73 .collect();
74 let mut self_ = VisibleInstGraph {
75 graph,
76 reverse,
77 generation: self.raw.stats.generation,
78 };
79
80 for (i_idx, data) in reconnect.into_iter_enumerated() {
81 let Some(v_idx) = self_.reverse(i_idx) else {
82 assert!(data.above.is_empty());
83 continue;
84 };
85 let mut edges_to_add = Vec::new();
86 for (edge, path) in data.above {
87 let from_v = edge.from.visible;
88 let from_h = edge.from.hidden;
89 let v_from_v = self_.reverse(from_v).unwrap();
90 if edge.is_direct_visible() {
91 assert!(from_h.is_none() && !edge.indirect);
92 let old_len = edges_to_add.len();
93 for edge in self.raw.graph.edges_connecting(from_v.0, i_idx.0) {
94 let edge = RawEdgeIndex(edge.id());
95 edges_to_add.push((edge, v_from_v, Ok(edge), VisibleEdge::Direct(edge)));
96 }
97 if path.is_some() {
99 assert!(edges_to_add.len() == old_len + 1);
100 } else {
101 assert!(edges_to_add.len() > old_len + 1);
102 }
103 } else {
104 assert_ne!(from_v, edge.to_h);
105 let from = from_h
106 .map(|fh| RawEdgeIndex(self.raw.graph.find_edge(from_v.0, fh.0).unwrap()));
107 let to = RawEdgeIndex(self.raw.graph.find_edge(edge.to_h.0, i_idx.0).unwrap());
108 edges_to_add.push((
109 to,
110 v_from_v,
111 from.ok_or(()),
112 VisibleEdge::Indirect {
113 from,
114 to,
115 indirect: edge.indirect,
116 path,
117 },
118 ));
119 }
120 }
121 edges_to_add
122 .sort_unstable_by_key(|&(to_edge, from, from_edge, _)| (to_edge, from, from_edge));
123
124 for (_, from, _, edge) in edges_to_add {
125 self_.graph.add_edge(from.0, v_idx.0, edge);
126 }
127 }
128 self_
129 }
130}
131impl VisibleInstGraph {
132 pub fn contains(&self, i_idx: RawNodeIndex) -> bool {
134 self.reverse.contains_key(&i_idx)
135 }
136
137 pub(super) fn reverse(&self, i_idx: RawNodeIndex) -> Option<VisibleNodeIndex> {
138 self.reverse.get(&i_idx).copied()
139 }
140}
141
142impl Index<VisibleNodeIndex> for VisibleInstGraph {
143 type Output = VisibleNode;
144 fn index(&self, idx: VisibleNodeIndex) -> &Self::Output {
145 &self.graph[idx.0]
146 }
147}
148impl IndexMut<VisibleNodeIndex> for VisibleInstGraph {
149 fn index_mut(&mut self, idx: VisibleNodeIndex) -> &mut Self::Output {
150 &mut self.graph[idx.0]
151 }
152}
153impl Index<VisibleEdgeIndex> for VisibleInstGraph {
154 type Output = VisibleEdge;
155 fn index(&self, idx: VisibleEdgeIndex) -> &Self::Output {
156 &self.graph[idx.0]
157 }
158}
159impl IndexMut<VisibleEdgeIndex> for VisibleInstGraph {
160 fn index_mut(&mut self, idx: VisibleEdgeIndex) -> &mut Self::Output {
161 &mut self.graph[idx.0]
162 }
163}
164
165#[derive(Debug, Clone)]
166pub struct VisibleNode {
167 pub idx: RawNodeIndex,
168 pub hidden_parents: u32,
169 pub hidden_children: u32,
170}
171
172#[derive(Clone, PartialEq, Eq, Hash)]
173pub enum VisibleEdge {
174 Direct(RawEdgeIndex),
175 Indirect {
176 from: Option<RawEdgeIndex>,
177 to: RawEdgeIndex,
178 indirect: bool,
179 path: Option<BoxSlice<RawNodeIndex>>,
181 },
182}
183
184impl std::fmt::Debug for VisibleEdge {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 match self {
187 VisibleEdge::Direct(_) => write!(f, "direct edge"),
188 VisibleEdge::Indirect { .. } => write!(f, "indirect edge"),
189 }
190 }
191}
192
193impl VisibleEdge {
194 pub fn last(&self) -> RawEdgeIndex {
195 match *self {
196 VisibleEdge::Direct(e) => e,
197 VisibleEdge::Indirect { to, .. } => to,
198 }
199 }
200
201 pub fn is_indirect(&self) -> bool {
202 matches!(self, VisibleEdge::Indirect { indirect: true, .. })
203 }
204 pub fn kind(&self, graph: &InstGraph) -> VisibleEdgeKind {
205 match *self {
206 VisibleEdge::Direct(e) => VisibleEdgeKind::Direct(e, graph.raw[e]),
207 VisibleEdge::Indirect {
208 from, to, ref path, ..
209 } => {
210 let Some(f) = from else {
211 return VisibleEdgeKind::Unknown(from, to);
212 };
213 let Some(non_visible_between) = path else {
214 return VisibleEdgeKind::Unknown(from, to);
215 };
216
217 let get_kind = |n| {
218 let idx: RawNodeIndex = *non_visible_between.get(n)?;
219 Some(graph.raw[idx].kind())
220 };
221
222 match (graph.raw[f], graph.raw[to]) {
223 (EdgeKind::Yield, EdgeKind::Blame { pattern_term })
225 if non_visible_between.len() == 1 =>
226 {
227 VisibleEdgeKind::YieldBlame {
228 enode: get_kind(0).unwrap().enode().unwrap(),
229 pattern_term,
230 }
231 }
232 (EdgeKind::Yield, EdgeKind::TEqualitySimple { .. })
233 if non_visible_between.len() == 2 =>
234 {
235 VisibleEdgeKind::YieldEq(get_kind(1).unwrap().eq_given().unwrap())
236 }
237 (
238 EdgeKind::Yield,
239 EdgeKind::BlameEq {
240 pattern_term,
241 eq_order,
242 },
243 ) if non_visible_between.len() == 3 => {
244 let trans = graph
245 .raw
246 .graph
247 .edges_directed(non_visible_between[2].0, Direction::Incoming)
248 .count();
249 let given_eq = get_kind(1).unwrap().eq_given().unwrap();
250 if trans == 1 {
251 VisibleEdgeKind::YieldBlameEq {
252 given_eq,
253 pattern_term,
254 eq_order,
255 }
256 } else {
257 VisibleEdgeKind::YieldEqOther(given_eq)
258 }
259 }
260
261 (EdgeKind::EqualityFact, EdgeKind::TEqualitySimple { .. })
263 if non_visible_between.len() == 1 =>
264 {
265 VisibleEdgeKind::ENodeEq(get_kind(0).unwrap().eq_given().unwrap())
266 }
267 (
268 EdgeKind::EqualityFact,
269 EdgeKind::BlameEq {
270 pattern_term,
271 eq_order,
272 },
273 ) if non_visible_between.len() == 2 => {
274 let trans = graph
275 .raw
276 .graph
277 .edges_directed(non_visible_between[1].0, Direction::Incoming)
278 .count();
279 let given_eq = get_kind(0).unwrap().eq_given().unwrap();
280 if trans == 1 {
281 VisibleEdgeKind::ENodeBlameEq {
282 given_eq,
283 pattern_term,
284 eq_order,
285 }
286 } else {
287 VisibleEdgeKind::ENodeEqOther(given_eq)
288 }
289 }
290
291 _ => VisibleEdgeKind::Unknown(from, to),
292 }
293 }
294 }
295 }
296}
297
298#[derive(Clone)]
299pub enum VisibleEdgeKind {
300 Direct(RawEdgeIndex, EdgeKind),
301 YieldBlame {
303 enode: ENodeIdx,
304 pattern_term: u16,
305 },
306 YieldEq((EqGivenIdx, Option<NonMaxU32>)),
308 YieldBlameEq {
311 given_eq: (EqGivenIdx, Option<NonMaxU32>),
312 pattern_term: u16,
313 eq_order: u16,
314 },
315 YieldEqOther((EqGivenIdx, Option<NonMaxU32>)),
317
318 ENodeEq((EqGivenIdx, Option<NonMaxU32>)),
320 ENodeBlameEq {
322 given_eq: (EqGivenIdx, Option<NonMaxU32>),
323 pattern_term: u16,
324 eq_order: u16,
325 },
326 ENodeEqOther((EqGivenIdx, Option<NonMaxU32>)),
328
329 Unknown(Option<RawEdgeIndex>, RawEdgeIndex),
330}
331
332impl VisibleEdgeKind {
333 pub fn blame(&self, graph: &InstGraph) -> NodeKind {
334 use NodeKind::*;
335 match self {
336 VisibleEdgeKind::Direct(edge, _) | VisibleEdgeKind::Unknown(.., edge) => {
337 *graph.raw.graph[graph.raw.graph.edge_endpoints(edge.0).unwrap().0].kind()
338 }
339
340 VisibleEdgeKind::YieldEq(given_eq)
341 | VisibleEdgeKind::YieldBlameEq { given_eq, .. }
342 | VisibleEdgeKind::YieldEqOther(given_eq)
343 | VisibleEdgeKind::ENodeEq(given_eq)
344 | VisibleEdgeKind::ENodeBlameEq { given_eq, .. }
345 | VisibleEdgeKind::ENodeEqOther(given_eq) => GivenEquality(given_eq.0, given_eq.1),
346
347 VisibleEdgeKind::YieldBlame { enode, .. } => ENode(*enode),
348 }
349 }
350}