diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index ebe7de75d7d..c26bec9a5f1 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -42,6 +42,7 @@ fn main() -> Result<(), String> { generate_minimal_execution_success_tests(&mut test_file, &test_dir); generate_interpret_execution_success_tests(&mut test_file, &test_dir); + generate_interpret_execution_failure_tests(&mut test_file, &test_dir); generate_fuzzing_failure_tests(&mut test_file, &test_dir); @@ -120,9 +121,12 @@ const TESTS_WITH_EXPECTED_WARNINGS: [&str; 5] = [ /// `nargo interpret` ignored tests, either because they don't currently work or /// because they are too slow to run. -const IGNORED_INTERPRET_EXECUTION_TESTS: [&str; 1] = [ +const IGNORED_INTERPRET_EXECUTION_TESTS: [&str; 2] = [ // slow "regression_4709", + // Doesn't match Brillig, but the expected ref-count of 5 has comments which + // suggest it's not exactly clear why we get that exact value anyway. + "reference_counts_inliner_max", ]; /// `nargo execute --minimal-ssa` ignored tests @@ -737,6 +741,32 @@ fn generate_interpret_execution_success_tests(test_file: &mut File, test_data_di writeln!(test_file, "}}").unwrap(); } +fn generate_interpret_execution_failure_tests(test_file: &mut File, test_data_dir: &Path) { + let test_type = "execution_failure"; + let test_cases = read_test_cases(test_data_dir, test_type); + + writeln!( + test_file, + "mod interpret_{test_type} {{ + use super::*; + " + ) + .unwrap(); + for (test_name, test_dir) in test_cases { + let test_dir = test_dir.display(); + + generate_test_cases( + test_file, + &test_name, + &test_dir, + "interpret", + "interpret_execution_failure(nargo);", + &MatrixConfig { vary_brillig: true, ..Default::default() }, + ); + } + writeln!(test_file, "}}").unwrap(); +} + /// Run integration tests with the `--minimal-ssa` option and check that the return /// value matches the expectations. This also enables `--force-brillig` since `--minimal-ssa` /// is only valid when all functions are unconstrained. diff --git a/tooling/nargo_cli/src/cli/interpret_cmd.rs b/tooling/nargo_cli/src/cli/interpret_cmd.rs index d8ff20b0f33..6c587e95826 100644 --- a/tooling/nargo_cli/src/cli/interpret_cmd.rs +++ b/tooling/nargo_cli/src/cli/interpret_cmd.rs @@ -72,6 +72,7 @@ pub(crate) fn run(args: InterpretCommand, workspace: Workspace) -> Result<(), Cl let opts = args.compile_options.as_ssa_options(PathBuf::new()); let ssa_passes = primary_passes(&opts); + let mut is_ok = true; for package in binary_packages { let ssa_options = @@ -132,7 +133,7 @@ pub(crate) fn run(args: InterpretCommand, workspace: Workspace) -> Result<(), Cl let file_manager = if args.compile_options.with_ssa_locations { Some(&file_manager) } else { None }; - print_and_interpret_ssa( + is_ok &= print_and_interpret_ssa( ssa_options, &args.ssa_pass, &mut ssa, @@ -155,7 +156,7 @@ pub(crate) fn run(args: InterpretCommand, workspace: Workspace) -> Result<(), Cl .run(ssa) .map_err(|e| CliError::Generic(format!("failed to run SSA pass {msg}: {e}")))?; - print_and_interpret_ssa( + is_ok &= print_and_interpret_ssa( ssa_options, &args.ssa_pass, &mut ssa, @@ -167,7 +168,11 @@ pub(crate) fn run(args: InterpretCommand, workspace: Workspace) -> Result<(), Cl )?; } } - Ok(()) + if is_ok { + Ok(()) + } else { + Err(CliError::Generic("The interpreter encountered an error on one or more passes.".into())) + } } /// Compile the source code into the monomorphized AST, which is one step before SSA passes. @@ -237,6 +242,12 @@ fn print_ssa(options: &SsaEvaluatorOptions, ssa: &mut Ssa, msg: &str, fm: Option } } +/// Interpret the SSA if it's part of the selected passes. +/// +/// The return value is: +/// * `Ok(true)` if the interpretation was successful, or it was skipped. +/// * `Ok(false)` if the interpreter returned an error, but we didn't have any expectation. +/// * `Err(_)` if the returned result did not match the expectation. fn interpret_ssa( passes_to_interpret: &[String], ssa: &Ssa, @@ -244,7 +255,7 @@ fn interpret_ssa( args: &[Value], return_value: &Option>, options: InterpreterOptions, -) -> Result<(), CliError> { +) -> Result { if passes_to_interpret.is_empty() || msg_matches(passes_to_interpret, msg) { // We need to give a fresh copy of arrays each time, because the shared structures are modified. let args = Value::snapshot_args(args); @@ -258,6 +269,7 @@ fn interpret_ssa( println!("--- Interpreter result after {msg}:\nErr({err})\n---"); } } + let is_ok = result.is_ok(); if let Some(return_value) = return_value { let result = result.expect("Expected a non-error result"); if &result != return_value { @@ -269,8 +281,10 @@ fn interpret_ssa( return Err(CliError::Generic(error)); } } + Ok(is_ok) + } else { + Ok(true) } - Ok(()) } #[allow(clippy::too_many_arguments)] @@ -283,7 +297,7 @@ fn print_and_interpret_ssa( return_value: &Option>, interpreter_options: InterpreterOptions, fm: Option<&FileManager>, -) -> Result<(), CliError> { +) -> Result { print_ssa(options, ssa, msg, fm); interpret_ssa(passes_to_interpret, ssa, msg, args, return_value, interpreter_options) } diff --git a/tooling/nargo_cli/tests/execute.rs b/tooling/nargo_cli/tests/execute.rs index cd0ba0f9510..8690325a82e 100644 --- a/tooling/nargo_cli/tests/execute.rs +++ b/tooling/nargo_cli/tests/execute.rs @@ -320,6 +320,10 @@ mod tests { nargo.assert().success(); } + fn interpret_execution_failure(mut nargo: Command) { + nargo.assert().failure(); + } + fn nargo_expand_execute(test_program_dir: PathBuf) { // First run `nargo execute` on the original code to get the output let mut nargo = Command::cargo_bin("nargo").unwrap(); diff --git a/tooling/ssa_cli/src/cli/interpret_cmd.rs b/tooling/ssa_cli/src/cli/interpret_cmd.rs index e17206915d9..4c270849985 100644 --- a/tooling/ssa_cli/src/cli/interpret_cmd.rs +++ b/tooling/ssa_cli/src/cli/interpret_cmd.rs @@ -73,6 +73,7 @@ pub(super) fn run(args: InterpretCommand, ssa: Ssa) -> eyre::Result<()> { println_to_stdout!("--- Interpreter result:\nErr({err})\n---"); } } + let is_ok = result.is_ok(); if let Some(return_value) = ssa_return { let return_value_as_string = vecmap(&return_value, ToString::to_string).join(", "); @@ -89,7 +90,7 @@ pub(super) fn run(args: InterpretCommand, ssa: Ssa) -> eyre::Result<()> { } } - Ok(()) + if is_ok { Ok(()) } else { bail!("The interpreter encountered an error.") } } /// Derive an ABI description from the SSA parameters.