From 005fd0b70072085a4b4726708b7d12e162136134 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 3 Mar 2023 13:26:12 -0600 Subject: [PATCH 1/3] Implement basic contracts --- crates/nargo/src/cli/compile_cmd.rs | 83 +++++++++++++------ crates/nargo/src/cli/test_cmd.rs | 2 +- .../tests/test_data/contracts/Nargo.toml | 5 ++ .../tests/test_data/contracts/Prover.toml | 2 + .../tests/test_data/contracts/src/main.nr | 8 ++ .../tests/test_data/contracts/target/Foo.json | 1 + .../contracts/target/Foo.json.checksum | 1 + .../contracts/target/foo-Foo-double.json | 1 + .../target/foo-Foo-double.json.checksum | 1 + .../contracts/target/foo-Foo-triple.json | 1 + .../target/foo-Foo-triple.json.checksum | 1 + .../test_data/contracts/target/foo-Foo.json | 1 + .../contracts/target/foo-Foo.json.checksum | 1 + .../test_data/contracts/target/mycircuit.json | 1 + .../contracts/target/mycircuit.json.checksum | 1 + crates/noirc_driver/src/lib.rs | 58 ++++++++----- crates/noirc_driver/src/main.rs | 2 +- .../src/hir/def_collector/dc_mod.rs | 28 ++++--- crates/noirc_frontend/src/hir/def_map/mod.rs | 57 ++++++++++++- .../src/hir/def_map/module_data.rs | 21 ++++- crates/noirc_frontend/src/lexer/token.rs | 3 + crates/noirc_frontend/src/parser/mod.rs | 1 + crates/noirc_frontend/src/parser/parser.rs | 19 ++++- 23 files changed, 227 insertions(+), 72 deletions(-) create mode 100644 crates/nargo/tests/test_data/contracts/Nargo.toml create mode 100644 crates/nargo/tests/test_data/contracts/Prover.toml create mode 100644 crates/nargo/tests/test_data/contracts/src/main.nr create mode 100644 crates/nargo/tests/test_data/contracts/target/Foo.json create mode 100644 crates/nargo/tests/test_data/contracts/target/Foo.json.checksum create mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json create mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum create mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json create mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum create mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo.json create mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum create mode 100644 crates/nargo/tests/test_data/contracts/target/mycircuit.json create mode 100644 crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum diff --git a/crates/nargo/src/cli/compile_cmd.rs b/crates/nargo/src/cli/compile_cmd.rs index 068451af451..e2995dd7ba4 100644 --- a/crates/nargo/src/cli/compile_cmd.rs +++ b/crates/nargo/src/cli/compile_cmd.rs @@ -1,5 +1,7 @@ use acvm::acir::circuit::Circuit; use acvm::ProofSystemCompiler; +use noirc_driver::Driver; +use noirc_frontend::node_interner::FuncId; use std::path::{Path, PathBuf}; use clap::Args; @@ -18,36 +20,60 @@ pub(crate) struct CompileCommand { /// Issue a warning for each unused variable instead of an error #[arg(short, long)] allow_warnings: bool, -} - -pub(crate) fn run(args: CompileCommand, config: NargoConfig) -> Result<(), CliError> { - let mut circuit_path = config.program_dir.clone(); - circuit_path.push(TARGET_DIR); - let circuit_path = compile_and_preprocess_circuit( - &args.circuit_name, - config.program_dir, - circuit_path, - args.allow_warnings, - )?; + /// Compile each contract function used within the program + #[arg(short, long)] + contracts: bool, +} - println!("Generated ACIR code into {}", circuit_path.display()); +pub(crate) fn run(mut args: CompileCommand, config: NargoConfig) -> Result<(), CliError> { + let driver = check_crate(&config.program_dir, args.allow_warnings)?; + + let mut circuit_dir = config.program_dir; + circuit_dir.push(TARGET_DIR); + + // If contracts is set we're compiling every function in a 'contract' rather than just 'main'. + if args.contracts { + let circuit_name = args.circuit_name.clone(); + + for contract in driver.get_all_contracts() { + for function in contract.functions { + let name = driver.function_name(function); + args.circuit_name = format!("{}-{}-{name}", circuit_name, &contract.name); + compile_and_save_program(&driver, function, &args, &circuit_dir)?; + } + } + Ok(()) + } else { + let main = driver.main_function(); + compile_and_save_program(&driver, main, &args, &circuit_dir) + } +} - Ok(()) +fn setup_driver(program_dir: impl AsRef) -> Result { + let backend = crate::backends::ConcreteBackend; + let mut driver = Resolver::resolve_root_config(program_dir.as_ref(), backend.np_language())?; + add_std_lib(&mut driver); + Ok(driver) } -fn compile_and_preprocess_circuit>( - circuit_name: &str, - program_dir: P, - circuit_dir: P, - allow_warnings: bool, -) -> Result { - let compiled_program = compile_circuit(program_dir, false, allow_warnings)?; - let circuit_path = save_program_to_file(&compiled_program, circuit_name, &circuit_dir); +/// Compile and save a program to disk with the given main function. +fn compile_and_save_program( + driver: &Driver, + main: FuncId, + args: &CompileCommand, + circuit_dir: &Path, +) -> Result<(), CliError> { + let compiled_program = driver + .compile_no_check(false, args.allow_warnings, main, true) + .map_err(|_| CliError::Generic(format!("'{}' failed to compile", args.circuit_name)))?; + + let circuit_path = save_program_to_file(&compiled_program, &args.circuit_name, circuit_dir); - preprocess_with_path(circuit_name, circuit_dir, &compiled_program.circuit)?; + preprocess_with_path(&args.circuit_name, circuit_dir, &compiled_program.circuit)?; - Ok(circuit_path) + println!("Generated ACIR code into {}", circuit_path.display()); + Ok(()) } pub(crate) fn compile_circuit>( @@ -55,11 +81,14 @@ pub(crate) fn compile_circuit>( show_ssa: bool, allow_warnings: bool, ) -> Result { - let backend = crate::backends::ConcreteBackend; - let mut driver = Resolver::resolve_root_config(program_dir.as_ref(), backend.np_language())?; - add_std_lib(&mut driver); + let mut driver = setup_driver(program_dir)?; + driver.compile_main(show_ssa, allow_warnings, true).map_err(|_| CliError::CompilationError) +} - driver.into_compiled_program(show_ssa, allow_warnings).map_err(|_| CliError::CompilationError) +fn check_crate(program_dir: impl AsRef, allow_warnings: bool) -> Result { + let mut driver = setup_driver(program_dir)?; + driver.check_crate(allow_warnings).map_err(|_| CliError::CompilationError)?; + Ok(driver) } fn preprocess_with_path>( diff --git a/crates/nargo/src/cli/test_cmd.rs b/crates/nargo/src/cli/test_cmd.rs index 3d0aa719691..52517d58fa0 100644 --- a/crates/nargo/src/cli/test_cmd.rs +++ b/crates/nargo/src/cli/test_cmd.rs @@ -93,7 +93,7 @@ fn run_test( let backend = crate::backends::ConcreteBackend; let program = driver - .compile_no_check(false, allow_warnings, Some(main), show_output) + .compile_no_check(false, allow_warnings, main, show_output) .map_err(|_| CliError::Generic(format!("Test '{test_name}' failed to compile")))?; let mut solved_witness = BTreeMap::new(); diff --git a/crates/nargo/tests/test_data/contracts/Nargo.toml b/crates/nargo/tests/test_data/contracts/Nargo.toml new file mode 100644 index 00000000000..e0b467ce5da --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/Prover.toml b/crates/nargo/tests/test_data/contracts/Prover.toml new file mode 100644 index 00000000000..97d5b1e0eed --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/Prover.toml @@ -0,0 +1,2 @@ +x = 3 +y = 2 diff --git a/crates/nargo/tests/test_data/contracts/src/main.nr b/crates/nargo/tests/test_data/contracts/src/main.nr new file mode 100644 index 00000000000..092439ad93a --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/src/main.nr @@ -0,0 +1,8 @@ +fn main(x : Field, y : pub Field) { + constrain Foo::double(x) == Foo::triple(y); +} + +contract Foo { + fn double(x: Field) -> Field { x * 2 } + fn triple(x: Field) -> Field { x * 3 } +} diff --git a/crates/nargo/tests/test_data/contracts/target/Foo.json b/crates/nargo/tests/test_data/contracts/target/Foo.json new file mode 100644 index 00000000000..795a01f3295 --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/Foo.json @@ -0,0 +1 @@ +{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/Foo.json.checksum b/crates/nargo/tests/test_data/contracts/target/Foo.json.checksum new file mode 100644 index 00000000000..cb562a060ca --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/Foo.json.checksum @@ -0,0 +1 @@ +94403f47ff427e11c76feea77e6fa6eaccc53041f9e64fdaec8f148b0cec81f9 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json b/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json new file mode 100644 index 00000000000..b28383a5013 --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json @@ -0,0 +1 @@ +{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum b/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum new file mode 100644 index 00000000000..8810da88998 --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum @@ -0,0 +1 @@ +f0472c9a43b3f9662f454a02daa8bb4a99d0e797063f67603030f92173618fd6 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json b/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json new file mode 100644 index 00000000000..795a01f3295 --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json @@ -0,0 +1 @@ +{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum b/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum new file mode 100644 index 00000000000..cb562a060ca --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum @@ -0,0 +1 @@ +94403f47ff427e11c76feea77e6fa6eaccc53041f9e64fdaec8f148b0cec81f9 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo.json b/crates/nargo/tests/test_data/contracts/target/foo-Foo.json new file mode 100644 index 00000000000..795a01f3295 --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/foo-Foo.json @@ -0,0 +1 @@ +{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum b/crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum new file mode 100644 index 00000000000..cb562a060ca --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum @@ -0,0 +1 @@ +94403f47ff427e11c76feea77e6fa6eaccc53041f9e64fdaec8f148b0cec81f9 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/mycircuit.json b/crates/nargo/tests/test_data/contracts/target/mycircuit.json new file mode 100644 index 00000000000..573e679538e --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/mycircuit.json @@ -0,0 +1 @@ +{"circuit":[0,0,0,0,6,0,0,0,1,0,0,0,2,0,0,0,5,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,239,255,255,254,2,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,3,0,0,0,4,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,4,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,5,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"public"}],"param_witnesses":{"x":[1],"y":[2]},"return_type":null,"return_witnesses":[]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum b/crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum new file mode 100644 index 00000000000..5ef987d4ab2 --- /dev/null +++ b/crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum @@ -0,0 +1 @@ +232ba300667f0890d6dd6b1cb3e37e83cf031263d778b2db8f4c135ebbb73084 \ No newline at end of file diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index d898a0ae1c7..23d0d51e504 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -8,7 +8,7 @@ use noirc_abi::FunctionSignature; use noirc_errors::{reporter, ReportedError}; use noirc_evaluator::create_circuit; use noirc_frontend::graph::{CrateId, CrateName, CrateType, LOCAL_CRATE}; -use noirc_frontend::hir::def_map::CrateDefMap; +use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; use noirc_frontend::monomorphization::monomorphize; use noirc_frontend::node_interner::FuncId; @@ -34,8 +34,7 @@ impl Driver { pub fn compile_file(root_file: PathBuf, np_language: acvm::Language) -> CompiledProgram { let mut driver = Driver::new(&np_language); driver.create_local_crate(root_file, CrateType::Binary); - - driver.into_compiled_program(false, false).unwrap_or_else(|_| std::process::exit(1)) + driver.compile_main(false, false, true).unwrap_or_else(|_| std::process::exit(1)) } /// Compiles a file and returns true if compilation was successful @@ -139,13 +138,34 @@ impl Driver { Some(func_meta.into_function_signature(&self.context.def_interner)) } - pub fn into_compiled_program( - mut self, + /// Run the frontend to check the crate for errors then compile the main function if there were none + pub fn compile_main( + &mut self, show_ssa: bool, allow_warnings: bool, + show_output: bool, ) -> Result { self.check_crate(allow_warnings)?; - self.compile_no_check(show_ssa, allow_warnings, None, true) + let main = self.main_function(); + self.compile_no_check(show_ssa, allow_warnings, main, show_output) + } + + /// Returns the FuncId of the 'main' funciton. + /// - Expects check_crate to be called beforehand + /// - Panics if no main function is found + pub fn main_function(&self) -> FuncId { + // Find the local crate, one should always be present + let local_crate = self.context.def_map(LOCAL_CRATE).unwrap(); + + // Check the crate type + // We don't panic here to allow users to `evaluate` libraries which will do nothing + if self.context.crate_graph[LOCAL_CRATE].crate_type != CrateType::Binary { + println!("cannot compile crate into a program as the local crate is not a binary. For libraries, please use the check command"); + std::process::exit(1); + }; + + // All Binaries should have a main function + local_crate.main_function().expect("cannot compile a program with no main function") } /// Compile the current crate. Assumes self.check_crate is called beforehand! @@ -155,25 +175,9 @@ impl Driver { show_ssa: bool, allow_warnings: bool, // Optional override to provide a different `main` function to start execution - main_function: Option, + main_function: FuncId, show_output: bool, ) -> Result { - // Find the local crate, one should always be present - let local_crate = self.context.def_map(LOCAL_CRATE).unwrap(); - - // If no override for the `main` function has been provided, attempt to find it. - let main_function = main_function.unwrap_or_else(|| { - // Check the crate type - // We don't panic here to allow users to `evaluate` libraries which will do nothing - if self.context.crate_graph[LOCAL_CRATE].crate_type != CrateType::Binary { - println!("cannot compile crate into a program as the local crate is not a binary. For libraries, please use the check command"); - std::process::exit(1); - }; - - // All Binaries should have a main function - local_crate.main_function().expect("cannot compile a program with no main function") - }); - let program = monomorphize(main_function, &self.context.def_interner); let np_language = self.language.clone(); @@ -206,6 +210,14 @@ impl Driver { .collect() } + /// Return a Vec of all `contract` declarations in the source code and the functions they contain + pub fn get_all_contracts(&self) -> Vec { + self.context + .def_map(LOCAL_CRATE) + .expect("The local crate should be analyzed already") + .get_all_contracts() + } + pub fn function_name(&self, id: FuncId) -> &str { self.context.def_interner.function_name(&id) } diff --git a/crates/noirc_driver/src/main.rs b/crates/noirc_driver/src/main.rs index e7f99ced95f..4d1e5472019 100644 --- a/crates/noirc_driver/src/main.rs +++ b/crates/noirc_driver/src/main.rs @@ -18,5 +18,5 @@ fn main() { driver.add_dep(LOCAL_CRATE, crate_id1, "coo4"); driver.add_dep(LOCAL_CRATE, crate_id2, "coo3"); - driver.into_compiled_program(false, false).ok(); + driver.compile_main(false, false, true).ok(); } diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs index 9433cd1519a..1a58decda99 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -161,7 +161,7 @@ impl<'a> ModCollector<'a> { let name = struct_definition.name.clone(); // Create the corresponding module for the struct namespace - let id = match self.push_child_module(&name, self.file_id, false, errors) { + let id = match self.push_child_module(&name, self.file_id, false, false, errors) { Some(local_id) => StructId(ModuleId { krate, local_id }), None => continue, }; @@ -195,7 +195,13 @@ impl<'a> ModCollector<'a> { errors: &mut Vec, ) { for submodule in submodules { - if let Some(child) = self.push_child_module(&submodule.name, file_id, true, errors) { + if let Some(child) = self.push_child_module( + &submodule.name, + file_id, + true, + submodule.is_contract, + errors, + ) { collect_defs( self.def_collector, submodule.contents, @@ -234,7 +240,9 @@ impl<'a> ModCollector<'a> { let ast = parse_file(&mut context.file_manager, child_file_id, errors); // Add module into def collector and get a ModuleId - if let Some(child_mod_id) = self.push_child_module(mod_name, child_file_id, true, errors) { + if let Some(child_mod_id) = + self.push_child_module(mod_name, child_file_id, true, false, errors) + { collect_defs( self.def_collector, ast, @@ -254,21 +262,15 @@ impl<'a> ModCollector<'a> { mod_name: &Ident, file_id: FileId, add_to_parent_scope: bool, + is_contract: bool, errors: &mut Vec, ) -> Option { - // Create a new default module - let module_id = self.def_collector.def_map.modules.insert(ModuleData::default()); + let parent = Some(self.module_id); + let new_module = ModuleData::new(parent, ModuleOrigin::File(file_id), is_contract); + let module_id = self.def_collector.def_map.modules.insert(new_module); let modules = &mut self.def_collector.def_map.modules; - // Update the child module to reference the parent - modules[module_id].parent = Some(self.module_id); - - // Update the origin of the child module - // Also note that the FileId is where this module is defined and not declared - // To find out where the module was declared, you need to check its parent - modules[module_id].origin = ModuleOrigin::File(file_id); - // Update the parent module to reference the child modules[self.module_id.0].children.insert(mod_name.clone(), LocalModuleId(module_id)); diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index be2a9feeb5f..2116e05a16a 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -72,10 +72,8 @@ impl CrateDefMap { // Allocate a default Module for the root, giving it a ModuleId let mut modules: Arena = Arena::default(); - let root = modules.insert(ModuleData::default()); - - // Set the origin of the root module - modules[root].origin = ModuleOrigin::CrateRoot(root_file_id); + let origin = ModuleOrigin::CrateRoot(root_file_id); + let root = modules.insert(ModuleData::new(None, origin, false)); let def_map = CrateDefMap { root: LocalModuleId(root), @@ -129,6 +127,57 @@ impl CrateDefMap { functions.filter(|id| interner.function_meta(id).attributes == Some(Attribute::Test)) }) } + + /// Go through all modules in this crate, find all `contract ... { ... }` declarations, + /// and collect them all into a Vec. + pub fn get_all_contracts(&self) -> Vec { + self.modules + .iter() + .filter_map(|(id, module)| { + if module.is_contract { + let functions = module + .scope + .values() + .values() + .filter_map(|(id, _)| id.as_function()) + .collect(); + + let name = self.get_module_path(id, module.parent); + Some(Contract { name, functions }) + } else { + None + } + }) + .collect() + } + + /// Find a child module's name by inspecting its parent. + /// Currently required as modules do not store their own names. + fn get_module_path(&self, child_id: Index, parent: Option) -> String { + if let Some(id) = parent { + let parent = &self.modules[id.0]; + let name = parent + .children + .iter() + .find(|(_, id)| id.0 == child_id) + .map(|(name, _)| &name.0.contents) + .expect("Child module was not a child of the given parent module"); + + let parent_name = self.get_module_path(id.0, parent.parent); + if parent_name.is_empty() { + name.to_string() + } else { + format!("{parent_name}.{name}") + } + } else { + String::new() + } + } +} + +pub struct Contract { + pub name: String, + pub functions: Vec, } /// Given a FileId, fetch the File, from the FileManager and parse it's content diff --git a/crates/noirc_frontend/src/hir/def_map/module_data.rs b/crates/noirc_frontend/src/hir/def_map/module_data.rs index 8f62f7581b1..d437a4d1b6c 100644 --- a/crates/noirc_frontend/src/hir/def_map/module_data.rs +++ b/crates/noirc_frontend/src/hir/def_map/module_data.rs @@ -8,13 +8,32 @@ use super::{ItemScope, LocalModuleId}; /// Contains the actual contents of a module: its parent (if one exists), /// children, and scope with all definitions defined within the scope. -#[derive(Default, Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] pub struct ModuleData { pub parent: Option, pub children: HashMap, pub scope: ItemScope, pub origin: ModuleOrigin, + + /// True if this module is a `contract Foo { ... }` module containing contract functions + pub is_contract: bool, +} + +impl ModuleData { + pub fn new( + parent: Option, + origin: ModuleOrigin, + is_contract: bool, + ) -> ModuleData { + ModuleData { + parent, + children: HashMap::new(), + scope: ItemScope::default(), + origin, + is_contract, + } + } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 9213c7d5291..6cce4d351b4 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -418,6 +418,7 @@ pub enum Keyword { Char, CompTime, Constrain, + Contract, Crate, Dep, Else, @@ -447,6 +448,7 @@ impl fmt::Display for Keyword { Keyword::Char => write!(f, "char"), Keyword::CompTime => write!(f, "comptime"), Keyword::Constrain => write!(f, "constrain"), + Keyword::Contract => write!(f, "contract"), Keyword::Crate => write!(f, "crate"), Keyword::Dep => write!(f, "dep"), Keyword::Else => write!(f, "else"), @@ -479,6 +481,7 @@ impl Keyword { "char" => Keyword::Char, "comptime" => Keyword::CompTime, "constrain" => Keyword::Constrain, + "contract" => Keyword::Contract, "crate" => Keyword::Crate, "dep" => Keyword::Dep, "else" => Keyword::Else, diff --git a/crates/noirc_frontend/src/parser/mod.rs b/crates/noirc_frontend/src/parser/mod.rs index 72fb973fbf6..fa0903ee659 100644 --- a/crates/noirc_frontend/src/parser/mod.rs +++ b/crates/noirc_frontend/src/parser/mod.rs @@ -234,6 +234,7 @@ pub struct ParsedModule { pub struct SubModule { pub name: Ident, pub contents: ParsedModule, + pub is_contract: bool, } impl ParsedModule { diff --git a/crates/noirc_frontend/src/parser/parser.rs b/crates/noirc_frontend/src/parser/parser.rs index 708070652cc..9e767d3819b 100644 --- a/crates/noirc_frontend/src/parser/parser.rs +++ b/crates/noirc_frontend/src/parser/parser.rs @@ -101,7 +101,8 @@ fn top_level_statement( function_definition(false).map(TopLevelStatement::Function), struct_definition(), implementation(), - submodule(module_parser), + submodule(module_parser.clone()), + contract(module_parser), module_declaration().then_ignore(force(just(Token::Semicolon))), use_statement().then_ignore(force(just(Token::Semicolon))), global_declaration().then_ignore(force(just(Token::Semicolon))), @@ -128,7 +129,21 @@ fn submodule(module_parser: impl NoirParser) -> impl NoirParser) -> impl NoirParser { + keyword(Keyword::Contract) + .ignore_then(ident()) + .then_ignore(just(Token::LeftBrace)) + .then(module_parser) + .then_ignore(just(Token::RightBrace)) + .map(|(name, contents)| { + TopLevelStatement::SubModule(SubModule { name, contents, is_contract: true }) + }) } /// function_definition: attribute fn ident generics '(' function_parameters ')' function_return_type block From 6f2f81622ac263c2987a3544448edf5c1759be37 Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Fri, 3 Mar 2023 13:44:02 -0600 Subject: [PATCH 2/3] Remove target directory --- crates/nargo/tests/test_data/contracts/target/Foo.json | 1 - crates/nargo/tests/test_data/contracts/target/Foo.json.checksum | 1 - .../nargo/tests/test_data/contracts/target/foo-Foo-double.json | 1 - .../test_data/contracts/target/foo-Foo-double.json.checksum | 1 - .../nargo/tests/test_data/contracts/target/foo-Foo-triple.json | 1 - .../test_data/contracts/target/foo-Foo-triple.json.checksum | 1 - crates/nargo/tests/test_data/contracts/target/foo-Foo.json | 1 - .../nargo/tests/test_data/contracts/target/foo-Foo.json.checksum | 1 - crates/nargo/tests/test_data/contracts/target/mycircuit.json | 1 - .../tests/test_data/contracts/target/mycircuit.json.checksum | 1 - 10 files changed, 10 deletions(-) delete mode 100644 crates/nargo/tests/test_data/contracts/target/Foo.json delete mode 100644 crates/nargo/tests/test_data/contracts/target/Foo.json.checksum delete mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json delete mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum delete mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json delete mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum delete mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo.json delete mode 100644 crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum delete mode 100644 crates/nargo/tests/test_data/contracts/target/mycircuit.json delete mode 100644 crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum diff --git a/crates/nargo/tests/test_data/contracts/target/Foo.json b/crates/nargo/tests/test_data/contracts/target/Foo.json deleted file mode 100644 index 795a01f3295..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/Foo.json +++ /dev/null @@ -1 +0,0 @@ -{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/Foo.json.checksum b/crates/nargo/tests/test_data/contracts/target/Foo.json.checksum deleted file mode 100644 index cb562a060ca..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/Foo.json.checksum +++ /dev/null @@ -1 +0,0 @@ -94403f47ff427e11c76feea77e6fa6eaccc53041f9e64fdaec8f148b0cec81f9 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json b/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json deleted file mode 100644 index b28383a5013..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json +++ /dev/null @@ -1 +0,0 @@ -{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum b/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum deleted file mode 100644 index 8810da88998..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/foo-Foo-double.json.checksum +++ /dev/null @@ -1 +0,0 @@ -f0472c9a43b3f9662f454a02daa8bb4a99d0e797063f67603030f92173618fd6 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json b/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json deleted file mode 100644 index 795a01f3295..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json +++ /dev/null @@ -1 +0,0 @@ -{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum b/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum deleted file mode 100644 index cb562a060ca..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/foo-Foo-triple.json.checksum +++ /dev/null @@ -1 +0,0 @@ -94403f47ff427e11c76feea77e6fa6eaccc53041f9e64fdaec8f148b0cec81f9 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo.json b/crates/nargo/tests/test_data/contracts/target/foo-Foo.json deleted file mode 100644 index 795a01f3295..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/foo-Foo.json +++ /dev/null @@ -1 +0,0 @@ -{"circuit":[0,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[1]},"return_type":{"kind":"field"},"return_witnesses":[2]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum b/crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum deleted file mode 100644 index cb562a060ca..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/foo-Foo.json.checksum +++ /dev/null @@ -1 +0,0 @@ -94403f47ff427e11c76feea77e6fa6eaccc53041f9e64fdaec8f148b0cec81f9 \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/mycircuit.json b/crates/nargo/tests/test_data/contracts/target/mycircuit.json deleted file mode 100644 index 573e679538e..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/mycircuit.json +++ /dev/null @@ -1 +0,0 @@ -{"circuit":[0,0,0,0,6,0,0,0,1,0,0,0,2,0,0,0,5,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,239,255,255,254,2,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,3,0,0,0,4,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,4,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,5,0,0,0,48,100,78,114,225,49,160,41,184,80,69,182,129,129,88,93,40,51,232,72,121,185,112,145,67,225,245,147,240,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"public"}],"param_witnesses":{"x":[1],"y":[2]},"return_type":null,"return_witnesses":[]}} \ No newline at end of file diff --git a/crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum b/crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum deleted file mode 100644 index 5ef987d4ab2..00000000000 --- a/crates/nargo/tests/test_data/contracts/target/mycircuit.json.checksum +++ /dev/null @@ -1 +0,0 @@ -232ba300667f0890d6dd6b1cb3e37e83cf031263d778b2db8f4c135ebbb73084 \ No newline at end of file From b455337c42d72f3bcb30cb9876b9d708af8e6a3f Mon Sep 17 00:00:00 2001 From: Jake Fecher Date: Mon, 6 Mar 2023 13:05:49 -0600 Subject: [PATCH 3/3] Add doc comment --- crates/noirc_frontend/src/hir/def_map/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 2116e05a16a..7c0788050ce 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -175,7 +175,10 @@ impl CrateDefMap { } } +/// A 'contract' in Noir source code with the given name and functions. +/// This is not an AST node, it is just a convenient form to return for CrateDefMap::get_all_contracts. pub struct Contract { + /// To keep `name` semi-unique, it is prefixed with the names of parent modules via CrateDefMap::get_module_path pub name: String, pub functions: Vec, }