diff --git a/Cargo.lock b/Cargo.lock index e84901d48ae..0486220f416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2287,7 +2287,6 @@ dependencies = [ "fm", "getrandom", "gloo-utils", - "iter-extended", "log", "nargo", "noirc_driver", diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index ec569e0e182..1b627adb3e4 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -169,20 +169,27 @@ pub fn compile_main( } /// Run the frontend to check the crate for errors then compile all contracts if there were none -pub fn compile_contracts( +pub fn compile_contract( context: &mut Context, crate_id: CrateId, options: &CompileOptions, -) -> CompilationResult> { +) -> CompilationResult { let (_, warnings) = check_crate(context, crate_id, options.deny_warnings)?; // TODO: We probably want to error if contracts is empty let contracts = context.get_all_contracts(&crate_id); + let mut compiled_contracts = vec![]; let mut errors = warnings; + if contracts.len() > 1 { + let err = CustomDiagnostic::from_message("Packages are limited to a single contract") + .in_file(FileId::default()); + return Err(vec![err]); + }; + for contract in contracts { - match compile_contract(context, contract, options) { + match compile_contract_inner(context, contract, options) { Ok(contract) => compiled_contracts.push(contract), Err(mut more_errors) => errors.append(&mut more_errors), } @@ -191,19 +198,20 @@ pub fn compile_contracts( if has_errors(&errors, options.deny_warnings) { Err(errors) } else { + assert_eq!(compiled_contracts.len(), 1); + let compiled_contract = compiled_contracts.remove(0); + if options.print_acir { - for compiled_contract in &compiled_contracts { - for contract_function in &compiled_contract.functions { - println!( - "Compiled ACIR for {}::{} (unoptimized):", - compiled_contract.name, contract_function.name - ); - println!("{}", contract_function.bytecode); - } + for contract_function in &compiled_contract.functions { + println!( + "Compiled ACIR for {}::{} (unoptimized):", + compiled_contract.name, contract_function.name + ); + println!("{}", contract_function.bytecode); } } // errors here is either empty or contains only warnings - Ok((compiled_contracts, errors)) + Ok((compiled_contract, errors)) } } @@ -217,7 +225,7 @@ fn has_errors(errors: &[FileDiagnostic], deny_warnings: bool) -> bool { } /// Compile all of the functions associated with a Noir contract. -fn compile_contract( +fn compile_contract_inner( context: &Context, contract: Contract, options: &CompileOptions, diff --git a/compiler/wasm/Cargo.toml b/compiler/wasm/Cargo.toml index 1049dc92f47..c6126818434 100644 --- a/compiler/wasm/Cargo.toml +++ b/compiler/wasm/Cargo.toml @@ -13,7 +13,6 @@ crate-type = ["cdylib"] [dependencies] acvm.workspace = true fm.workspace = true -iter-extended.workspace = true nargo.workspace = true noirc_driver.workspace = true noirc_frontend.workspace = true diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index f610ff7e79f..c98e586ab45 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -1,9 +1,8 @@ use fm::FileManager; use gloo_utils::format::JsValueSerdeExt; -use iter_extended::try_vecmap; use log::debug; use noirc_driver::{ - add_dep, compile_contracts, compile_main, prepare_crate, prepare_dependency, CompileOptions, + add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, }; use noirc_frontend::{graph::CrateGraph, hir::Context}; use serde::{Deserialize, Serialize}; @@ -116,17 +115,15 @@ pub fn compile(args: JsValue) -> JsValue { let is_opcode_supported = acvm::pwg::default_is_opcode_supported(np_language); if options.contracts { - let compiled_contracts = - compile_contracts(&mut context, crate_id, &options.compile_options) - .expect("Contract compilation failed") - .0; + let compiled_contract = compile_contract(&mut context, crate_id, &options.compile_options) + .expect("Contract compilation failed") + .0; - let optimized_contracts = try_vecmap(compiled_contracts, |contract| { - nargo::ops::optimize_contract(contract, np_language, &is_opcode_supported) - }) - .expect("Contract optimization failed"); + let optimized_contract = + nargo::ops::optimize_contract(compiled_contract, np_language, &is_opcode_supported) + .expect("Contract optimization failed"); - ::from_serde(&optimized_contracts).unwrap() + ::from_serde(&optimized_contract).unwrap() } else { let compiled_program = compile_main(&mut context, crate_id, &options.compile_options) .expect("Compilation failed") diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 6b0c376447d..2069bda308f 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -3,7 +3,7 @@ use std::path::Path; use acvm::acir::circuit::Opcode; use acvm::Language; use fm::FileManager; -use iter_extended::{try_vecmap, vecmap}; +use iter_extended::vecmap; use nargo::artifacts::contract::PreprocessedContract; use nargo::artifacts::contract::PreprocessedContractFunction; use nargo::artifacts::debug::DebugArtifact; @@ -76,10 +76,8 @@ pub(crate) fn run( for (package, program) in binary_packages.into_iter().zip(compiled_programs) { save_program(program, &package, &circuit_dir, args.output_debug); } - for (package, contracts_with_debug_artifacts) in - contract_packages.into_iter().zip(compiled_contracts) - { - save_contracts(contracts_with_debug_artifacts, &package, &circuit_dir, args.output_debug); + for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { + save_contract(contract, &package, &circuit_dir, args.output_debug); } Ok(()) @@ -90,7 +88,7 @@ pub(super) fn compile_workspace( binary_packages: &[Package], contract_packages: &[Package], compile_options: &CompileOptions, -) -> Result<(Vec, Vec>), CliError> { +) -> Result<(Vec, Vec), CliError> { let (np_language, is_opcode_supported) = backend.get_backend_info()?; // Compile all of the packages in parallel. @@ -98,11 +96,11 @@ pub(super) fn compile_workspace( .par_iter() .map(|package| compile_program(package, compile_options, np_language, &is_opcode_supported)) .collect(); - let contract_results: Vec<(FileManager, CompilationResult>)> = + let contract_results: Vec<(FileManager, CompilationResult)> = contract_packages .par_iter() .map(|package| { - compile_contracts(package, compile_options, np_language, &is_opcode_supported) + compile_contract(package, compile_options, np_language, &is_opcode_supported) }) .collect(); @@ -113,7 +111,7 @@ pub(super) fn compile_workspace( report_errors(compilation_result, &file_manager, compile_options.deny_warnings) }) .collect::>()?; - let compiled_contracts: Vec> = contract_results + let compiled_contracts: Vec = contract_results .into_iter() .map(|(file_manager, compilation_result)| { report_errors(compilation_result, &file_manager, compile_options.deny_warnings) @@ -165,27 +163,26 @@ fn compile_program( (context.file_manager, Ok((optimized_program, warnings))) } -fn compile_contracts( +fn compile_contract( package: &Package, compile_options: &CompileOptions, np_language: Language, is_opcode_supported: &impl Fn(&Opcode) -> bool, -) -> (FileManager, CompilationResult>) { +) -> (FileManager, CompilationResult) { let (mut context, crate_id) = prepare_package(package); - let (contracts, warnings) = - match noirc_driver::compile_contracts(&mut context, crate_id, compile_options) { + let (contract, warnings) = + match noirc_driver::compile_contract(&mut context, crate_id, compile_options) { Ok(contracts_and_warnings) => contracts_and_warnings, Err(errors) => { return (context.file_manager, Err(errors)); } }; - let optimized_contracts = try_vecmap(contracts, |contract| { + let optimized_contract = nargo::ops::optimize_contract(contract, np_language, &is_opcode_supported) - }) - .expect("Backend does not support an opcode that is in the IR"); + .expect("Backend does not support an opcode that is in the IR"); - (context.file_manager, Ok((optimized_contracts, warnings))) + (context.file_manager, Ok((optimized_contract, warnings))) } fn save_program( @@ -210,8 +207,8 @@ fn save_program( } } -fn save_contracts( - contracts: Vec, +fn save_contract( + contract: CompiledContract, package: &Package, circuit_dir: &Path, output_debug: bool, @@ -220,51 +217,37 @@ fn save_contracts( // As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms) // are compiled via nargo-core and then the PreprocessedContract is constructed here. // This is due to EACH function needing it's own CRS, PKey, and VKey from the backend. - let preprocessed_contracts: Vec<(PreprocessedContract, DebugArtifact)> = - vecmap(contracts, |contract| { - let debug_artifact = DebugArtifact { - debug_symbols: contract - .functions - .iter() - .map(|function| function.debug.clone()) - .collect(), - file_map: contract.file_map, - }; - - let preprocessed_functions = - vecmap(contract.functions, |func| PreprocessedContractFunction { - name: func.name, - function_type: func.function_type, - is_internal: func.is_internal, - abi: func.abi, - - bytecode: func.bytecode, - }); - - ( - PreprocessedContract { - name: contract.name, - backend: String::from(BACKEND_IDENTIFIER), - functions: preprocessed_functions, - }, - debug_artifact, - ) - }); - - for (contract, debug_artifact) in preprocessed_contracts { - save_contract_to_file( - &contract, - &format!("{}-{}", package.name, contract.name), + let debug_artifact = DebugArtifact { + debug_symbols: contract.functions.iter().map(|function| function.debug.clone()).collect(), + file_map: contract.file_map, + }; + + let preprocessed_functions = vecmap(contract.functions, |func| PreprocessedContractFunction { + name: func.name, + function_type: func.function_type, + is_internal: func.is_internal, + abi: func.abi, + bytecode: func.bytecode, + }); + + let preprocessed_contract = PreprocessedContract { + name: contract.name, + backend: String::from(BACKEND_IDENTIFIER), + functions: preprocessed_functions, + }; + + save_contract_to_file( + &preprocessed_contract, + &format!("{}-{}", package.name, preprocessed_contract.name), + circuit_dir, + ); + + if output_debug { + save_debug_artifact_to_file( + &debug_artifact, + &format!("{}-{}", package.name, preprocessed_contract.name), circuit_dir, ); - - if output_debug { - save_debug_artifact_to_file( - &debug_artifact, - &format!("{}-{}", package.name, contract.name), - circuit_dir, - ); - } } } diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index dfc1e684832..e8e3b6c5e02 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -63,14 +63,11 @@ pub(crate) fn run( count_opcodes_and_gates_in_program(backend, program, package, np_language) })?; - let contract_info = try_vecmap(compiled_contracts, |contracts| { - count_opcodes_and_gates_in_contracts(backend, contracts, np_language) + let contract_info = try_vecmap(compiled_contracts, |contract| { + count_opcodes_and_gates_in_contract(backend, contract, np_language) })?; - let info_report = InfoReport { - programs: program_info, - contracts: contract_info.into_iter().flatten().collect(), - }; + let info_report = InfoReport { programs: program_info, contracts: contract_info }; if args.json { // Expose machine-readable JSON data. @@ -176,20 +173,18 @@ fn count_opcodes_and_gates_in_program( }) } -fn count_opcodes_and_gates_in_contracts( +fn count_opcodes_and_gates_in_contract( backend: &Backend, - compiled_contracts: Vec, + contract: CompiledContract, language: Language, -) -> Result, CliError> { - try_vecmap(compiled_contracts, |contract| { - let functions = try_vecmap(contract.functions, |function| -> Result<_, BackendError> { - Ok(FunctionInfo { - name: function.name, - acir_opcodes: function.bytecode.opcodes.len(), - circuit_size: backend.get_exact_circuit_size(&function.bytecode)?, - }) - })?; +) -> Result { + let functions = try_vecmap(contract.functions, |function| -> Result<_, BackendError> { + Ok(FunctionInfo { + name: function.name, + acir_opcodes: function.bytecode.opcodes.len(), + circuit_size: backend.get_exact_circuit_size(&function.bytecode)?, + }) + })?; - Ok(ContractInfo { name: contract.name, language, functions }) - }) + Ok(ContractInfo { name: contract.name, language, functions }) } diff --git a/tooling/nargo_cli/tests/compile_failure/multiple_contracts/Nargo.toml b/tooling/nargo_cli/tests/compile_failure/multiple_contracts/Nargo.toml new file mode 100644 index 00000000000..c71c86c664b --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/multiple_contracts/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "multiple_contracts" +type = "contract" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] diff --git a/tooling/nargo_cli/tests/compile_failure/multiple_contracts/src/main.nr b/tooling/nargo_cli/tests/compile_failure/multiple_contracts/src/main.nr new file mode 100644 index 00000000000..0562ca9ccd5 --- /dev/null +++ b/tooling/nargo_cli/tests/compile_failure/multiple_contracts/src/main.nr @@ -0,0 +1,4 @@ +contract Foo {} + + +contract Bar {}