]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_dataflow/src/framework/fmt.rs
Rollup merge of #105517 - pcc:process-panic-after-fork, r=davidtwco
[rust.git] / compiler / rustc_mir_dataflow / src / framework / fmt.rs
1 //! Custom formatting traits used when outputting Graphviz diagrams with the results of a dataflow
2 //! analysis.
3
4 use rustc_index::bit_set::{BitSet, ChunkedBitSet, HybridBitSet};
5 use rustc_index::vec::Idx;
6 use std::fmt;
7
8 /// An extension to `fmt::Debug` for data that can be better printed with some auxiliary data `C`.
9 pub trait DebugWithContext<C>: Eq + fmt::Debug {
10     fn fmt_with(&self, _ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11         fmt::Debug::fmt(self, f)
12     }
13
14     /// Print the difference between `self` and `old`.
15     ///
16     /// This should print nothing if `self == old`.
17     ///
18     /// `+` and `-` are typically used to indicate differences. However, these characters are
19     /// fairly common and may be needed to print a types representation. If using them to indicate
20     /// a diff, prefix them with the "Unit Separator"  control character (␟  U+001F).
21     fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22         if self == old {
23             return Ok(());
24         }
25
26         write!(f, "\u{001f}+")?;
27         self.fmt_with(ctxt, f)?;
28
29         if f.alternate() {
30             write!(f, "\n")?;
31         } else {
32             write!(f, "\t")?;
33         }
34
35         write!(f, "\u{001f}-")?;
36         old.fmt_with(ctxt, f)
37     }
38 }
39
40 /// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_with`.
41 pub struct DebugWithAdapter<'a, T, C> {
42     pub this: T,
43     pub ctxt: &'a C,
44 }
45
46 impl<T, C> fmt::Debug for DebugWithAdapter<'_, T, C>
47 where
48     T: DebugWithContext<C>,
49 {
50     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51         self.this.fmt_with(self.ctxt, f)
52     }
53 }
54
55 /// Implements `fmt::Debug` by deferring to `<T as DebugWithContext<C>>::fmt_diff_with`.
56 pub struct DebugDiffWithAdapter<'a, T, C> {
57     pub new: T,
58     pub old: T,
59     pub ctxt: &'a C,
60 }
61
62 impl<T, C> fmt::Debug for DebugDiffWithAdapter<'_, T, C>
63 where
64     T: DebugWithContext<C>,
65 {
66     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67         self.new.fmt_diff_with(&self.old, self.ctxt, f)
68     }
69 }
70
71 // Impls
72
73 impl<T, C> DebugWithContext<C> for BitSet<T>
74 where
75     T: Idx + DebugWithContext<C>,
76 {
77     fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78         f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
79     }
80
81     fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82         let size = self.domain_size();
83         assert_eq!(size, old.domain_size());
84
85         let mut set_in_self = HybridBitSet::new_empty(size);
86         let mut cleared_in_self = HybridBitSet::new_empty(size);
87
88         for i in (0..size).map(T::new) {
89             match (self.contains(i), old.contains(i)) {
90                 (true, false) => set_in_self.insert(i),
91                 (false, true) => cleared_in_self.insert(i),
92                 _ => continue,
93             };
94         }
95
96         fmt_diff(&set_in_self, &cleared_in_self, ctxt, f)
97     }
98 }
99
100 impl<T, C> DebugWithContext<C> for ChunkedBitSet<T>
101 where
102     T: Idx + DebugWithContext<C>,
103 {
104     fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105         f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
106     }
107
108     fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109         let size = self.domain_size();
110         assert_eq!(size, old.domain_size());
111
112         let mut set_in_self = HybridBitSet::new_empty(size);
113         let mut cleared_in_self = HybridBitSet::new_empty(size);
114
115         for i in (0..size).map(T::new) {
116             match (self.contains(i), old.contains(i)) {
117                 (true, false) => set_in_self.insert(i),
118                 (false, true) => cleared_in_self.insert(i),
119                 _ => continue,
120             };
121         }
122
123         fmt_diff(&set_in_self, &cleared_in_self, ctxt, f)
124     }
125 }
126
127 fn fmt_diff<T, C>(
128     inserted: &HybridBitSet<T>,
129     removed: &HybridBitSet<T>,
130     ctxt: &C,
131     f: &mut fmt::Formatter<'_>,
132 ) -> fmt::Result
133 where
134     T: Idx + DebugWithContext<C>,
135 {
136     let mut first = true;
137     for idx in inserted.iter() {
138         let delim = if first {
139             "\u{001f}+"
140         } else if f.alternate() {
141             "\n\u{001f}+"
142         } else {
143             ", "
144         };
145
146         write!(f, "{delim}")?;
147         idx.fmt_with(ctxt, f)?;
148         first = false;
149     }
150
151     if !f.alternate() {
152         first = true;
153         if !inserted.is_empty() && !removed.is_empty() {
154             write!(f, "\t")?;
155         }
156     }
157
158     for idx in removed.iter() {
159         let delim = if first {
160             "\u{001f}-"
161         } else if f.alternate() {
162             "\n\u{001f}-"
163         } else {
164             ", "
165         };
166
167         write!(f, "{delim}")?;
168         idx.fmt_with(ctxt, f)?;
169         first = false;
170     }
171
172     Ok(())
173 }
174
175 impl<T, C> DebugWithContext<C> for &'_ T
176 where
177     T: DebugWithContext<C>,
178 {
179     fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180         (*self).fmt_with(ctxt, f)
181     }
182
183     fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184         (*self).fmt_diff_with(*old, ctxt, f)
185     }
186 }
187
188 impl<C> DebugWithContext<C> for rustc_middle::mir::Local {}
189 impl<C> DebugWithContext<C> for crate::move_paths::InitIndex {}
190
191 impl<'tcx, C> DebugWithContext<C> for crate::move_paths::MovePathIndex
192 where
193     C: crate::move_paths::HasMoveData<'tcx>,
194 {
195     fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196         write!(f, "{}", ctxt.move_data().move_paths[*self])
197     }
198 }
199
200 impl<T, C> DebugWithContext<C> for crate::lattice::Dual<T>
201 where
202     T: DebugWithContext<C>,
203 {
204     fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205         (self.0).fmt_with(ctxt, f)
206     }
207
208     fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209         (self.0).fmt_diff_with(&old.0, ctxt, f)
210     }
211 }