Skip to content

Commit e910ded

Browse files
authored
Merge pull request #29028 from ProvableHQ/IGI-111/multi-pass-test-framework
Explicit pass schedule in test framework
2 parents d151563 + 1a19aa4 commit e910ded

1 file changed

Lines changed: 135 additions & 54 deletions

File tree

compiler/passes/src/test_passes.rs

Lines changed: 135 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,37 @@ To add a new compiler pass, you need to update this file and create the test dir
3333
1. Add a new entry to the `compiler_passes!` table:
3434
3535
```rust
36-
(runner_name, PassStruct, (input), run_type_checking)
36+
(runner_name, [(PassStruct, input), ...])
3737
```
3838
3939
- `runner_name` – the function name for this pass runner (snake_case).
40-
- `PassStruct` – the compiler pass struct you are testing.
41-
- `input` – the argument to the pass (`()` if none, or a struct literal like `(SsaFormingInput { rename_defs: true })`).
42-
- `run_prelude` - whether to run setup passes such as type checking before this pass, you most likely want this to be true
40+
- `[(PassStruct, input), ...]` – a list of passes to run sequentially. Each entry is a tuple of `(pass_struct, input)`.
41+
- `input` – the argument to the pass. Can be `()` if none, or a struct literal like `(SsaFormingInput { rename_defs: true })`.
4342
44-
Example:
43+
Examples:
4544
4645
```rust
47-
(new_pass_runner, NewPass, (NewPassInput { option: true }), true),
46+
// Single pass with typical prelude
47+
(new_pass_runner, [
48+
(PathResolution, ()),
49+
(SymbolTableCreation, ()),
50+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
51+
(NewPass, (NewPassInput { option: true }))
52+
]),
53+
54+
// Multiple passes run sequentially
55+
(multi_pass_runner, [
56+
(PathResolution, ()),
57+
(SymbolTableCreation, ()),
58+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
59+
(FirstPass, ()),
60+
(SecondPass, (SecondPassInput { value: NetworkName::TestnetV0 }))
61+
]),
62+
63+
// Pass without prelude (if prelude not needed)
64+
(no_prelude_runner, [
65+
(SomePass, ())
66+
]),
4867
```
4968
5069
2. No other code needs to change — macros automatically generate:
@@ -88,38 +107,102 @@ use leo_span::{create_session_if_not_set_then, source_map::FileName, with_sessio
88107
use serial_test::serial;
89108

90109
/// Table of all compiler passes and their runner names.
91-
/// Each entry is a tuple of `(runner_name, pass_struct, input)`
92-
/// - `input` is the argument to the pass, can be `()` or a struct literal.
110+
/// Each entry is a tuple of `(runner_name, [(pass_struct, input), ...])`
111+
/// - `runner_name` – the function name for this pass runner (snake_case).
112+
/// - `[(pass_struct, input), ...]` – a list of passes to run sequentially. Each entry is a tuple of `(pass_struct, input)`.
113+
/// Include the prelude passes (PathResolution, SymbolTableCreation, TypeChecking) at the beginning if needed.
114+
/// - `input` – the argument to the pass. Can be `()` if none, or a struct literal like `(SsaFormingInput { rename_defs: true })`.
93115
macro_rules! compiler_passes {
94116
($macro:ident) => {
95117
$macro! {
96-
(common_subexpression_elimination_runner, CommonSubexpressionEliminating, (), true),
97-
(const_prop_unroll_and_morphing_runner, ConstPropUnrollAndMorphing, (TypeCheckingInput::new(NetworkName::TestnetV0)), true),
98-
(destructuring_runner, Destructuring, (), true),
99-
(dead_code_elimination_runner, DeadCodeEliminating, (), true),
100-
(flattening_runner, Flattening, (), true),
101-
(function_inlining_runner, FunctionInlining, (), true),
102-
(option_lowering_runner, OptionLowering, (TypeCheckingInput::new(NetworkName::TestnetV0)), true),
103-
(processing_async_runner, ProcessingAsync, (TypeCheckingInput::new(NetworkName::TestnetV0)), true),
104-
(processing_script_runner, ProcessingScript, (), true),
105-
(ssa_forming_runner, SsaForming, (SsaFormingInput { rename_defs: true }), true),
106-
(storage_lowering_runner, StorageLowering, (TypeCheckingInput::new(NetworkName::TestnetV0)), true),
107-
(write_transforming_runner, WriteTransforming, (), true),
108-
(remove_unreachable, RemoveUnreachable, (), false)
118+
(common_subexpression_elimination_runner, [
119+
(PathResolution, ()),
120+
(SymbolTableCreation, ()),
121+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
122+
(CommonSubexpressionEliminating, ())
123+
]),
124+
(const_prop_unroll_and_morphing_runner, [
125+
(PathResolution, ()),
126+
(SymbolTableCreation, ()),
127+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
128+
(ConstPropUnrollAndMorphing, (TypeCheckingInput::new(NetworkName::TestnetV0)))
129+
]),
130+
(destructuring_runner, [
131+
(PathResolution, ()),
132+
(SymbolTableCreation, ()),
133+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
134+
(Destructuring, ())
135+
]),
136+
(dead_code_elimination_runner, [
137+
(PathResolution, ()),
138+
(SymbolTableCreation, ()),
139+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
140+
(DeadCodeEliminating, ())
141+
]),
142+
(flattening_runner, [
143+
(PathResolution, ()),
144+
(SymbolTableCreation, ()),
145+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
146+
(Flattening, ())
147+
]),
148+
(function_inlining_runner, [
149+
(PathResolution, ()),
150+
(SymbolTableCreation, ()),
151+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
152+
(FunctionInlining, ())
153+
]),
154+
(option_lowering_runner, [
155+
(PathResolution, ()),
156+
(SymbolTableCreation, ()),
157+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
158+
(OptionLowering, (TypeCheckingInput::new(NetworkName::TestnetV0)))
159+
]),
160+
(processing_async_runner, [
161+
(PathResolution, ()),
162+
(SymbolTableCreation, ()),
163+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
164+
(ProcessingAsync, (TypeCheckingInput::new(NetworkName::TestnetV0)))
165+
]),
166+
(processing_script_runner, [
167+
(PathResolution, ()),
168+
(SymbolTableCreation, ()),
169+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
170+
(ProcessingScript, ())
171+
]),
172+
(ssa_forming_runner, [
173+
(PathResolution, ()),
174+
(SymbolTableCreation, ()),
175+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
176+
(SsaForming, (SsaFormingInput { rename_defs: true }))
177+
]),
178+
(storage_lowering_runner, [
179+
(PathResolution, ()),
180+
(SymbolTableCreation, ()),
181+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
182+
(StorageLowering, (TypeCheckingInput::new(NetworkName::TestnetV0)))
183+
]),
184+
(write_transforming_runner, [
185+
(PathResolution, ()),
186+
(SymbolTableCreation, ()),
187+
(TypeChecking, (TypeCheckingInput::new(NetworkName::TestnetV0))),
188+
(WriteTransforming, ())
189+
]),
190+
(remove_unreachable, [
191+
(RemoveUnreachable, ())
192+
])
109193
}
110194
};
111195
}
112196

113-
/// Macro to generate a single runner function for a compiler pass.
197+
/// Macro to generate a single runner function for compiler passes.
114198
///
115199
/// Each runner:
116200
/// - Sets up a BufferEmitter and Handler for error/warning reporting.
117201
/// - Parse the test into an AST.
118-
/// - Runs the first three fixed passes: PathResolution, SymbolTableCreation, TypeChecking.
119-
/// - Runs the specified compiler pass.
202+
/// - Runs the specified list of compiler passes sequentially.
120203
/// - Returns the resulting AST or formatted errors/warnings.
121204
macro_rules! make_runner {
122-
($runner_name:ident, $pass:ident, $input:expr, $run_prelude:expr) => {
205+
($runner_name:ident, [$(($pass:ident, $input:expr)),* $(,)?]) => {
123206
fn $runner_name(source: &str) -> String {
124207
let buf = BufferEmitter::new();
125208
let handler = Handler::new(buf.clone());
@@ -138,28 +221,12 @@ macro_rules! make_runner {
138221
Err(()) => return format!("{}{}", buf.extract_errs(), buf.extract_warnings()),
139222
};
140223

141-
// Run these three passes before the tested pass; they populate symbol & type tables,
142-
// which are required for the following compiler pass to function correctly.
143-
// Type checking may be disabled by argument
144-
if $run_prelude {
145-
if handler.extend_if_error(PathResolution::do_pass((), &mut state)).is_err() {
224+
// Run the specified passes sequentially
225+
$(
226+
if handler.extend_if_error($pass::do_pass($input, &mut state)).is_err() {
146227
return format!("{}{}", buf.extract_errs(), buf.extract_warnings());
147228
}
148-
if handler.extend_if_error(SymbolTableCreation::do_pass((), &mut state)).is_err() {
149-
return format!("{}{}", buf.extract_errs(), buf.extract_warnings());
150-
}
151-
if handler
152-
.extend_if_error(TypeChecking::do_pass(TypeCheckingInput::new(state.network), &mut state))
153-
.is_err()
154-
{
155-
return format!("{}{}", buf.extract_errs(), buf.extract_warnings());
156-
}
157-
}
158-
159-
// Run the specific pass
160-
if handler.extend_if_error($pass::do_pass($input, &mut state)).is_err() {
161-
return format!("{}{}", buf.extract_errs(), buf.extract_warnings());
162-
}
229+
)*
163230

164231
// Success: return AST with any warnings
165232
format!("{}{}", buf.extract_warnings(), state.ast.ast)
@@ -170,9 +237,9 @@ macro_rules! make_runner {
170237

171238
/// Macro to generate all runners from the compiler_passes table.
172239
macro_rules! make_all_runners {
173-
($(($runner:ident, $pass:ident, $input:tt, $run_prelude:tt)),* $(,)?) => {
240+
($(($runner:ident, $passes:tt)),* $(,)?) => {
174241
$(
175-
make_runner!($runner, $pass, $input, $run_prelude);
242+
make_runner!($runner, $passes);
176243
)*
177244
};
178245
}
@@ -182,23 +249,37 @@ compiler_passes!(make_all_runners);
182249
///
183250
/// Each test function:
184251
/// - Uses the runner function generated above.
185-
/// - Uses `leo_test_framework::run_tests` with a path derived from the pass struct name.
252+
/// - Uses `leo_test_framework::run_tests` with a path derived from the last pass struct name (the actual pass being tested).
186253
/// - Uses `paste::paste!` to safely concatenate identifiers.
187254
macro_rules! make_all_tests {
188-
($(($runner:ident, $pass:ident, $input:tt, $run_prelude:tt)),* $(,)?) => {
255+
($(($runner:ident, [$(($pass:ident, $input:tt)),* $(,)?])),* $(,)?) => {
189256
$(
190257
paste::paste! {
191258
#[test]
192259
#[serial]
193260
fn [<$runner _test>]() {
194-
// Automatically derive the snake_case directory name from the pass name
195-
leo_test_framework::run_tests(
196-
concat!("passes/", stringify!([<$pass:snake>])),
197-
$runner,
198-
);
261+
// Automatically derive the snake_case directory name from the last pass name (the actual pass being tested)
262+
// We need to extract the last pass from the list
263+
make_all_tests_inner!($runner, [$(($pass, $input)),*]);
199264
}
200265
}
201266
)*
202267
};
203268
}
269+
270+
/// Helper macro to extract the last pass name from the list.
271+
macro_rules! make_all_tests_inner {
272+
($runner:ident, [($pass:ident, $input:tt)]) => {
273+
paste::paste! {
274+
leo_test_framework::run_tests(
275+
concat!("passes/", stringify!([<$pass:snake>])),
276+
$runner,
277+
);
278+
}
279+
};
280+
($runner:ident, [($pass:ident, $input:tt), $(($rest_pass:ident, $rest_input:tt)),+ $(,)?]) => {
281+
make_all_tests_inner!($runner, [$(($rest_pass, $rest_input)),+]);
282+
};
283+
}
284+
204285
compiler_passes!(make_all_tests);

0 commit comments

Comments
 (0)