1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! The plugin-seam trait and the per-run outcome it reports.
use cratePassError;
/// Whether a pass changed the unit on a given run.
///
/// A pass returns [`Outcome::Changed`] when it modified the unit and
/// [`Outcome::Unchanged`] when it left it untouched. The
/// [`PassManager`](crate::PassManager) uses this signal to decide whether a
/// fixpoint loop has settled (a full sweep with no `Changed` is a fixpoint) and
/// to build the [`Report`](crate::Report). Reporting `Unchanged` after mutating
/// the unit breaks fixpoint termination, so the value is taken at its word — keep
/// it honest.
/// A single transform or analysis over a unit of type `T` — the plugin seam.
///
/// Implement this trait to define a pass. The unit `T` is whatever the pass
/// rewrites: an intermediate representation, a single function, a module, an
/// abstract syntax tree, or a struct bundling an IR with the diagnostics and
/// analysis state the pass needs. The manager is generic over `T` and never
/// inspects it — a pass is the only thing trusted to read or mutate the unit.
///
/// A pass is registered with [`PassManager::add`](crate::PassManager::add) and
/// run in registration order. It must be `'static`: it may own state across
/// runs, but it may not borrow from outside the manager.
///
/// # Contract
///
/// - [`name`](Self::name) returns a stable, static identifier used in the
/// [`Report`](crate::Report) and in error context. It must not change between
/// runs of the same pass.
/// - [`run`](Self::run) transforms `unit` in place and reports an
/// [`Outcome`]: [`Changed`](Outcome::Changed) only when the unit was actually
/// modified, so a fixpoint loop can terminate. A pass that cannot proceed
/// returns a [`PassError`] instead of panicking.
///
/// # Examples
///
/// A pass that drops zero entries from a list, reporting whether it removed any:
///
/// ```
/// use pass_lang::{Outcome, Pass, PassError};
///
/// struct DropZeros;
///
/// impl Pass<Vec<i64>> for DropZeros {
/// fn name(&self) -> &'static str {
/// "drop-zeros"
/// }
///
/// fn run(&mut self, unit: &mut Vec<i64>) -> Result<Outcome, PassError> {
/// let before = unit.len();
/// unit.retain(|&x| x != 0);
/// Ok(Outcome::from_changed(unit.len() != before))
/// }
/// }
///
/// let mut unit = vec![0, 1, 0, 2];
/// assert_eq!(DropZeros.run(&mut unit).unwrap(), Outcome::Changed);
/// assert_eq!(unit, vec![1, 2]);
/// // Running again changes nothing.
/// assert_eq!(DropZeros.run(&mut unit).unwrap(), Outcome::Unchanged);
/// ```