diff --git a/Cargo.lock b/Cargo.lock index 9d6adadc5d4..e1234498f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3499,6 +3499,7 @@ dependencies = [ "bn254_blackbox_solver", "cfg-if", "chrono", + "function_name", "fxhash", "im", "iter-extended", diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index f9cc2e7b3bf..0e11175d0f7 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -39,6 +39,8 @@ similar-asserts.workspace = true tracing-test = "0.2.5" num-traits.workspace = true test-case.workspace = true +function_name = "0.3.0" +noirc_frontend = { workspace = true, features = ["test_utils"] } [features] bn254 = ["noirc_frontend/bn254"] diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 0fe528e4a51..f8f388e3a7e 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod context; mod program; +mod tests; mod value; use acvm::AcirField; diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs new file mode 100644 index 00000000000..50f913105fc --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs @@ -0,0 +1,109 @@ +#![cfg(test)] + +use crate::{errors::RuntimeError, ssa::opt::assert_normalized_ssa_equals}; + +use super::{Ssa, generate_ssa}; + +use function_name::named; + +use noirc_frontend::function_path; +use noirc_frontend::test_utils::{Expect, get_monomorphized}; + +fn get_initial_ssa(src: &str, test_path: &str) -> Result { + let program = match get_monomorphized(src, test_path, Expect::Success) { + Ok(program) => program, + Err(errors) => { + panic!( + "Expected program to have no errors before SSA generation, but found: {errors:?}" + ) + } + }; + + generate_ssa(program) +} + +#[named] +#[test] +fn assert() { + let assert_src = " + fn main(input: u32) { + assert(input == 5); + } + "; + let assert_ssa = get_initial_ssa(assert_src, function_path!()).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: u32): + v2 = eq v0, u32 5 + constrain v0 == u32 5 + return + } + "; + assert_normalized_ssa_equals(assert_ssa, expected); +} + +#[named] +#[test] +fn assert_eq() { + let assert_eq_src = " + fn main(input: u32) { + assert_eq(input, 5); + } + "; + + let assert_eq_ssa = get_initial_ssa(assert_eq_src, function_path!()).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: u32): + v2 = eq v0, u32 5 + constrain v0 == u32 5 + return + } + "; + // The SSA from assert_eq should match that from a regular assert checking for equality + // The expected SSA above should match that in the `assert()` test + assert_normalized_ssa_equals(assert_eq_ssa, expected); +} + +#[named] +#[test] +fn basic_loop() { + let src = " + fn main(sum_to_check: u32) { + let mut sum = 0; + for i in 0..4 { + sum = sum + i; + } + assert(sum_to_check == sum); + } + "; + + let ssa = get_initial_ssa(src, function_path!()).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: u32): + v2 = allocate -> &mut u32 + store u32 0 at v2 + jmp b1(u32 0) + b1(v4: u32): + v5 = lt v4, u32 4 + jmpif v5 then: b2, else: b3 + b2(): + v6 = load v2 -> u32 + v7 = add v6, v4 + store v7 at v2 + v9 = unchecked_add v4, u32 1 + jmp b1(v9) + b3(): + v10 = load v2 -> u32 + v11 = eq v0, v10 + constrain v0 == v10 + return + } + "; + + assert_normalized_ssa_equals(ssa, expected); +} diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index 3ae3667dc56..317c105c000 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -42,4 +42,5 @@ proptest-derive.workspace = true [features] bn254 = [] bls12_381 = [] +test_utils = [] nextest = [] diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index fdcdca35f6d..f4ad001c599 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -46,3 +46,5 @@ pub use hir_def::types::*; // Unit tests that involve all modules pub mod tests; +// Utility functions for easily compiling the frontend for tests in other crates +pub mod test_utils; diff --git a/compiler/noirc_frontend/src/monomorphization/tests.rs b/compiler/noirc_frontend/src/monomorphization/tests.rs index e4d73bdc1fc..5dfefaaadf5 100644 --- a/compiler/noirc_frontend/src/monomorphization/tests.rs +++ b/compiler/noirc_frontend/src/monomorphization/tests.rs @@ -2,30 +2,10 @@ use crate::{ check_monomorphization_error_using_features, elaborator::UnstableFeature, - tests::{Expect, get_program}, + test_utils::{Expect, get_monomorphized}, }; -use super::{ast::Program, errors::MonomorphizationError, monomorphize}; - -pub fn get_monomorphized( - src: &str, - test_path: &str, - expect: Expect, -) -> Result { - let (_parsed_module, mut context, errors) = get_program(src, test_path, expect); - assert!( - errors.iter().all(|err| !err.is_error()), - "Expected monomorphized program to have no errors before monomorphization, but found: {errors:?}" - ); - - let main = context - .get_main_function(context.root_crate_id()) - .unwrap_or_else(|| panic!("get_monomorphized: test program contains no 'main' function")); - - monomorphize(main, &mut context.def_interner, false) -} - -fn check_rewrite(src: &str, expected: &str, test_path: &str) { +pub(crate) fn check_rewrite(src: &str, expected: &str, test_path: &str) { let program = get_monomorphized(src, test_path, Expect::Success).unwrap(); assert!(format!("{}", program) == expected); } @@ -34,7 +14,7 @@ fn check_rewrite(src: &str, expected: &str, test_path: &str) { #[macro_export] macro_rules! get_monomorphized { ($src:expr, $expect:expr) => { - $crate::monomorphization::tests::get_monomorphized($src, $crate::function_path!(), $expect) + $crate::test_utils::get_monomorphized($src, $crate::function_path!(), $expect) }; } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index d40d7fdcc31..b0d66722dc6 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1997,7 +1997,7 @@ impl NodeInterner { /// This function is needed when creating a NodeInterner for testing so that calls /// to `get_operator_trait` do not panic when the stdlib isn't present. - #[cfg(test)] + #[cfg(any(test, feature = "test_utils"))] pub fn populate_dummy_operator_traits(&mut self) { let dummy_trait = TraitId(ModuleId::dummy_id()); self.infix_operator_traits.insert(BinaryOpKind::Add, dummy_trait); diff --git a/compiler/noirc_frontend/src/test_utils.rs b/compiler/noirc_frontend/src/test_utils.rs new file mode 100644 index 00000000000..3a2bf7ba68c --- /dev/null +++ b/compiler/noirc_frontend/src/test_utils.rs @@ -0,0 +1,294 @@ +//! This crate represents utility methods which can be useful for testing in other crates +//! which also desire to compile the frontend. +//! +//! This module is split out from the `tests` module and has an additional `test_utils` feature +//! as a module configured only for tests will not be accessible in other crates. +//! A crate that needs to use the methods in this module should add the `noirc_frontend` +//! crate as a dev dependency with the `test_utils` feature activated. +#![cfg(any(test, feature = "test_utils"))] + +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::path::Path; + +use crate::elaborator::FrontendOptions; + +use iter_extended::vecmap; +use noirc_errors::Location; + +use crate::hir::Context; +use crate::hir::def_collector::dc_crate::CompilationError; +use crate::hir::def_collector::dc_crate::DefCollector; +use crate::hir::def_map::CrateDefMap; +use crate::hir::def_map::ModuleData; +use crate::parser::{ItemKind, ParserErrorReason}; +use crate::token::SecondaryAttribute; +use crate::{ParsedModule, parse_program}; +use fm::FileManager; + +use crate::monomorphization::{ast::Program, errors::MonomorphizationError, monomorphize}; + +pub fn get_monomorphized( + src: &str, + test_path: &str, + expect: Expect, +) -> Result { + let (_parsed_module, mut context, errors) = get_program(src, test_path, expect); + assert!( + errors.iter().all(|err| !err.is_error()), + "Expected monomorphized program to have no errors before monomorphization, but found: {errors:?}" + ); + + let main = context + .get_main_function(context.root_crate_id()) + .unwrap_or_else(|| panic!("get_monomorphized: test program contains no 'main' function")); + + monomorphize(main, &mut context.def_interner, false) +} + +pub(crate) fn has_parser_error(errors: &[CompilationError]) -> bool { + errors.iter().any(|e| matches!(e, CompilationError::ParseError(_))) +} + +pub(crate) fn remove_experimental_warnings(errors: &mut Vec) { + errors.retain(|error| match error { + CompilationError::ParseError(error) => { + !matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(..))) + } + _ => true, + }); +} + +pub(crate) fn get_program<'a, 'b>( + src: &'a str, + test_path: &'b str, + expect: Expect, +) -> (ParsedModule, Context<'a, 'b>, Vec) { + let allow_parser_errors = false; + get_program_with_options( + src, + test_path, + expect, + allow_parser_errors, + FrontendOptions::test_default(), + ) +} + +pub enum Expect { + Bug, + Success, + Error, +} + +/// Compile a program. +/// +/// The stdlib is not available for these snippets. +pub(crate) fn get_program_with_options( + src: &str, + test_path: &str, + expect: Expect, + allow_parser_errors: bool, + options: FrontendOptions, +) -> (ParsedModule, Context<'static, 'static>, Vec) { + let root = std::path::Path::new("/"); + let mut fm = FileManager::new(root); + let root_file_id = fm.add_file_with_source(Path::new("test_file"), src.to_string()).unwrap(); + let mut context = Context::new(fm, Default::default()); + + context.def_interner.populate_dummy_operator_traits(); + let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + + let (program, parser_errors) = parse_program(src, root_file_id); + let mut errors = vecmap(parser_errors, |e| e.into()); + remove_experimental_warnings(&mut errors); + + if allow_parser_errors || !has_parser_error(&errors) { + let inner_attributes: Vec = program + .items + .iter() + .filter_map(|item| { + if let ItemKind::InnerAttribute(attribute) = &item.kind { + Some(attribute.clone()) + } else { + None + } + }) + .collect(); + + let location = Location::new(Default::default(), root_file_id); + let root_module = ModuleData::new( + None, + location, + Vec::new(), + inner_attributes.clone(), + false, // is contract + false, // is struct + ); + + let def_map = CrateDefMap::new(root_crate_id, root_module); + + // Now we want to populate the CrateDefMap using the DefCollector + errors.extend(DefCollector::collect_crate_and_dependencies( + def_map, + &mut context, + program.clone().into_sorted(), + root_file_id, + options, + )); + } + + emit_compile_test(test_path, src, expect); + (program, context, errors) +} + +// if the "nextest" feature is enabled, this will panic instead of emitting a test crate +fn emit_compile_test(test_path: &str, src: &str, mut expect: Expect) { + let package_name = test_path.replace("::", "_"); + let skipped_tests = [ + // skip ~2.4k name_shadowing tests + "name_shadowing_", + // TODO(https://github.com/noir-lang/noir/issues/7763) + "unconditional_recursion_fail_", + "unconditional_recursion_pass_", + // TODO(https://github.com/noir-lang/noir/issues/7783): array type fails to resolve when + // compiled + "traits_calls_trait_method_using_struct_name_when_multiple_impls_exist", + // TODO(https://github.com/noir-lang/noir/issues/7766): trait generic that passes + // frontend test fails to resolve with nargo + "turbofish_numeric_generic_nested_", + ]; + if skipped_tests.iter().any(|skipped_test_name| package_name.contains(skipped_test_name)) { + return; + } + + // in these cases, we expect a warning when 'check_errors' or similar is used + let error_to_warn_cases = [ + "cast_256_to_u8_size_checks", + "enums_errors_on_unspecified_unstable_enum", + "immutable_references_without_ownership_feature", + "imports_warns_on_use_of_private_exported_item", + "metaprogramming_does_not_fail_to_parse_macro_on_parser_warning", + "resolve_unused_var", + "struct_array_len", + "unused_items_errors_on_unused_private_import", + "unused_items_errors_on_unused_pub_crate_import", + "unused_items_errors_on_unused_struct", + "unused_items_errors_on_unused_trait", + "unused_items_errors_on_unused_type_alias", + "unused_items_warns_on_unused_global", + "visibility_warns_if_calling_private_struct_method", + "warns_on_nested_unsafe", + "warns_on_unneeded_unsafe", + // TODO(https://github.com/noir-lang/noir/issues/6932): these will be hard errors + "visibility_error_when_accessing_private_struct_field", + "visibility_error_when_using_private_struct_field_in_constructor", + "visibility_error_when_using_private_struct_field_in_struct_pattern", + "visibility_errors_if_accessing_private_struct_member_inside_comptime_context", + "visibility_errors_if_accessing_private_struct_member_inside_function_generated_at_comptime", + "visibility_errors_if_trying_to_access_public_function_inside_private_module", + "visibility_errors_once_on_unused_import_that_is_not_accessible", + // TODO(https://github.com/noir-lang/noir/issues/7795): these will be hard errors + "indexing_array_with_non_u32_on_lvalue_produces_a_warning", + "indexing_array_with_non_u32_produces_a_warning", + ]; + if let Expect::Error = expect { + if error_to_warn_cases + .iter() + .any(|error_to_warn_case| package_name.contains(error_to_warn_case)) + { + expect = Expect::Success; + } + } + + let error_to_bug_cases = ["cast_negative_one_to_u8_size_checks"]; + if let Expect::Success = expect { + if error_to_bug_cases + .iter() + .any(|error_to_bug_case| package_name.contains(error_to_bug_case)) + { + expect = Expect::Bug; + } + } + + // "compiler/noirc_frontend" + let noirc_frontend_path = Path::new(std::env!("CARGO_MANIFEST_DIR")); + let noir_root_path = noirc_frontend_path + .parent() + .expect("expected 'noirc_frontend' to be in 'compiler'") + .parent() + .expect("expected 'compiler' to be in the noir root"); + let test_programs_path = noir_root_path.join("test_programs"); + + let tests_dir_name = match expect { + Expect::Bug => "compile_success_with_bug", + Expect::Success => "compile_success_no_bug", + Expect::Error => "compile_failure", + }; + let tests_dir = test_programs_path.join(tests_dir_name); + let crate_path = tests_dir.join(&package_name); + let nargo_toml_path = crate_path.join("Nargo.toml"); + let src_hash_path = crate_path.join("src_hash.txt"); + let src_path = crate_path.join("src"); + let main_nr_path = src_path.join("main.nr"); + + // hash `src` + let mut hasher = DefaultHasher::new(); + src.hash(&mut hasher); + let new_hash = hasher.finish().to_string(); + + if crate_path.is_dir() && src_hash_path.is_file() { + let current_hash = + std::fs::read_to_string(&src_hash_path).expect("Unable to read src_hash.txt"); + // if out of date, update main.nr and hash file + if current_hash != new_hash { + if cfg!(feature = "nextest") { + panic!( + "test generated from frontend unit test {test_path} is out of date: run `cargo test` to update" + ); + } + std::fs::write(main_nr_path, src).expect("Unable to write test file"); + std::fs::write(src_hash_path, new_hash).expect("Unable to write src_hash.txt file"); + } + } else { + if cfg!(feature = "nextest") { + panic!( + "new test generated from frontend unit test {test_path}: run `cargo test` to generate" + ); + } + + // create missing dir's + std::fs::create_dir_all(&crate_path).unwrap_or_else(|_| { + panic!("expected to be able to create the directory {}", crate_path.display()) + }); + std::fs::create_dir_all(&src_path).unwrap_or_else(|_| { + panic!("expected to be able to create the directory {}", src_path.display()) + }); + + let package_type = "bin"; // nargo::package::PackageType::Binary; + let toml_contents = format!( + r#" + [package] + name = "{package_name}" + type = "{package_type}" + authors = [""] + + [dependencies]"# + ); + + std::fs::write(&nargo_toml_path, toml_contents).unwrap_or_else(|_| { + panic!("Unable to write Nargo.toml to {}", nargo_toml_path.display()) + }); + std::fs::write(&main_nr_path, src) + .unwrap_or_else(|_| panic!("Unable to write test file to {}", main_nr_path.display())); + std::fs::write(&src_hash_path, new_hash).unwrap_or_else(|_| { + panic!("Unable to write src_hash.txt file to {}", src_hash_path.display()) + }); + } +} + +// NOTE: this will fail in CI when called twice within one test: test names must be unique +#[macro_export] +macro_rules! function_path { + () => { + std::concat!(std::module_path!(), "::", function_name!(),) + }; +} diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 7a9b8736bcc..875ac396d39 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -17,58 +17,23 @@ mod visibility; // what we should do is have test cases which are passed to a test harness // A test harness will allow for more expressive and readable tests use std::collections::HashMap; -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::path::Path; use ::function_name::named; use crate::elaborator::{FrontendOptions, UnstableFeature}; +use crate::function_path; +use crate::test_utils::{Expect, get_program, get_program_with_options}; -use iter_extended::vecmap; use noirc_errors::reporter::report_all; -use noirc_errors::{CustomDiagnostic, Location, Span}; +use noirc_errors::{CustomDiagnostic, Span}; use crate::hir::Context; use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_map::ModuleData; use crate::node_interner::{NodeInterner, StmtId}; -use crate::hir::def_collector::dc_crate::DefCollector; -use crate::hir::def_map::CrateDefMap; +use crate::ParsedModule; use crate::hir_def::expr::HirExpression; use crate::hir_def::stmt::HirStatement; -use crate::parser::{ItemKind, ParserErrorReason}; -use crate::token::SecondaryAttribute; -use crate::{ParsedModule, parse_program}; -use fm::FileManager; - -pub(crate) fn has_parser_error(errors: &[CompilationError]) -> bool { - errors.iter().any(|e| matches!(e, CompilationError::ParseError(_))) -} - -pub(crate) fn remove_experimental_warnings(errors: &mut Vec) { - errors.retain(|error| match error { - CompilationError::ParseError(error) => { - !matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(..))) - } - _ => true, - }); -} - -pub(crate) fn get_program<'a, 'b>( - src: &'a str, - test_path: &'b str, - expect: Expect, -) -> (ParsedModule, Context<'a, 'b>, Vec) { - let allow_parser_errors = false; - get_program_with_options( - src, - test_path, - expect, - allow_parser_errors, - FrontendOptions::test_default(), - ) -} pub(crate) fn get_program_using_features( src: &str, @@ -82,222 +47,10 @@ pub(crate) fn get_program_using_features( get_program_with_options(src, test_path, expect, allow_parser_errors, options) } -/// Compile a program. -/// -/// The stdlib is not available for these snippets. -pub(crate) fn get_program_with_options( - src: &str, - test_path: &str, - expect: Expect, - allow_parser_errors: bool, - options: FrontendOptions, -) -> (ParsedModule, Context<'static, 'static>, Vec) { - let root = std::path::Path::new("/"); - let mut fm = FileManager::new(root); - let root_file_id = fm.add_file_with_source(Path::new("test_file"), src.to_string()).unwrap(); - let mut context = Context::new(fm, Default::default()); - - context.def_interner.populate_dummy_operator_traits(); - let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - - let (program, parser_errors) = parse_program(src, root_file_id); - let mut errors = vecmap(parser_errors, |e| e.into()); - remove_experimental_warnings(&mut errors); - - if allow_parser_errors || !has_parser_error(&errors) { - let inner_attributes: Vec = program - .items - .iter() - .filter_map(|item| { - if let ItemKind::InnerAttribute(attribute) = &item.kind { - Some(attribute.clone()) - } else { - None - } - }) - .collect(); - - let location = Location::new(Default::default(), root_file_id); - let root_module = ModuleData::new( - None, - location, - Vec::new(), - inner_attributes.clone(), - false, // is contract - false, // is struct - ); - - let def_map = CrateDefMap::new(root_crate_id, root_module); - - // Now we want to populate the CrateDefMap using the DefCollector - errors.extend(DefCollector::collect_crate_and_dependencies( - def_map, - &mut context, - program.clone().into_sorted(), - root_file_id, - options, - )); - } - - emit_compile_test(test_path, src, expect); - (program, context, errors) -} - pub(crate) fn get_program_errors(src: &str, test_path: &str) -> Vec { get_program(src, test_path, Expect::Error).2 } -pub enum Expect { - Bug, - Success, - Error, -} - -// if the "nextest" feature is enabled, this will panic instead of emitting a test crate -fn emit_compile_test(test_path: &str, src: &str, mut expect: Expect) { - let package_name = test_path.replace("::", "_"); - let skipped_tests = [ - // skip ~2.4k name_shadowing tests - "name_shadowing_", - // TODO(https://github.com/noir-lang/noir/issues/7763) - "unconditional_recursion_fail_", - "unconditional_recursion_pass_", - // TODO(https://github.com/noir-lang/noir/issues/7783): array type fails to resolve when - // compiled - "traits_calls_trait_method_using_struct_name_when_multiple_impls_exist", - // TODO(https://github.com/noir-lang/noir/issues/7766): trait generic that passes - // frontend test fails to resolve with nargo - "turbofish_numeric_generic_nested_", - ]; - if skipped_tests.iter().any(|skipped_test_name| package_name.contains(skipped_test_name)) { - return; - } - - // in these cases, we expect a warning when 'check_errors' or similar is used - let error_to_warn_cases = [ - "cast_256_to_u8_size_checks", - "enums_errors_on_unspecified_unstable_enum", - "immutable_references_without_ownership_feature", - "imports_warns_on_use_of_private_exported_item", - "metaprogramming_does_not_fail_to_parse_macro_on_parser_warning", - "resolve_unused_var", - "struct_array_len", - "unused_items_errors_on_unused_private_import", - "unused_items_errors_on_unused_pub_crate_import", - "unused_items_errors_on_unused_struct", - "unused_items_errors_on_unused_trait", - "unused_items_errors_on_unused_type_alias", - "unused_items_warns_on_unused_global", - "visibility_warns_if_calling_private_struct_method", - "warns_on_nested_unsafe", - "warns_on_unneeded_unsafe", - // TODO(https://github.com/noir-lang/noir/issues/6932): these will be hard errors - "visibility_error_when_accessing_private_struct_field", - "visibility_error_when_using_private_struct_field_in_constructor", - "visibility_error_when_using_private_struct_field_in_struct_pattern", - "visibility_errors_if_accessing_private_struct_member_inside_comptime_context", - "visibility_errors_if_accessing_private_struct_member_inside_function_generated_at_comptime", - "visibility_errors_if_trying_to_access_public_function_inside_private_module", - "visibility_errors_once_on_unused_import_that_is_not_accessible", - // TODO(https://github.com/noir-lang/noir/issues/7795): these will be hard errors - "indexing_array_with_non_u32_on_lvalue_produces_a_warning", - "indexing_array_with_non_u32_produces_a_warning", - ]; - if let Expect::Error = expect { - if error_to_warn_cases - .iter() - .any(|error_to_warn_case| package_name.contains(error_to_warn_case)) - { - expect = Expect::Success; - } - } - - let error_to_bug_cases = ["cast_negative_one_to_u8_size_checks"]; - if let Expect::Success = expect { - if error_to_bug_cases - .iter() - .any(|error_to_bug_case| package_name.contains(error_to_bug_case)) - { - expect = Expect::Bug; - } - } - - // "compiler/noirc_frontend" - let noirc_frontend_path = Path::new(std::env!("CARGO_MANIFEST_DIR")); - let noir_root_path = noirc_frontend_path - .parent() - .expect("expected 'noirc_frontend' to be in 'compiler'") - .parent() - .expect("expected 'compiler' to be in the noir root"); - let test_programs_path = noir_root_path.join("test_programs"); - - let tests_dir_name = match expect { - Expect::Bug => "compile_success_with_bug", - Expect::Success => "compile_success_no_bug", - Expect::Error => "compile_failure", - }; - let tests_dir = test_programs_path.join(tests_dir_name); - let crate_path = tests_dir.join(&package_name); - let nargo_toml_path = crate_path.join("Nargo.toml"); - let src_hash_path = crate_path.join("src_hash.txt"); - let src_path = crate_path.join("src"); - let main_nr_path = src_path.join("main.nr"); - - // hash `src` - let mut hasher = DefaultHasher::new(); - src.hash(&mut hasher); - let new_hash = hasher.finish().to_string(); - - if crate_path.is_dir() && src_hash_path.is_file() { - let current_hash = - std::fs::read_to_string(&src_hash_path).expect("Unable to read src_hash.txt"); - // if out of date, update main.nr and hash file - if current_hash != new_hash { - if cfg!(feature = "nextest") { - panic!( - "test generated from frontend unit test {test_path} is out of date: run `cargo test` to update" - ); - } - std::fs::write(main_nr_path, src).expect("Unable to write test file"); - std::fs::write(src_hash_path, new_hash).expect("Unable to write src_hash.txt file"); - } - } else { - if cfg!(feature = "nextest") { - panic!( - "new test generated from frontend unit test {test_path}: run `cargo test` to generate" - ); - } - - // create missing dir's - std::fs::create_dir_all(&crate_path).unwrap_or_else(|_| { - panic!("expected to be able to create the directory {}", crate_path.display()) - }); - std::fs::create_dir_all(&src_path).unwrap_or_else(|_| { - panic!("expected to be able to create the directory {}", src_path.display()) - }); - - let package_type = "bin"; // nargo::package::PackageType::Binary; - let toml_contents = format!( - r#" - [package] - name = "{package_name}" - type = "{package_type}" - authors = [""] - - [dependencies]"# - ); - - std::fs::write(&nargo_toml_path, toml_contents).unwrap_or_else(|_| { - panic!("Unable to write Nargo.toml to {}", nargo_toml_path.display()) - }); - std::fs::write(&main_nr_path, src) - .unwrap_or_else(|_| panic!("Unable to write test file to {}", main_nr_path.display())); - std::fs::write(&src_hash_path, new_hash).unwrap_or_else(|_| { - panic!("Unable to write src_hash.txt file to {}", src_hash_path.display()) - }); - } -} - fn assert_no_errors(src: &str, test_path: &str) { let (_, context, errors) = get_program(src, test_path, Expect::Success); if !errors.is_empty() { @@ -532,14 +285,6 @@ fn get_error_line_span_and_message( Some((span, error)) } -// NOTE: this will fail in CI when called twice within one test: test names must be unique -#[macro_export] -macro_rules! function_path { - () => { - std::concat!(std::module_path!(), "::", function_name!(),) - }; -} - // NOTE: this will fail in CI when called twice within one test: test names must be unique #[macro_export] macro_rules! get_program { diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/Nargo.toml b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/Nargo.toml new file mode 100644 index 00000000000..3a208e21208 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_evaluator_ssa_ssa_gen_tests_assert" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/src/main.nr b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/src/main.nr new file mode 100644 index 00000000000..649232738a9 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/src/main.nr @@ -0,0 +1,5 @@ + + fn main(input: u32) { + assert(input == 5); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/src_hash.txt b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/src_hash.txt new file mode 100644 index 00000000000..ab6b15314ff --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert/src_hash.txt @@ -0,0 +1 @@ +5290189907834307294 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/Nargo.toml b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/Nargo.toml new file mode 100644 index 00000000000..ea789609826 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_evaluator_ssa_ssa_gen_tests_assert_eq" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/src/main.nr b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/src/main.nr new file mode 100644 index 00000000000..82cf6d0abbf --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/src/main.nr @@ -0,0 +1,5 @@ + + fn main(input: u32) { + assert_eq(input, 5); + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/src_hash.txt b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/src_hash.txt new file mode 100644 index 00000000000..3ababbfe85c --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_assert_eq/src_hash.txt @@ -0,0 +1 @@ +372471763183268859 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/Nargo.toml b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/Nargo.toml new file mode 100644 index 00000000000..9a75f3bbc83 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_evaluator_ssa_ssa_gen_tests_basic_loop" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/src/main.nr b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/src/main.nr new file mode 100644 index 00000000000..43cee1e38fa --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/src/main.nr @@ -0,0 +1,7 @@ +fn main(sum_to_check: u32) { + let mut sum = 0; + for i in 0..4 { + sum = sum + i; + } + assert(sum_to_check == sum); +} diff --git a/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/src_hash.txt b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/src_hash.txt new file mode 100644 index 00000000000..a408fb49ebe --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_evaluator_ssa_ssa_gen_tests_basic_loop/src_hash.txt @@ -0,0 +1 @@ +7769757192889123017 \ No newline at end of file diff --git a/test_programs/format.sh b/test_programs/format.sh index 9a9a212f11d..cff5925a7aa 100755 --- a/test_programs/format.sh +++ b/test_programs/format.sh @@ -16,7 +16,7 @@ function collect_dirs { for dir in $test_dirs; do # TODO(https://github.com/noir-lang/noir/issues/7835): example blocking issue # skip generated tests - if [[ "${dir}" =~ ^noirc_frontend_* ]]; then + if [[ "${dir}" =~ ^(noirc_frontend_*|noirc_evaluator_*) ]]; then continue fi