From 64a16e23f322a5f0b9a7e2952364d526fb6a2e0f Mon Sep 17 00:00:00 2001 From: Tom French Date: Mon, 24 Jun 2024 19:59:32 +0100 Subject: [PATCH] git subrepo pull --force noir/noir-repo subrepo: subdir: "noir/noir-repo" merged: "f2f8ecc833" upstream: origin: "https://github.com/noir-lang/noir" branch: "master" commit: "f2f8ecc833" git-subrepo: version: "0.4.6" origin: "???" commit: "???" --- noir/noir-repo/.aztec-sync-commit | 2 +- .../.github/scripts/cargo-binstall-install.sh | 7 +- noir/noir-repo/.gitrepo | 4 +- noir/noir-repo/Cargo.lock | 57 +- noir/noir-repo/Cargo.toml | 6 +- .../acir/src/circuit/black_box_functions.rs | 12 +- .../opcodes/black_box_function_call.rs | 12 +- .../acvm-repo/acvm/src/pwg/blackbox/mod.rs | 10 +- .../acvm/src/pwg/blackbox/pedersen.rs | 47 ++ noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../src/curve_specific_solver.rs | 25 + .../bn254_blackbox_solver/src/lib.rs | 28 + .../acvm-repo/brillig/src/black_box.rs | 4 +- .../acvm-repo/brillig_vm/src/black_box.rs | 37 +- noir/noir-repo/aztec_macros/src/lib.rs | 33 +- ...te_note_hash_and_optionally_a_nullifier.rs | 8 +- .../src/transforms/contract_interface.rs | 232 +++------ .../aztec_macros/src/transforms/events.rs | 461 ++++++----------- .../src/transforms/note_interface.rs | 27 +- .../aztec_macros/src/transforms/storage.rs | 20 +- .../aztec_macros/src/utils/ast_utils.rs | 2 +- .../aztec_macros/src/utils/constants.rs | 1 + .../aztec_macros/src/utils/errors.rs | 6 - .../compiler/noirc_driver/src/lib.rs | 15 +- .../compiler/noirc_errors/src/reporter.rs | 6 +- .../brillig/brillig_gen/brillig_black_box.rs | 35 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 15 +- .../src/brillig/brillig_ir/debug_show.rs | 20 +- .../compiler/noirc_evaluator/src/ssa.rs | 101 ++-- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 32 +- .../ssa/acir_gen/acir_ir/generated_acir.rs | 26 +- .../src/ssa/ir/instruction/call.rs | 6 +- .../noirc_frontend/src/ast/expression.rs | 18 +- .../src/elaborator/expressions.rs | 18 +- .../noirc_frontend/src/elaborator/mod.rs | 2 +- .../noirc_frontend/src/elaborator/types.rs | 26 +- .../noirc_frontend/src/elaborator/unquote.rs | 323 ++---------- .../noirc_frontend/src/hir/comptime/errors.rs | 37 +- .../src/hir/comptime/interpreter.rs | 40 +- .../src/hir/comptime/interpreter/builtin.rs | 159 +++++- .../src/hir/comptime/interpreter/unquote.rs | 301 +---------- .../noirc_frontend/src/hir/comptime/scan.rs | 2 +- .../noirc_frontend/src/hir/comptime/value.rs | 38 +- .../src/hir/resolution/resolver.rs | 10 +- .../src/hir/type_check/errors.rs | 6 +- .../noirc_frontend/src/hir/type_check/expr.rs | 4 +- .../noirc_frontend/src/hir_def/expr.rs | 15 +- .../noirc_frontend/src/hir_def/types.rs | 25 +- .../noirc_frontend/src/lexer/errors.rs | 12 + .../noirc_frontend/src/lexer/lexer.rs | 105 +++- .../noirc_frontend/src/lexer/token.rs | 44 +- .../src/monomorphization/errors.rs | 3 + .../src/monomorphization/mod.rs | 10 +- .../noirc_frontend/src/node_interner.rs | 11 +- .../noirc_frontend/src/noir_parser.lalrpop | 1 - .../compiler/noirc_frontend/src/parser/mod.rs | 4 +- .../noirc_frontend/src/parser/parser.rs | 49 +- .../src/parser/parser/primitives.rs | 7 + .../noirc_frontend/src/parser/parser/types.rs | 9 +- .../compiler/noirc_frontend/src/tests.rs | 24 + .../docs/docs/how_to/how-to-oracles.md | 8 +- noir/noir-repo/noir_stdlib/src/hash.nr | 25 +- noir/noir-repo/noir_stdlib/src/lib.nr | 1 + noir/noir-repo/noir_stdlib/src/meta.nr | 1 + .../noir_stdlib/src/meta/type_def.nr | 16 + .../comptime_type_definition}/Nargo.toml | 2 +- .../comptime_type_definition/src/main.nr | 13 + .../derive_impl/Nargo.toml | 7 + .../derive_impl/src/main.nr | 44 ++ .../compile_success_empty/macros/src/main.nr | 6 +- .../verify_honk_proof/Prover.toml | 4 - .../verify_honk_proof/src/main.nr | 19 - noir/noir-repo/tooling/acvm_cli/Cargo.toml | 2 +- noir/noir-repo/tooling/lsp/src/solver.rs | 16 + noir/noir-repo/tooling/nargo_cli/Cargo.toml | 9 +- .../tooling/nargo_cli/tests/stdlib-tests.rs | 3 +- .../tooling/nargo_fmt/src/rewrite/expr.rs | 5 +- .../tooling/nargo_fmt/src/visitor/item.rs | 2 +- .../tooling/nargo_fmt/tests/expected/fn.nr | 4 + .../tooling/nargo_fmt/tests/input/fn.nr | 5 + .../noir_js_backend_barretenberg/package.json | 2 +- .../src/backend.ts | 5 +- noir/noir-repo/tooling/noirc_abi/src/lib.rs | 2 +- noir/noir-repo/tooling/profiler/Cargo.toml | 43 -- .../profiler/src/cli/gates_flamegraph_cmd.rs | 485 ------------------ .../noir-repo/tooling/profiler/src/cli/mod.rs | 33 -- noir/noir-repo/tooling/profiler/src/main.rs | 35 -- noir/noir-repo/yarn.lock | 13 +- 88 files changed, 1388 insertions(+), 2024 deletions(-) create mode 100644 noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs create mode 100644 noir/noir-repo/noir_stdlib/src/meta.nr create mode 100644 noir/noir-repo/noir_stdlib/src/meta/type_def.nr rename noir/noir-repo/test_programs/{execution_success/verify_honk_proof => compile_success_empty/comptime_type_definition}/Nargo.toml (70%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/derive_impl/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr delete mode 100644 noir/noir-repo/tooling/profiler/Cargo.toml delete mode 100644 noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs delete mode 100644 noir/noir-repo/tooling/profiler/src/cli/mod.rs delete mode 100644 noir/noir-repo/tooling/profiler/src/main.rs diff --git a/noir/noir-repo/.aztec-sync-commit b/noir/noir-repo/.aztec-sync-commit index 5a1cd9c70bd4..bdaabc697272 100644 --- a/noir/noir-repo/.aztec-sync-commit +++ b/noir/noir-repo/.aztec-sync-commit @@ -1 +1 @@ -58e15edf7fd3d32267b0aed883fc84f6cee327c9 +12af650f0d27c37dca06bb329bf76a5574534d78 diff --git a/noir/noir-repo/.github/scripts/cargo-binstall-install.sh b/noir/noir-repo/.github/scripts/cargo-binstall-install.sh index 4530b074d482..55d90904ca31 100755 --- a/noir/noir-repo/.github/scripts/cargo-binstall-install.sh +++ b/noir/noir-repo/.github/scripts/cargo-binstall-install.sh @@ -1,9 +1,4 @@ #!/usr/bin/env bash set -eu -if [ command -v cargo-binstall &> /dev/null ]; then - cargo-binstall cargo-binstall -y -else - curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash -fi - +curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash diff --git a/noir/noir-repo/.gitrepo b/noir/noir-repo/.gitrepo index 12e4bdc68a3c..e49d68724f31 100644 --- a/noir/noir-repo/.gitrepo +++ b/noir/noir-repo/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = master - commit = a6016b46abf6da6de4566cf6d35a675d805dd9b5 - parent = 6c7062443ae23cc75ac06b7ac1492d12f803d0e5 + commit = f2f8ecc833d4725d0829f9c339389c90d1a4fbcd + parent = b77204a75c0bbe0850994e37e6f6ab21c9478446 method = merge cmdver = 0.4.6 diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 788c548683d9..eb0580083591 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -1186,19 +1186,6 @@ dependencies = [ "syn 2.0.64", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if 1.0.0", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core 0.9.8", -] - [[package]] name = "debugid" version = "0.8.0" @@ -1395,15 +1382,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "log", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -2039,17 +2017,12 @@ dependencies = [ [[package]] name = "inferno" -version = "0.11.19" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321f0f839cd44a4686e9504b0a62b4d69a50b62072144c71c68f5873c167b8d9" +checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc" dependencies = [ "ahash 0.8.11", - "clap", - "crossbeam-channel", - "crossbeam-utils", - "dashmap", - "env_logger", - "indexmap 2.2.6", + "indexmap 1.9.3", "is-terminal", "itoa", "log", @@ -2741,30 +2714,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "noir_profiler" -version = "0.31.0" -dependencies = [ - "acir", - "clap", - "codespan-reporting", - "color-eyre", - "const_format", - "fm", - "im", - "inferno", - "nargo", - "noirc_abi", - "noirc_artifacts", - "noirc_driver", - "noirc_errors", - "serde", - "serde_json", - "tempfile", - "tracing-appender", - "tracing-subscriber", -] - [[package]] name = "noir_wasm" version = "0.31.0" diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index f180af01bdfd..129661d157d1 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -24,7 +24,6 @@ members = [ "tooling/noirc_abi", "tooling/noirc_abi_wasm", "tooling/acvm_cli", - "tooling/profiler", # ACVM "acvm-repo/acir_field", "acvm-repo/acir", @@ -37,7 +36,7 @@ members = [ # Utility crates "utils/iter-extended", ] -default-members = ["tooling/nargo_cli", "tooling/acvm_cli", "tooling/profiler"] +default-members = ["tooling/nargo_cli", "tooling/acvm_cli"] resolver = "2" [workspace.package] @@ -85,7 +84,7 @@ acvm_cli = { path = "tooling/acvm_cli" } # Arkworks ark-bn254 = { version = "^0.4.0", default-features = false, features = ["curve"] } ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } -grumpkin = { version = "0.1.0", package = "noir_grumpkin", features = ["std"] } +grumpkin = { version = "0.1.0", package = "noir_grumpkin", features = ["std"] } ark-ec = { version = "^0.4.0", default-features = false } ark-ff = { version = "^0.4.0", default-features = false } ark-std = { version = "^0.4.0", default-features = false } @@ -144,7 +143,6 @@ similar-asserts = "1.5.0" tempfile = "3.6.0" jsonrpc = { version = "0.16.0", features = ["minreq_http"] } flate2 = "1.0.24" -color-eyre = "0.6.2" rand = "0.8.5" proptest = "1.2.0" proptest-derive = "0.4.0" diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs index aadee59f5076..fb7d9eb584ca 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -82,9 +82,9 @@ pub enum BlackBoxFunc { /// /// [grumpkin]: https://hackmd.io/@aztec-network/ByzgNxBfd#2-Grumpkin---A-curve-on-top-of-BN-254-for-SNARK-efficient-group-operations SchnorrVerify, - /// Deprecated. To be removed with a sync from aztec-packages + /// Will be deprecated PedersenCommitment, - /// Deprecated. To be removed with a sync from aztec-packages + /// Will be deprecated PedersenHash, /// Verifies a ECDSA signature over the secp256k1 curve. /// - inputs: @@ -227,8 +227,8 @@ impl BlackBoxFunc { BlackBoxFunc::BigIntToLeBytes => "bigint_to_le_bytes", BlackBoxFunc::Poseidon2Permutation => "poseidon2_permutation", BlackBoxFunc::Sha256Compression => "sha256_compression", - BlackBoxFunc::PedersenCommitment => "deprecated pedersen commitment", - BlackBoxFunc::PedersenHash => "deprecated pedersen hash", + BlackBoxFunc::PedersenCommitment => "pedersen_commitment", + BlackBoxFunc::PedersenHash => "pedersen_hash", } } @@ -257,8 +257,8 @@ impl BlackBoxFunc { "bigint_to_le_bytes" => Some(BlackBoxFunc::BigIntToLeBytes), "poseidon2_permutation" => Some(BlackBoxFunc::Poseidon2Permutation), "sha256_compression" => Some(BlackBoxFunc::Sha256Compression), - "deprecated pedersen commitment" => Some(BlackBoxFunc::PedersenCommitment), - "deprecated pedersen hash" => Some(BlackBoxFunc::PedersenHash), + "pedersen_commitment" => Some(BlackBoxFunc::PedersenCommitment), + "pedersen_hash" => Some(BlackBoxFunc::PedersenHash), _ => None, } } diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 09b39964813b..b8be81fcdef1 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -54,13 +54,13 @@ pub enum BlackBoxFuncCall { message: Vec, output: Witness, }, - /// Deprecated. To be removed with a sync from aztec-packages + /// Will be deprecated PedersenCommitment { inputs: Vec, domain_separator: u32, outputs: (Witness, Witness), }, - /// Deprecated. To be removed with a sync from aztec-packages + /// Will be deprecated PedersenHash { inputs: Vec, domain_separator: u32, @@ -222,6 +222,8 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Blake3 { inputs, .. } | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } + | BlackBoxFuncCall::PedersenCommitment { inputs, .. } + | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(), BlackBoxFuncCall::Keccakf1600 { inputs, .. } => inputs.to_vec(), @@ -318,8 +320,6 @@ impl BlackBoxFuncCall { inputs.push(*key_hash); inputs } - BlackBoxFuncCall::PedersenCommitment { .. } => todo!(), - BlackBoxFuncCall::PedersenHash { .. } => todo!(), } } @@ -341,7 +341,9 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::XOR { output, .. } | BlackBoxFuncCall::SchnorrVerify { output, .. } | BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } + | BlackBoxFuncCall::PedersenHash { output, .. } | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output], + BlackBoxFuncCall::PedersenCommitment { outputs, .. } => vec![outputs.0, outputs.1], BlackBoxFuncCall::MultiScalarMul { outputs, .. } | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } => { vec![outputs.0, outputs.1, outputs.2] @@ -356,8 +358,6 @@ impl BlackBoxFuncCall { vec![] } BlackBoxFuncCall::BigIntToLeBytes { outputs, .. } => outputs.to_vec(), - BlackBoxFuncCall::PedersenCommitment { .. } => todo!(), - BlackBoxFuncCall::PedersenHash { .. } => todo!(), } } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs index b3064c47d82c..0c65759ebcd3 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -18,6 +18,7 @@ pub(crate) mod bigint; mod embedded_curve_ops; mod hash; mod logic; +mod pedersen; mod range; mod signature; pub(crate) mod utils; @@ -26,6 +27,7 @@ use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; // Hash functions should eventually be exposed for external consumers. use hash::{solve_generic_256_hash_opcode, solve_sha_256_permutation_opcode}; use logic::{and, xor}; +use pedersen::{pedersen, pedersen_hash}; pub(crate) use range::solve_range_opcode; use signature::{ ecdsa::{secp256k1_prehashed, secp256r1_prehashed}, @@ -125,6 +127,12 @@ pub(crate) fn solve( message, *output, ), + BlackBoxFuncCall::PedersenCommitment { inputs, domain_separator, outputs } => { + pedersen(backend, initial_witness, inputs, *domain_separator, *outputs) + } + BlackBoxFuncCall::PedersenHash { inputs, domain_separator, output } => { + pedersen_hash(backend, initial_witness, inputs, *domain_separator, *output) + } BlackBoxFuncCall::EcdsaSecp256k1 { public_key_x, public_key_y, @@ -179,7 +187,5 @@ pub(crate) fn solve( BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len } => { solve_poseidon2_permutation_opcode(backend, initial_witness, inputs, outputs, *len) } - BlackBoxFuncCall::PedersenCommitment { .. } => todo!("Deprecated BlackBox"), - BlackBoxFuncCall::PedersenHash { .. } => todo!("Deprecated BlackBox"), } } diff --git a/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs new file mode 100644 index 000000000000..f64a3a794653 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs @@ -0,0 +1,47 @@ +use acir::{ + circuit::opcodes::FunctionInput, + native_types::{Witness, WitnessMap}, + AcirField, +}; + +use crate::{ + pwg::{insert_value, witness_to_value, OpcodeResolutionError}, + BlackBoxFunctionSolver, +}; + +pub(super) fn pedersen( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + inputs: &[FunctionInput], + domain_separator: u32, + outputs: (Witness, Witness), +) -> Result<(), OpcodeResolutionError> { + let scalars: Result, _> = + inputs.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); + let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); + + let (res_x, res_y) = backend.pedersen_commitment(&scalars, domain_separator)?; + + insert_value(&outputs.0, res_x, initial_witness)?; + insert_value(&outputs.1, res_y, initial_witness)?; + + Ok(()) +} + +pub(super) fn pedersen_hash( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + inputs: &[FunctionInput], + domain_separator: u32, + output: Witness, +) -> Result<(), OpcodeResolutionError> { + let scalars: Result, _> = + inputs.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); + let scalars: Vec<_> = scalars?.into_iter().cloned().collect(); + + let res = backend.pedersen_hash(&scalars, domain_separator)?; + + insert_value(&output, res, initial_witness)?; + + Ok(()) +} diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index c07d2d8a4c1d..16fb26e55db1 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 869017f52eec..f729a5033fb6 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -14,6 +14,16 @@ pub trait BlackBoxFunctionSolver { signature: &[u8; 64], message: &[u8], ) -> Result; + fn pedersen_commitment( + &self, + inputs: &[F], + domain_separator: u32, + ) -> Result<(F, F), BlackBoxResolutionError>; + fn pedersen_hash( + &self, + inputs: &[F], + domain_separator: u32, + ) -> Result; fn multi_scalar_mul( &self, points: &[F], @@ -57,6 +67,21 @@ impl BlackBoxFunctionSolver for StubbedBlackBoxSolver { ) -> Result { Err(Self::fail(BlackBoxFunc::SchnorrVerify)) } + fn pedersen_commitment( + &self, + _inputs: &[F], + _domain_separator: u32, + ) -> Result<(F, F), BlackBoxResolutionError> { + Err(Self::fail(BlackBoxFunc::PedersenCommitment)) + } + fn pedersen_hash( + &self, + _inputs: &[F], + _domain_separator: u32, + ) -> Result { + Err(Self::fail(BlackBoxFunc::PedersenHash)) + } + fn multi_scalar_mul( &self, _points: &[F], diff --git a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs index ec69c3797f66..6897116e90ec 100644 --- a/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/noir-repo/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -10,6 +10,7 @@ mod pedersen; mod poseidon2; mod schnorr; +use ark_ec::AffineRepr; pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul}; pub use generator::generators::derive_generators; pub use poseidon2::poseidon2_permutation; @@ -40,6 +41,33 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { )) } + fn pedersen_commitment( + &self, + inputs: &[FieldElement], + domain_separator: u32, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + let inputs: Vec = inputs.iter().map(|input| input.into_repr()).collect(); + let result = pedersen::commitment::commit_native_with_index(&inputs, domain_separator); + let result = if let Some((x, y)) = result.xy() { + (FieldElement::from_repr(*x), FieldElement::from_repr(*y)) + } else { + (FieldElement::from(0_u128), FieldElement::from(0_u128)) + }; + + Ok(result) + } + + fn pedersen_hash( + &self, + inputs: &[FieldElement], + domain_separator: u32, + ) -> Result { + let inputs: Vec = inputs.iter().map(|input| input.into_repr()).collect(); + let result = pedersen::hash::hash_with_index(&inputs, domain_separator); + let result = FieldElement::from_repr(result); + Ok(result) + } + fn multi_scalar_mul( &self, points: &[FieldElement], diff --git a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs index 2b39e279aa8e..60e4af11ea21 100644 --- a/noir/noir-repo/acvm-repo/brillig/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig/src/black_box.rs @@ -61,13 +61,13 @@ pub enum BlackBoxOp { signature: HeapVector, result: MemoryAddress, }, - /// Deprecated. To be removed with a sync from aztec-packages + /// Will be deprecated PedersenCommitment { inputs: HeapVector, domain_separator: MemoryAddress, output: HeapArray, }, - /// Deprecated. To be removed with a sync from aztec-packages + /// Will be deprecated PedersenHash { inputs: HeapVector, domain_separator: MemoryAddress, diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs index 544963b00db3..36d045efabf8 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs @@ -232,6 +232,41 @@ pub(crate) fn evaluate_black_box ); Ok(()) } + BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { + let inputs: Vec = read_heap_vector(memory, inputs) + .iter() + .map(|x| *x.extract_field().unwrap()) + .collect(); + let domain_separator: u32 = + memory.read(*domain_separator).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + BlackBoxFunc::PedersenCommitment, + "Invalid separator length".to_string(), + ) + })?; + let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?; + memory.write_slice( + memory.read_ref(output.pointer), + &[MemoryValue::new_field(x), MemoryValue::new_field(y)], + ); + Ok(()) + } + BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { + let inputs: Vec = read_heap_vector(memory, inputs) + .iter() + .map(|x| *x.extract_field().unwrap()) + .collect(); + let domain_separator: u32 = + memory.read(*domain_separator).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + BlackBoxFunc::PedersenCommitment, + "Invalid separator length".to_string(), + ) + })?; + let hash = solver.pedersen_hash(&inputs, domain_separator)?; + memory.write(*output, MemoryValue::new_field(hash)); + Ok(()) + } BlackBoxOp::BigIntAdd { lhs, rhs, output } => { let lhs = memory.read(*lhs).try_into().unwrap(); let rhs = memory.read(*rhs).try_into().unwrap(); @@ -343,8 +378,6 @@ pub(crate) fn evaluate_black_box Ok(()) } - BlackBoxOp::PedersenCommitment { .. } => todo!("Deprecated Blackbox"), - BlackBoxOp::PedersenHash { .. } => todo!("Deprecated Blackbox"), } } diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 580a132aa5a4..a36b7b17d097 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -7,7 +7,7 @@ use transforms::{ contract_interface::{ generate_contract_interface, stub_function, update_fn_signatures_in_contract_interface, }, - events::{generate_event_impls, transform_event_abi}, + events::{generate_selector_impl, transform_events}, functions::{ check_for_public_args, export_fn_abi, transform_function, transform_unconstrained, }, @@ -65,14 +65,19 @@ fn transform( // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&file_id, &mut submodule.contents, submodule.name.0.contents.as_str()) - .map_err(|err| (err.into(), file_id))? + if transform_module( + crate_id, + &file_id, + context, + &mut submodule.contents, + submodule.name.0.contents.as_str(), + ) + .map_err(|err| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; } } - generate_event_impls(&mut ast).map_err(|err| (err.into(), file_id))?; generate_note_interface_impl(&mut ast).map_err(|err| (err.into(), file_id))?; Ok(ast) @@ -82,7 +87,9 @@ fn transform( /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise fn transform_module( + crate_id: &CrateId, file_id: &FileId, + context: &HirContext, module: &mut SortedModule, module_name: &str, ) -> Result { @@ -99,7 +106,19 @@ fn transform_module( if !check_for_storage_implementation(module, storage_struct_name) { generate_storage_implementation(module, storage_struct_name)?; } - generate_storage_layout(module, storage_struct_name.clone(), module_name)?; + // Make sure we're only generating the storage layout for the root crate + // In case we got a contract importing other contracts for their interface, we + // don't want to generate the storage layout for them + if crate_id == context.root_crate_id() { + generate_storage_layout(module, storage_struct_name.clone())?; + } + } + + for structure in module.types.iter_mut() { + if structure.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { + module.impls.push(generate_selector_impl(structure)); + has_transformed_module = true; + } } let has_initializer = module.functions.iter().any(|func| { @@ -200,7 +219,7 @@ fn transform_module( }); } - generate_contract_interface(module, module_name, &stubs, storage_defined)?; + generate_contract_interface(module, module_name, &stubs)?; } Ok(has_transformed_module) @@ -216,7 +235,7 @@ fn transform_hir( context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { if has_aztec_dependency(crate_id, context) { - transform_event_abi(crate_id, context)?; + transform_events(crate_id, context)?; inject_compute_note_hash_and_optionally_a_nullifier(crate_id, context)?; assign_storage_slots(crate_id, context)?; inject_note_exports(crate_id, context)?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs index 30c0f63a2d45..40fde39a06f9 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs @@ -176,7 +176,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( format!( " unconstrained fn compute_note_hash_and_optionally_a_nullifier( - contract_address: dep::aztec::protocol_types::address::AztecAddress, + contract_address: aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, @@ -194,7 +194,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( let if_statements: Vec = note_types.iter().map(|note_type| format!( "if (note_type_id == {0}::get_note_type_id()) {{ - dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier({0}::deserialize_content, note_header, compute_nullifier, serialized_note) + aztec::note::utils::compute_note_hash_and_optionally_a_nullifier({0}::deserialize_content, note_header, compute_nullifier, serialized_note) }}" , note_type)).collect(); @@ -208,14 +208,14 @@ fn generate_compute_note_hash_and_optionally_a_nullifier_source( format!( " unconstrained fn compute_note_hash_and_optionally_a_nullifier( - contract_address: dep::aztec::protocol_types::address::AztecAddress, + contract_address: aztec::protocol_types::address::AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field, compute_nullifier: bool, serialized_note: [Field; {}], ) -> pub [Field; 4] {{ - let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); + let note_header = aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); {} }}", diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index bdf1a6f16ccf..e6ac43ad2c41 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -30,8 +30,8 @@ use crate::utils::{ // for i in 0..third_arg.len() { // args_acc = args_acc.append(third_arg[i].serialize().as_slice()); // } -// let args_hash = dep::aztec::hash::hash_args(args_acc); -// assert(args_hash == dep::aztec::oracle::arguments::pack_arguments(args_acc)); +// let args_hash = aztec::hash::hash_args(args_acc); +// assert(args_hash == aztec::oracle::arguments::pack_arguments(args_acc)); // PublicCallInterface { // target_contract: self.target_contract, // selector: FunctionSelector::from_signature("SELECTOR_PLACEHOLDER"), @@ -57,14 +57,18 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call let fn_return_type: noirc_frontend::ast::UnresolvedType = func.return_type(); let fn_selector = format!( - "dep::aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature(\"{}\")", + "aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature(\"{}\")", SELECTOR_PLACEHOLDER ); let parameters = func.parameters(); let is_void = if matches!(fn_return_type.typ, UnresolvedTypeData::Unit) { "Void" } else { "" }; let is_static = if is_static_call { "Static" } else { "" }; - let return_type_hint = fn_return_type.typ.to_string().replace("plain::", ""); + let return_type_hint = if is_void == "Void" { + "".to_string() + } else { + format!("<{}>", fn_return_type.typ.to_string().replace("plain::", "")) + }; let call_args = parameters .iter() .map(|arg| { @@ -72,95 +76,48 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call match &arg.typ.typ { UnresolvedTypeData::Array(_, typ) => { format!( - "let serialized_{0} = {0}.map(|x: {1}| x.serialize()); - for i in 0..{0}.len() {{ - args_acc = args_acc.append(serialized_{0}[i].as_slice()); - }}\n", + "let hash_{0} = {0}.map(|x: {1}| x.serialize()); + for i in 0..{0}.len() {{ + args_acc = args_acc.append(hash_{0}[i].as_slice()); + }}\n", param_name, typ.typ.to_string().replace("plain::", "") ) } - UnresolvedTypeData::Named(_, _, _) | UnresolvedTypeData::String(_) => { - format!("args_acc = args_acc.append({}.serialize().as_slice());\n", param_name) - } _ => { - format!("args_acc = args_acc.append(&[{}.to_field()]);\n", param_name) + format!("args_acc = args_acc.append({}.serialize().as_slice());\n", param_name) } } }) .collect::>() .join(""); - - let param_types = if !parameters.is_empty() { - parameters - .iter() - .map(|param| param.pattern.name_ident().0.contents.clone()) - .collect::>() - .join(", ") - } else { - "".to_string() - }; - - let original = format!( - "| inputs: dep::aztec::context::inputs::{}ContextInputs | -> {} {{ - {}(inputs{}) - }}", - aztec_visibility, - if aztec_visibility == "Private" { - "dep::aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs".to_string() - } else { - return_type_hint.clone() - }, - fn_name, - if param_types.is_empty() { "".to_string() } else { format!(" ,{} ", param_types) } - ); - let arg_types = format!( - "({}{})", - parameters - .iter() - .map(|param| param.typ.typ.to_string().replace("plain::", "")) - .collect::>() - .join(","), - // In order to distinguish between a single element Tuple (Type,) and a single type with unnecessary parenthesis around it (Type), - // The latter gets simplified to Type, that is NOT a valid env - if parameters.len() == 1 { "," } else { "" } - ); - - let generics = if is_void == "Void" { - format!("{}>", arg_types) - } else { - format!("{}, {}>", return_type_hint, arg_types) - }; - - let fn_body = if aztec_visibility != "Public" { + if aztec_visibility != "Public" { let args_hash = if !parameters.is_empty() { format!( "let mut args_acc: [Field] = &[]; {} - let args_hash = dep::aztec::hash::hash_args(args_acc); - assert(args_hash == dep::aztec::oracle::arguments::pack_arguments(args_acc));", + let args_hash = aztec::hash::hash_args(args_acc); + assert(args_hash == aztec::oracle::arguments::pack_arguments(args_acc));", call_args ) } else { - " - let mut args_acc: [Field] = &[]; - let args_hash = 0; - " - .to_string() + "let args_hash = 0;".to_string() }; - format!( + let fn_body = format!( "{} - let selector = {}; - dep::aztec::context::{}{}{}CallInterface {{ - target_contract: self.target_contract, - selector, - name: \"{}\", - args_hash, - args: args_acc, - original: {} - }}", - args_hash, fn_selector, aztec_visibility, is_static, is_void, fn_name, original + aztec::context::{}{}{}CallInterface {{ + target_contract: self.target_contract, + selector: {}, + args_hash, + }}", + args_hash, aztec_visibility, is_static, is_void, fn_selector, + ); + format!( + "pub fn {}(self, {}) -> aztec::context::{}{}{}CallInterface{} {{ + {} + }}", + fn_name, fn_parameters, aztec_visibility, is_static, is_void, return_type_hint, fn_body ) } else { let args = format!( @@ -169,34 +126,23 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction, is_static_call ", call_args ); - format!( + let fn_body = format!( "{} - let selector = {}; - dep::aztec::context::{}{}{}CallInterface {{ + aztec::context::Public{}{}CallInterface {{ target_contract: self.target_contract, - selector, - name: \"{}\", + selector: {}, args: args_acc, - gas_opts: dep::aztec::context::gas::GasOpts::default(), - original: {} + gas_opts: aztec::context::gas::GasOpts::default(), + }}", + args, is_static, is_void, fn_selector, + ); + format!( + "pub fn {}(self, {}) -> aztec::context::Public{}{}CallInterface{} {{ + {} }}", - args, fn_selector, aztec_visibility, is_static, is_void, fn_name, original + fn_name, fn_parameters, is_static, is_void, return_type_hint, fn_body ) - }; - - format!( - "pub fn {}(self, {}) -> dep::aztec::context::{}{}{}CallInterface<{},{} {{ - {} - }}", - fn_name, - fn_parameters, - aztec_visibility, - is_static, - is_void, - fn_name.len(), - generics, - fn_body - ) + } } // Generates the contract interface as a struct with an `at` function that holds the stubbed functions and provides @@ -206,55 +152,32 @@ pub fn generate_contract_interface( module: &mut SortedModule, module_name: &str, stubs: &[(String, Location)], - has_storage_layout: bool, ) -> Result<(), AztecMacroError> { - let storage_layout_getter = format!( - "#[contract_library_method] - pub fn storage() -> StorageLayout {{ - {}_STORAGE_LAYOUT - }}", - module_name, - ); let contract_interface = format!( " struct {0} {{ - target_contract: dep::aztec::protocol_types::address::AztecAddress + target_contract: aztec::protocol_types::address::AztecAddress }} impl {0} {{ {1} pub fn at( - target_contract: dep::aztec::protocol_types::address::AztecAddress + target_contract: aztec::protocol_types::address::AztecAddress ) -> Self {{ Self {{ target_contract }} }} - - pub fn interface() -> Self {{ - Self {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} - }} - - {2} }} #[contract_library_method] pub fn at( - target_contract: dep::aztec::protocol_types::address::AztecAddress + target_contract: aztec::protocol_types::address::AztecAddress ) -> {0} {{ {0} {{ target_contract }} }} - - #[contract_library_method] - pub fn interface() -> {0} {{ - {0} {{ target_contract: dep::aztec::protocol_types::address::AztecAddress::zero() }} - }} - - {3} ", module_name, stubs.iter().map(|(src, _)| src.to_owned()).collect::>().join("\n"), - if has_storage_layout { storage_layout_getter.clone() } else { "".to_string() }, - if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() } ); let (contract_interface_ast, errors) = parse_program(&contract_interface); @@ -271,7 +194,7 @@ pub fn generate_contract_interface( .iter() .enumerate() .map(|(i, (method, orig_span))| { - if method.name() == "at" || method.name() == "interface" || method.name() == "storage" { + if method.name() == "at" { (method.clone(), *orig_span) } else { let (_, new_location) = stubs[i]; @@ -285,9 +208,7 @@ pub fn generate_contract_interface( module.types.push(contract_interface_ast.types.pop().unwrap()); module.impls.push(impl_with_locations); - for function in contract_interface_ast.functions { - module.functions.push(function); - } + module.functions.push(contract_interface_ast.functions.pop().unwrap()); Ok(()) } @@ -326,7 +247,7 @@ pub fn update_fn_signatures_in_contract_interface( let name = context.def_interner.function_name(func_id); let fn_parameters = &context.def_interner.function_meta(func_id).parameters.clone(); - if name == "at" || name == "interface" || name == "storage" { + if name == "at" { continue; } @@ -339,29 +260,42 @@ pub fn update_fn_signatures_in_contract_interface( .collect::>(), ); let hir_func = context.def_interner.function(func_id).block(&context.def_interner); - - let function_selector_statement = context.def_interner.statement( - hir_func.statements().get(hir_func.statements().len() - 2).ok_or(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "Function signature statement not found, invalid body length" - .to_string(), - ), - }, - file_id, - ))?, + let call_interface_constructor_statement = context.def_interner.statement( + hir_func + .statements() + .last() + .ok_or((AztecMacroError::AztecDepNotFound, file_id))?, ); - let function_selector_expression_id = match function_selector_statement { - HirStatement::Let(let_statement) => Ok(let_statement.expression), - _ => Err(( - AztecMacroError::CouldNotGenerateContractInterface { - secondary_message: Some( - "Function selector statement must be an expression".to_string(), - ), - }, - file_id, - )), - }?; + let call_interface_constructor_expression = + match call_interface_constructor_statement { + HirStatement::Expression(expression_id) => { + match context.def_interner.expression(&expression_id) { + HirExpression::Constructor(hir_constructor_expression) => { + Ok(hir_constructor_expression) + } + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "CallInterface constructor statement must be a constructor expression" + .to_string(), + ), + }, + file_id, + )), + } + } + _ => Err(( + AztecMacroError::CouldNotGenerateContractInterface { + secondary_message: Some( + "CallInterface constructor statement must be an expression" + .to_string(), + ), + }, + file_id, + )), + }?; + let (_, function_selector_expression_id) = + call_interface_constructor_expression.fields[1]; let function_selector_expression = context.def_interner.expression(&function_selector_expression_id); diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index 748032f63007..69cb6ddafc3e 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -1,306 +1,178 @@ -use noirc_frontend::ast::{ItemVisibility, NoirFunction, NoirTraitImpl, TraitImplItem}; -use noirc_frontend::macros_api::{NodeInterner, StructId}; -use noirc_frontend::token::SecondaryAttribute; +use iter_extended::vecmap; +use noirc_errors::Span; +use noirc_frontend::ast::{ + ExpressionKind, FunctionDefinition, FunctionReturnType, ItemVisibility, Literal, NoirFunction, + Visibility, +}; use noirc_frontend::{ graph::CrateId, - macros_api::{FileId, HirContext}, - parse_program, - parser::SortedModule, + macros_api::{ + BlockExpression, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, + NoirStruct, PathKind, StatementKind, StructId, StructType, Type, TypeImpl, + UnresolvedTypeData, + }, + token::SecondaryAttribute, }; -use crate::utils::hir_utils::collect_crate_structs; -use crate::utils::{ast_utils::is_custom_attribute, errors::AztecMacroError}; - -// Automatic implementation of most of the methods in the EventInterface trait, guiding the user with meaningful error messages in case some -// methods must be implemented manually. -pub fn generate_event_impls(module: &mut SortedModule) -> Result<(), AztecMacroError> { - // Find structs annotated with #[aztec(event)] - // Why doesn't this work ? Events are not tagged and do not appear, it seems only going through the submodule works - // let annotated_event_structs = module - // .types - // .iter_mut() - // .filter(|typ| typ.attributes.iter().any(|attr: &SecondaryAttribute| is_custom_attribute(attr, "aztec(event)"))); - // This did not work because I needed the submodule itself to add the trait impl back in to, but it would be nice if it was tagged on the module level - // let mut annotated_event_structs = module.submodules.iter_mut() - // .flat_map(|submodule| submodule.contents.types.iter_mut()) - // .filter(|typ| typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)"))); - - // To diagnose - // let test = module.types.iter_mut(); - // for event_struct in test { - // print!("\ngenerate_event_interface_impl COUNT: {}\n", event_struct.name.0.contents); - // } - - for submodule in module.submodules.iter_mut() { - let annotated_event_structs = submodule.contents.types.iter_mut().filter(|typ| { - typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) - }); - - for event_struct in annotated_event_structs { - // event_struct.attributes.push(SecondaryAttribute::Abi("events".to_string())); - // If one impl is pushed, this doesn't throw the "#[abi(tag)] attributes can only be used in contracts" error - // But if more than one impl is pushed, we get an increasing amount of "#[abi(tag)] attributes can only be used in contracts" errors - // We work around this by doing this addition in the HIR pass via transform_event_abi below. - - let event_type = event_struct.name.0.contents.to_string(); - let event_len = event_struct.fields.len() as u32; - // event_byte_len = event fields * 32 + randomness (32) + event_type_id (32) - let event_byte_len = event_len * 32 + 64; - - let mut event_fields = vec![]; - - for (field_ident, field_type) in event_struct.fields.iter() { - event_fields.push(( - field_ident.0.contents.to_string(), - field_type.typ.to_string().replace("plain::", ""), - )); - } - - let mut event_interface_trait_impl = - generate_trait_impl_stub_event_interface(event_type.as_str(), event_byte_len)?; - event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_get_event_type_id(event_type.as_str(), event_len)?, - )); - event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_private_to_be_bytes(event_type.as_str(), event_byte_len)?, - )); - event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_to_be_bytes(event_type.as_str(), event_byte_len)?, - )); - submodule.contents.trait_impls.push(event_interface_trait_impl); - - let serialize_trait_impl = - generate_trait_impl_serialize(event_type.as_str(), event_len, &event_fields)?; - submodule.contents.trait_impls.push(serialize_trait_impl); - - let deserialize_trait_impl = - generate_trait_impl_deserialize(event_type.as_str(), event_len, &event_fields)?; - submodule.contents.trait_impls.push(deserialize_trait_impl); - } - } - - Ok(()) -} - -fn generate_trait_impl_stub_event_interface( - event_type: &str, - byte_length: u32, -) -> Result { - let byte_length_without_randomness = byte_length - 32; - let trait_impl_source = format!( - " -impl dep::aztec::event::event_interface::EventInterface<{byte_length}, {byte_length_without_randomness}> for {event_type} {{ - }} - " - ) - .to_string(); - - let (parsed_ast, errors) = parse_program(&trait_impl_source); - if !errors.is_empty() { - dbg!(errors); - return Err(AztecMacroError::CouldNotImplementEventInterface { - secondary_message: Some(format!("Failed to parse Noir macro code (trait impl of {event_type} for EventInterface). This is either a bug in the compiler or the Noir macro code")), - }); - } - - let mut sorted_ast = parsed_ast.into_sorted(); - let event_interface_impl = sorted_ast.trait_impls.remove(0); - - Ok(event_interface_impl) -} - -fn generate_trait_impl_serialize( - event_type: &str, - event_len: u32, - event_fields: &[(String, String)], -) -> Result { - let field_names = - event_fields.iter().map(|field| format!("self.{}", field.0)).collect::>(); - let field_input = field_names.join(","); - - let trait_impl_source = format!( - " - impl dep::aztec::protocol_types::traits::Serialize<{event_len}> for {event_type} {{ - fn serialize(self: {event_type}) -> [Field; {event_len}] {{ - [{field_input}] - }} - }} - " - ) - .to_string(); - - let (parsed_ast, errors) = parse_program(&trait_impl_source); - if !errors.is_empty() { - dbg!(errors); - return Err(AztecMacroError::CouldNotImplementEventInterface { - secondary_message: Some(format!("Failed to parse Noir macro code (trait impl of Serialize for {event_type}). This is either a bug in the compiler or the Noir macro code")), - }); - } - - let mut sorted_ast = parsed_ast.into_sorted(); - let serialize_impl = sorted_ast.trait_impls.remove(0); - - Ok(serialize_impl) -} - -fn generate_trait_impl_deserialize( - event_type: &str, - event_len: u32, - event_fields: &[(String, String)], -) -> Result { - let field_names: Vec = event_fields - .iter() - .enumerate() - .map(|(index, field)| format!("{}: fields[{}]", field.0, index)) - .collect::>(); - let field_input = field_names.join(","); - - let trait_impl_source = format!( - " - impl dep::aztec::protocol_types::traits::Deserialize<{event_len}> for {event_type} {{ - fn deserialize(fields: [Field; {event_len}]) -> {event_type} {{ - {event_type} {{ {field_input} }} - }} - }} - " - ) - .to_string(); +use crate::{ + chained_dep, + utils::{ + ast_utils::{ + call, expression, ident, ident_path, is_custom_attribute, make_statement, make_type, + path, variable_path, + }, + constants::SIGNATURE_PLACEHOLDER, + errors::AztecMacroError, + hir_utils::{collect_crate_structs, signature_of_type}, + }, +}; - let (parsed_ast, errors) = parse_program(&trait_impl_source); - if !errors.is_empty() { - dbg!(errors); - return Err(AztecMacroError::CouldNotImplementEventInterface { - secondary_message: Some(format!("Failed to parse Noir macro code (trait impl of Deserialize for {event_type}). This is either a bug in the compiler or the Noir macro code")), - }); +/// Generates the impl for an event selector +/// +/// Inserts the following code: +/// ```noir +/// impl SomeStruct { +/// fn selector() -> FunctionSelector { +/// aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature("SIGNATURE_PLACEHOLDER") +/// } +/// } +/// ``` +/// +/// This allows developers to emit events without having to write the signature of the event every time they emit it. +/// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. +/// It'll get resolved after by transforming the HIR. +pub fn generate_selector_impl(structure: &mut NoirStruct) -> TypeImpl { + structure.attributes.push(SecondaryAttribute::Abi("events".to_string())); + let struct_type = + make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); + + let selector_path = + chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); + let mut from_signature_path = selector_path.clone(); + from_signature_path.segments.push(ident("from_signature")); + + let selector_fun_body = BlockExpression { + statements: vec![make_statement(StatementKind::Expression(call( + variable_path(from_signature_path), + vec![expression(ExpressionKind::Literal(Literal::Str( + SIGNATURE_PLACEHOLDER.to_string(), + )))], + )))], + }; + + // Define `FunctionSelector` return type + let return_type = + FunctionReturnType::Ty(make_type(UnresolvedTypeData::Named(selector_path, vec![], true))); + + let mut selector_fn_def = FunctionDefinition::normal( + &ident("selector"), + &vec![], + &[], + &selector_fun_body, + &[], + &return_type, + ); + + selector_fn_def.visibility = ItemVisibility::Public; + + // Seems to be necessary on contract modules + selector_fn_def.return_visibility = Visibility::Public; + + TypeImpl { + object_type: struct_type, + type_span: structure.span, + generics: vec![], + methods: vec![(NoirFunction::normal(selector_fn_def), Span::default())], } - - let mut sorted_ast = parsed_ast.into_sorted(); - let deserialize_impl = sorted_ast.trait_impls.remove(0); - - Ok(deserialize_impl) } -fn generate_fn_get_event_type_id( - event_type: &str, - field_length: u32, -) -> Result { - let from_signature_input = - std::iter::repeat("Field").take(field_length as usize).collect::>().join(","); - let function_source = format!( - " - fn get_event_type_id() -> dep::aztec::protocol_types::abis::event_selector::EventSelector {{ - dep::aztec::protocol_types::abis::event_selector::EventSelector::from_signature(\"{event_type}({from_signature_input})\") - }} - ", - ) - .to_string(); - - let (function_ast, errors) = parse_program(&function_source); - if !errors.is_empty() { - dbg!(errors); - return Err(AztecMacroError::CouldNotImplementEventInterface { - secondary_message: Some(format!("Failed to parse Noir macro code (fn get_event_type_id, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), - }); - } - - let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.visibility = ItemVisibility::Public; - Ok(noir_fn) +/// Computes the signature for a resolved event type. +/// It has the form 'EventName(Field,(Field),[u8;2])' +fn event_signature(event: &StructType) -> String { + let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); + format!("{}({})", event.name.0.contents, fields.join(",")) } -fn generate_fn_private_to_be_bytes( - event_type: &str, - byte_length: u32, -) -> Result { - let function_source = format!( - " - fn private_to_be_bytes(self: {event_type}, randomness: Field) -> [u8; {byte_length}] {{ - let mut buffer: [u8; {byte_length}] = [0; {byte_length}]; - - let randomness_bytes = randomness.to_be_bytes(32); - let event_type_id_bytes = {event_type}::get_event_type_id().to_field().to_be_bytes(32); - - for i in 0..32 {{ - buffer[i] = randomness_bytes[i]; - buffer[32 + i] = event_type_id_bytes[i]; - }} - - let serialized_event = self.serialize(); - - for i in 0..serialized_event.len() {{ - let bytes = serialized_event[i].to_be_bytes(32); - for j in 0..32 {{ - buffer[64 + i * 32 + j] = bytes[j]; - }} - }} - - buffer - }} - " - ) - .to_string(); - - let (function_ast, errors) = parse_program(&function_source); - if !errors.is_empty() { - dbg!(errors); - return Err(AztecMacroError::CouldNotImplementEventInterface { - secondary_message: Some(format!("Failed to parse Noir macro code (fn private_to_be_bytes, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), - }); +/// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. +fn transform_event( + struct_id: StructId, + interner: &mut NodeInterner, +) -> Result<(), (AztecMacroError, FileId)> { + let struct_type = interner.get_struct(struct_id); + let selector_id = interner + .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Selector method not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + let selector_function = interner.function(&selector_id); + + let compute_selector_statement = interner.statement( + selector_function.block(interner).statements().first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?, + ); + + let compute_selector_expression = match compute_selector_statement { + HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { + HirExpression::Call(hir_call_expression) => Some(hir_call_expression), + _ => None, + }, + _ => None, } - - let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.visibility = ItemVisibility::Public; - Ok(noir_fn) -} - -fn generate_fn_to_be_bytes( - event_type: &str, - byte_length: u32, -) -> Result { - let byte_length_without_randomness = byte_length - 32; - let function_source = format!( - " - fn to_be_bytes(self: {event_type}) -> [u8; {byte_length_without_randomness}] {{ - let mut buffer: [u8; {byte_length_without_randomness}] = [0; {byte_length_without_randomness}]; - - let event_type_id_bytes = {event_type}::get_event_type_id().to_field().to_be_bytes(32); - - for i in 0..32 {{ - buffer[i] = event_type_id_bytes[i]; - }} - - let serialized_event = self.serialize(); - - for i in 0..serialized_event.len() {{ - let bytes = serialized_event[i].to_be_bytes(32); - for j in 0..32 {{ - buffer[32 + i * 32 + j] = bytes[j]; - }} - }} - - buffer - }} - ") - .to_string(); - - let (function_ast, errors) = parse_program(&function_source); - if !errors.is_empty() { - dbg!(errors); - return Err(AztecMacroError::CouldNotImplementEventInterface { - secondary_message: Some(format!("Failed to parse Noir macro code (fn to_be_bytes, implemented for EventInterface of {event_type}). This is either a bug in the compiler or the Noir macro code")), - }); + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + match interner.expression(first_arg_id) { + HirExpression::Literal(HirLiteral::Str(signature)) + if signature == SIGNATURE_PLACEHOLDER => + { + let selector_literal_id = *first_arg_id; + + let structure = interner.get_struct(struct_id); + let signature = event_signature(&structure.borrow()); + interner.update_expression(selector_literal_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); + }); + + // Also update the type! It might have a different length now than the placeholder. + interner.push_expr_type( + selector_literal_id, + Type::String(Box::new(Type::Constant(signature.len() as u32))), + ); + Ok(()) + } + _ => Err(( + AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Signature placeholder literal does not match".to_owned(), + }, + struct_type.borrow().location.file, + )), } - - let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.visibility = ItemVisibility::Public; - Ok(noir_fn) } -// We do this pass in the HIR to work around the "#[abi(tag)] attributes can only be used in contracts" error -pub fn transform_event_abi( +pub fn transform_events( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { @@ -312,14 +184,3 @@ pub fn transform_event_abi( } Ok(()) } - -fn transform_event( - struct_id: StructId, - interner: &mut NodeInterner, -) -> Result<(), (AztecMacroError, FileId)> { - interner.update_struct_attributes(struct_id, |struct_attributes| { - struct_attributes.push(SecondaryAttribute::Abi("events".to_string())); - }); - - Ok(()) -} diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index 71177c25f30d..f0e7d0d50341 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -190,19 +190,19 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt } if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { - let compute_note_content_hash_fn = + let get_header_fn = generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; - trait_impl.items.push(TraitImplItem::Function(compute_note_content_hash_fn)); + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); } if !check_trait_method_implemented(trait_impl, "to_be_bytes") { - let to_be_bytes_fn = generate_note_to_be_bytes( + let get_header_fn = generate_note_to_be_bytes( ¬e_type, note_bytes_len.as_str(), note_serialized_len.as_str(), note_interface_impl_span, )?; - trait_impl.items.push(TraitImplItem::Function(to_be_bytes_fn)); + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); } } @@ -268,7 +268,7 @@ fn generate_note_get_header( ) -> Result { let function_source = format!( " - fn get_header(note: {}) -> dep::aztec::note::note_header::NoteHeader {{ + fn get_header(note: {}) -> aztec::note::note_header::NoteHeader {{ note.{} }} ", @@ -299,7 +299,7 @@ fn generate_note_set_header( ) -> Result { let function_source = format!( " - fn set_header(self: &mut {}, header: dep::aztec::note::note_header::NoteHeader) {{ + fn set_header(self: &mut {}, header: aztec::note::note_header::NoteHeader) {{ self.{} = header; }} ", @@ -488,7 +488,7 @@ fn generate_note_properties_fn( // Automatically generate the method to compute the note's content hash as: // fn compute_note_content_hash(self: NoteType) -> Field { -// dep::aztec::hash::pedersen_hash(self.serialize_content(), dep::aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) +// aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) // } // fn generate_compute_note_content_hash( @@ -498,7 +498,7 @@ fn generate_compute_note_content_hash( let function_source = format!( " fn compute_note_content_hash(self: {}) -> Field {{ - dep::aztec::hash::pedersen_hash(self.serialize_content(), dep::aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) + aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) }} ", note_type @@ -557,10 +557,7 @@ fn generate_note_properties_struct_source( .iter() .filter_map(|(field_name, _)| { if field_name != note_header_field_name { - Some(format!( - "{}: dep::aztec::note::note_getter_options::PropertySelector", - field_name - )) + Some(format!("{}: aztec::note::note_getter_options::PropertySelector", field_name)) } else { None } @@ -588,7 +585,7 @@ fn generate_note_properties_fn_source( .filter_map(|(index, (field_name, _))| { if field_name != note_header_field_name { Some(format!( - "{}: dep::aztec::note::note_getter_options::PropertySelector {{ index: {}, offset: 0, length: 32 }}", + "{}: aztec::note::note_getter_options::PropertySelector {{ index: {}, offset: 0, length: 32 }}", field_name, index )) @@ -665,9 +662,7 @@ fn generate_note_deserialize_content_source( ) } } else { - format!( - "{note_header_field_name}: dep::aztec::note::note_header::NoteHeader::empty()", - ) + format!("{}: aztec::note::note_header::NoteHeader::empty()", note_header_field_name) } }) .collect::>() diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index bac87502c7d1..8b778b4cca67 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -497,7 +497,6 @@ pub fn assign_storage_slots( pub fn generate_storage_layout( module: &mut SortedModule, storage_struct_name: String, - module_name: &str, ) -> Result<(), AztecMacroError> { let definition = module .types @@ -505,28 +504,33 @@ pub fn generate_storage_layout( .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) .unwrap(); + let mut generic_args = vec![]; let mut storable_fields = vec![]; let mut storable_fields_impl = vec![]; - definition.fields.iter().for_each(|(field_ident, _)| { - storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident)); - storable_fields_impl - .push(format!("{}: dep::aztec::prelude::Storable {{ slot: 0 }}", field_ident,)); + definition.fields.iter().enumerate().for_each(|(index, (field_ident, field_type))| { + storable_fields.push(format!("{}: aztec::prelude::Storable", field_ident, index)); + generic_args.push(format!("N{}", index)); + storable_fields_impl.push(format!( + "{}: aztec::prelude::Storable {{ slot: 0, typ: \"{}\" }}", + field_ident, + field_type.to_string().replace("plain::", "") + )); }); let storage_fields_source = format!( " - struct StorageLayout {{ + struct StorageLayout<{}> {{ {} }} #[abi(storage)] - global {}_STORAGE_LAYOUT = StorageLayout {{ + global STORAGE_LAYOUT = StorageLayout {{ {} }}; ", + generic_args.join(", "), storable_fields.join(",\n"), - module_name, storable_fields_impl.join(",\n") ); diff --git a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs index 48b3b25747be..4467c4bca4b5 100644 --- a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs @@ -161,7 +161,7 @@ macro_rules! chained_dep { ( $base:expr $(, $tail:expr)* ) => { { let mut base_path = ident_path($base); - base_path.kind = PathKind::Dep; + base_path.kind = PathKind::Plain; $( base_path.segments.push(ident($tail)); )* diff --git a/noir/noir-repo/aztec_macros/src/utils/constants.rs b/noir/noir-repo/aztec_macros/src/utils/constants.rs index 2178f7a25264..848cca0477d8 100644 --- a/noir/noir-repo/aztec_macros/src/utils/constants.rs +++ b/noir/noir-repo/aztec_macros/src/utils/constants.rs @@ -1,3 +1,4 @@ pub const FUNCTION_TREE_HEIGHT: u32 = 5; pub const MAX_CONTRACT_PRIVATE_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); +pub const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; pub const SELECTOR_PLACEHOLDER: &str = "SELECTOR_PLACEHOLDER"; diff --git a/noir/noir-repo/aztec_macros/src/utils/errors.rs b/noir/noir-repo/aztec_macros/src/utils/errors.rs index 557d065cb25b..852b5f1e57ac 100644 --- a/noir/noir-repo/aztec_macros/src/utils/errors.rs +++ b/noir/noir-repo/aztec_macros/src/utils/errors.rs @@ -14,7 +14,6 @@ pub enum AztecMacroError { CouldNotAssignStorageSlots { secondary_message: Option }, CouldNotImplementComputeNoteHashAndOptionallyANullifier { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, - CouldNotImplementEventInterface { secondary_message: Option }, MultipleStorageDefinitions { span: Option }, CouldNotExportStorageLayout { span: Option, secondary_message: Option }, CouldNotInjectContextGenericInStorage { secondary_message: Option }, @@ -68,11 +67,6 @@ impl From for MacroError { secondary_message, span }, - AztecMacroError::CouldNotImplementEventInterface { secondary_message } => MacroError { - primary_message: "Could not implement automatic methods for event, please provide an implementation of the EventInterface trait".to_string(), - secondary_message, - span: None, - }, AztecMacroError::MultipleStorageDefinitions { span } => MacroError { primary_message: "Only one struct can be tagged as #[aztec(storage)]".to_string(), secondary_message: None, diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index dde2bd08d747..4ba9b85f967b 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -544,14 +544,15 @@ pub fn compile_no_check( return Ok(cached_program.expect("cache must exist for hashes to match")); } let return_visibility = program.return_visibility; + let ssa_evaluator_options = noirc_evaluator::ssa::SsaEvaluatorOptions { + enable_ssa_logging: options.show_ssa, + enable_brillig_logging: options.show_brillig, + force_brillig_output: options.force_brillig, + print_codegen_timings: options.benchmark_codegen, + }; - let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = create_program( - program, - options.show_ssa, - options.show_brillig, - options.force_brillig, - options.benchmark_codegen, - )?; + let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = + create_program(program, &ssa_evaluator_options)?; let abi = abi_gen::gen_abi(context, &main_function, return_visibility, error_types); let file_map = filter_relevant_files(&debug, &context.file_manager); diff --git a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs index 42cab72345d6..cb5abbe20797 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs @@ -202,14 +202,14 @@ fn stack_trace<'files>( let path = files.name(call_item.file).expect("should get file path"); let source = files.source(call_item.file).expect("should get file source"); - let (line, column) = line_and_column_from_span(source.as_ref(), &call_item.span); + let (line, column) = location(source.as_ref(), call_item.span.start()); result += &format!("{}. {}:{}:{}\n", i + 1, path, line, column); } result } -pub fn line_and_column_from_span(source: &str, span: &Span) -> (u32, u32) { +fn location(source: &str, span_start: u32) -> (u32, u32) { let mut line = 1; let mut column = 0; @@ -221,7 +221,7 @@ pub fn line_and_column_from_span(source: &str, span: &Span) -> (u32, u32) { column = 0; } - if span.start() <= i as u32 { + if span_start <= i as u32 { break; } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index c62365162ba7..367cdbe4973b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -137,6 +137,39 @@ pub(crate) fn convert_black_box_call( ) } } + + BlackBoxFunc::PedersenCommitment => { + if let ( + [message, BrilligVariable::SingleAddr(domain_separator)], + [BrilligVariable::BrilligArray(result_array)], + ) = (function_arguments, function_results) + { + let message_vector = convert_array_or_vector(brillig_context, message, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::PedersenCommitment { + inputs: message_vector.to_heap_vector(), + domain_separator: domain_separator.address, + output: result_array.to_heap_array(), + }); + } else { + unreachable!("ICE: Pedersen expects one array argument, a register for the domain separator, and one array result") + } + } + BlackBoxFunc::PedersenHash => { + if let ( + [message, BrilligVariable::SingleAddr(domain_separator)], + [BrilligVariable::SingleAddr(result)], + ) = (function_arguments, function_results) + { + let message_vector = convert_array_or_vector(brillig_context, message, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::PedersenHash { + inputs: message_vector.to_heap_vector(), + domain_separator: domain_separator.address, + output: result.address, + }); + } else { + unreachable!("ICE: Pedersen hash expects one array argument, a register for the domain separator, and one register result") + } + } BlackBoxFunc::SchnorrVerify => { if let ( [BrilligVariable::SingleAddr(public_key_x), BrilligVariable::SingleAddr(public_key_y), BrilligVariable::BrilligArray(signature), message], @@ -391,8 +424,6 @@ pub(crate) fn convert_black_box_call( unreachable!("ICE: AES128Encrypt expects three array arguments, one array result") } } - BlackBoxFunc::PedersenCommitment => todo!("Deprecated Blackbox"), - BlackBoxFunc::PedersenHash => todo!("Deprecated Blackbox"), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index a0bf89fff0d2..9785e073be98 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -158,7 +158,20 @@ pub(crate) mod tests { ) -> Result { Ok(true) } - + fn pedersen_commitment( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Ok((2_u128.into(), 3_u128.into())) + } + fn pedersen_hash( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result { + Ok(6_u128.into()) + } fn multi_scalar_mul( &self, _points: &[FieldElement], diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index a595584b376e..b258905d657e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -347,6 +347,24 @@ impl DebugShow { result ); } + BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { + debug_println!( + self.enable_debug_trace, + " PEDERSEN {} {} -> {}", + inputs, + domain_separator, + output + ); + } + BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { + debug_println!( + self.enable_debug_trace, + " PEDERSEN_HASH {} {} -> {}", + inputs, + domain_separator, + output + ); + } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, @@ -444,8 +462,6 @@ impl DebugShow { output ); } - BlackBoxOp::PedersenCommitment { .. } => todo!("Deprecated Blackbox"), - BlackBoxOp::PedersenHash { .. } => todo!("Deprecated Blackbox"), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 7e362599fb51..71fb51bf7502 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -48,47 +48,51 @@ pub mod ssa_gen; /// and Brillig functions for unconstrained execution. pub(crate) fn optimize_into_acir( program: Program, - print_passes: bool, - print_brillig_trace: bool, - force_brillig_output: bool, - print_timings: bool, + options: &SsaEvaluatorOptions, ) -> Result { let ssa_gen_span = span!(Level::TRACE, "ssa_generation"); let ssa_gen_span_guard = ssa_gen_span.enter(); - let ssa = SsaBuilder::new(program, print_passes, force_brillig_output, print_timings)? - .run_pass(Ssa::defunctionalize, "After Defunctionalization:") - .run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:") - .run_pass(Ssa::separate_runtime, "After Runtime Separation:") - .run_pass(Ssa::resolve_is_unconstrained, "After Resolving IsUnconstrained:") - .run_pass(Ssa::inline_functions, "After Inlining:") - // Run mem2reg with the CFG separated into blocks - .run_pass(Ssa::mem2reg, "After Mem2Reg:") - .run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization") - .try_run_pass(Ssa::evaluate_assert_constant, "After Assert Constant:")? - .try_run_pass(Ssa::unroll_loops_iteratively, "After Unrolling:")? - .run_pass(Ssa::simplify_cfg, "After Simplifying:") - .run_pass(Ssa::flatten_cfg, "After Flattening:") - .run_pass(Ssa::remove_bit_shifts, "After Removing Bit Shifts:") - // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores - .run_pass(Ssa::mem2reg, "After Mem2Reg:") - // Run the inlining pass again to handle functions with `InlineType::NoPredicates`. - // Before flattening is run, we treat functions marked with the `InlineType::NoPredicates` as an entry point. - // This pass must come immediately following `mem2reg` as the succeeding passes - // may create an SSA which inlining fails to handle. - .run_pass(Ssa::inline_functions_with_no_predicates, "After Inlining:") - .run_pass(Ssa::remove_if_else, "After Remove IfElse:") - .run_pass(Ssa::fold_constants, "After Constant Folding:") - .run_pass(Ssa::remove_enable_side_effects, "After EnableSideEffects removal:") - .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") - .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") - .run_pass(Ssa::array_set_optimization, "After Array Set Optimizations:") - .finish(); - - let brillig = time("SSA to Brillig", print_timings, || ssa.to_brillig(print_brillig_trace)); + let ssa = SsaBuilder::new( + program, + options.enable_ssa_logging, + options.force_brillig_output, + options.print_codegen_timings, + )? + .run_pass(Ssa::defunctionalize, "After Defunctionalization:") + .run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:") + .run_pass(Ssa::separate_runtime, "After Runtime Separation:") + .run_pass(Ssa::resolve_is_unconstrained, "After Resolving IsUnconstrained:") + .run_pass(Ssa::inline_functions, "After Inlining:") + // Run mem2reg with the CFG separated into blocks + .run_pass(Ssa::mem2reg, "After Mem2Reg:") + .run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization") + .try_run_pass(Ssa::evaluate_assert_constant, "After Assert Constant:")? + .try_run_pass(Ssa::unroll_loops_iteratively, "After Unrolling:")? + .run_pass(Ssa::simplify_cfg, "After Simplifying:") + .run_pass(Ssa::flatten_cfg, "After Flattening:") + .run_pass(Ssa::remove_bit_shifts, "After Removing Bit Shifts:") + // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores + .run_pass(Ssa::mem2reg, "After Mem2Reg:") + // Run the inlining pass again to handle functions with `InlineType::NoPredicates`. + // Before flattening is run, we treat functions marked with the `InlineType::NoPredicates` as an entry point. + // This pass must come immediately following `mem2reg` as the succeeding passes + // may create an SSA which inlining fails to handle. + .run_pass(Ssa::inline_functions_with_no_predicates, "After Inlining:") + .run_pass(Ssa::remove_if_else, "After Remove IfElse:") + .run_pass(Ssa::fold_constants, "After Constant Folding:") + .run_pass(Ssa::remove_enable_side_effects, "After EnableSideEffects removal:") + .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") + .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") + .run_pass(Ssa::array_set_optimization, "After Array Set Optimizations:") + .finish(); + + let brillig = time("SSA to Brillig", options.print_codegen_timings, || { + ssa.to_brillig(options.enable_brillig_logging) + }); drop(ssa_gen_span_guard); - time("SSA to ACIR", print_timings, || ssa.into_acir(&brillig)) + time("SSA to ACIR", options.print_codegen_timings, || ssa.into_acir(&brillig)) } // Helper to time SSA passes @@ -144,17 +148,26 @@ impl SsaProgramArtifact { } } +pub struct SsaEvaluatorOptions { + /// Emit debug information for the intermediate SSA IR + pub enable_ssa_logging: bool, + + pub enable_brillig_logging: bool, + + /// Force Brillig output (for step debugging) + pub force_brillig_output: bool, + + /// Pretty print benchmark times of each code generation pass + pub print_codegen_timings: bool, +} + /// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program]. /// /// The output ACIR is backend-agnostic and so must go through a transformation pass before usage in proof generation. -#[allow(clippy::type_complexity)] #[tracing::instrument(level = "trace", skip_all)] pub fn create_program( program: Program, - enable_ssa_logging: bool, - enable_brillig_logging: bool, - force_brillig_output: bool, - print_codegen_timings: bool, + options: &SsaEvaluatorOptions, ) -> Result { let debug_variables = program.debug_variables.clone(); let debug_types = program.debug_types.clone(); @@ -163,13 +176,7 @@ pub fn create_program( let func_sigs = program.function_signatures.clone(); let recursive = program.recursive; - let (generated_acirs, generated_brillig, error_types) = optimize_into_acir( - program, - enable_ssa_logging, - enable_brillig_logging, - force_brillig_output, - print_codegen_timings, - )?; + let (generated_acirs, generated_brillig, error_types) = optimize_into_acir(program, options)?; assert_eq!( generated_acirs.len(), func_sigs.len(), diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 90d630dd5258..928a7b105eae 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1215,6 +1215,31 @@ impl AcirContext { ) -> Result, RuntimeError> { // Separate out any arguments that should be constants let (constant_inputs, constant_outputs) = match name { + BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => { + // The last argument of pedersen is the domain separator, which must be a constant + let domain_var = match inputs.pop() { + Some(domain_var) => domain_var.into_var()?, + None => { + return Err(RuntimeError::InternalError(InternalError::MissingArg { + name: "pedersen call".to_string(), + arg: "domain separator".to_string(), + call_stack: self.get_call_stack(), + })) + } + }; + + let domain_constant = match self.vars[&domain_var].as_constant() { + Some(domain_constant) => domain_constant, + None => { + return Err(RuntimeError::InternalError(InternalError::NotAConstant { + name: "domain separator".to_string(), + call_stack: self.get_call_stack(), + })) + } + }; + + (vec![*domain_constant], Vec::new()) + } BlackBoxFunc::Poseidon2Permutation => { // The last argument is the state length, which must be a constant let state_len = match inputs.pop() { @@ -1539,6 +1564,8 @@ impl AcirContext { return Ok(outputs_var); } + // Remove "always true" predicates. + let predicate = if predicate == Expression::one() { None } else { Some(predicate) }; let brillig_inputs: Vec> = try_vecmap(inputs, |i| -> Result<_, InternalError> { @@ -1589,7 +1616,7 @@ impl AcirContext { }); self.acir_ir.brillig_call( - Some(predicate), + predicate, generated_brillig, brillig_inputs, brillig_outputs, @@ -1660,8 +1687,7 @@ impl AcirContext { Ok(()) } - /// Recursively create acir values for returned arrays. This is necessary because a brillig returned array can have nested arrays as elements. - /// A singular array of witnesses is collected for a top level array, by deflattening the assigned witnesses at each level. + /// Recursively create zeroed-out acir values for returned arrays. This is necessary because a brillig returned array can have nested arrays as elements. fn zeroed_array_output(&mut self, element_types: &[AcirType], size: usize) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index b9e1c04afc5a..bcccfac89505 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -224,6 +224,16 @@ impl GeneratedAcir { output: outputs[0], } } + BlackBoxFunc::PedersenCommitment => BlackBoxFuncCall::PedersenCommitment { + inputs: inputs[0].clone(), + outputs: (outputs[0], outputs[1]), + domain_separator: constant_inputs[0].to_u128() as u32, + }, + BlackBoxFunc::PedersenHash => BlackBoxFuncCall::PedersenHash { + inputs: inputs[0].clone(), + output: outputs[0], + domain_separator: constant_inputs[0].to_u128() as u32, + }, BlackBoxFunc::EcdsaSecp256k1 => { BlackBoxFuncCall::EcdsaSecp256k1 { // 32 bytes for each public key co-ordinate @@ -361,8 +371,6 @@ impl GeneratedAcir { .expect("Compiler should generate correct size inputs"), outputs: outputs.try_into().expect("Compiler should generate correct size outputs"), }, - BlackBoxFunc::PedersenCommitment => todo!("Deprecated Blackbox"), - BlackBoxFunc::PedersenHash => todo!("Deprecated Blackbox"), }; self.push_opcode(AcirOpcode::BlackBoxFuncCall(black_box_func_call)); @@ -641,7 +649,9 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { | BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s - | BlackBoxFunc::Blake3 => None, + | BlackBoxFunc::Blake3 + | BlackBoxFunc::PedersenCommitment + | BlackBoxFunc::PedersenHash => None, BlackBoxFunc::Keccakf1600 => Some(25), // The permutation takes a fixed number of inputs, but the inputs length depends on the proving system implementation. @@ -677,8 +687,6 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { // FromLeBytes takes a variable array of bytes as input BlackBoxFunc::BigIntFromLeBytes => None, - BlackBoxFunc::PedersenCommitment => todo!(), - BlackBoxFunc::PedersenHash => todo!(), } } @@ -702,6 +710,12 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::Sha256Compression => Some(8), + // Pedersen commitment returns a point + BlackBoxFunc::PedersenCommitment => Some(2), + + // Pedersen hash returns a field + BlackBoxFunc::PedersenHash => Some(1), + // Can only apply a range constraint to one // witness at a time. BlackBoxFunc::RANGE => Some(0), @@ -730,8 +744,6 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { // AES encryption returns a variable number of outputs BlackBoxFunc::AES128Encrypt => None, - BlackBoxFunc::PedersenCommitment => todo!(), - BlackBoxFunc::PedersenHash => todo!(), } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index a9e3570ba0f5..e4635c0f974c 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -446,7 +446,9 @@ fn simplify_black_box_func( BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256), BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s), BlackBoxFunc::Blake3 => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake3), - BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO(Guillaume) + BlackBoxFunc::PedersenCommitment + | BlackBoxFunc::PedersenHash + | BlackBoxFunc::Keccakf1600 => SimplifyResult::None, //TODO(Guillaume) BlackBoxFunc::Keccak256 => { match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { (Some((input, _)), Some(num_bytes)) if array_is_constant(dfg, &input) => { @@ -501,8 +503,6 @@ fn simplify_black_box_func( } BlackBoxFunc::Sha256Compression => SimplifyResult::None, //TODO(Guillaume) BlackBoxFunc::AES128Encrypt => SimplifyResult::None, - BlackBoxFunc::PedersenCommitment => todo!("Deprecated Blackbox"), - BlackBoxFunc::PedersenHash => todo!("Deprecated Blackbox"), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs index 075df4ee4e3f..2657869a9d7c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -7,7 +7,7 @@ use crate::ast::{ }; use crate::macros_api::StructId; use crate::node_interner::ExprId; -use crate::token::{Attributes, Token}; +use crate::token::{Attributes, Token, Tokens}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; @@ -33,18 +33,10 @@ pub enum ExpressionKind { Tuple(Vec), Lambda(Box), Parenthesized(Box), - Quote(BlockExpression, Span), + Quote(Tokens), Unquote(Box), Comptime(BlockExpression, Span), - /// Unquote expressions are replaced with UnquoteMarkers when Quoted - /// expressions are resolved. Since the expression being quoted remains an - /// ExpressionKind (rather than a resolved ExprId), the UnquoteMarker must be - /// here in the AST even though it is technically HIR-only. - /// Each usize in an UnquoteMarker is an index which corresponds to the index of the - /// expression in the Hir::Quote expression list to replace it with. - UnquoteMarker(usize), - // This variant is only emitted when inlining the result of comptime // code. It is used to translate function values back into the AST while // guaranteeing they have the same instantiated type and definition id without resolving again. @@ -557,12 +549,14 @@ impl Display for ExpressionKind { } Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), - Quote(block, _) => write!(f, "quote {block}"), Comptime(block, _) => write!(f, "comptime {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), Unquote(expr) => write!(f, "$({expr})"), - UnquoteMarker(index) => write!(f, "${index}"), + Quote(tokens) => { + let tokens = vecmap(&tokens.0, ToString::to_string); + write!(f, "quote {{ {} }}", tokens.join(" ")) + } } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index d6ad752b67a3..9d864a0de91a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -18,7 +18,7 @@ use crate::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, HirMethodReference, - HirPrefixExpression, HirQuoted, + HirPrefixExpression, }, traits::TraitConstraint, }, @@ -28,6 +28,7 @@ use crate::{ MethodCallExpression, PrefixExpression, }, node_interner::{DefinitionKind, ExprId, FuncId}, + token::Tokens, QuotedType, Shared, StructType, Type, }; @@ -58,7 +59,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), - ExpressionKind::Quote(quote, _) => self.elaborate_quote(quote), + ExpressionKind::Quote(quote) => self.elaborate_quote(quote), ExpressionKind::Comptime(comptime, _) => { return self.elaborate_comptime_block(comptime, expr.span) } @@ -68,9 +69,6 @@ impl<'context> Elaborator<'context> { self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); (HirExpression::Error, Type::Error) } - ExpressionKind::UnquoteMarker(index) => { - unreachable!("UnquoteMarker({index}) remaining in runtime code") - } }; let id = self.interner.push_expr(hir_expr); self.interner.push_expr_location(id, expr.span, self.file); @@ -646,11 +644,9 @@ impl<'context> Elaborator<'context> { (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type))) } - fn elaborate_quote(&mut self, mut block: BlockExpression) -> (HirExpression, Type) { - let mut unquoted_exprs = Vec::new(); - self.find_unquoted_exprs_in_block(&mut block, &mut unquoted_exprs); - let quoted = HirQuoted { quoted_block: block, unquoted_exprs }; - (HirExpression::Quote(quoted), Type::Quoted(QuotedType::Expr)) + fn elaborate_quote(&mut self, mut tokens: Tokens) -> (HirExpression, Type) { + tokens = self.find_unquoted_exprs_tokens(tokens); + (HirExpression::Quote(tokens), Type::Quoted(QuotedType::Quoted)) } fn elaborate_comptime_block(&mut self, block: BlockExpression, span: Span) -> (ExprId, Type) { @@ -716,7 +712,7 @@ impl<'context> Elaborator<'context> { location: Location, return_type: Type, ) -> Option<(HirExpression, Type)> { - self.unify(&return_type, &Type::Quoted(QuotedType::Expr), || { + self.unify(&return_type, &Type::Quoted(QuotedType::Quoted), || { TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), span: location.span } }); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index 31c40815c36a..dc99ceae2f04 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1190,7 +1190,7 @@ impl<'context> Elaborator<'context> { self.current_item = Some(DependencyId::Global(global_id)); let let_stmt = global.stmt_def; - if !self.module_id().module(self.def_maps).is_contract + if !self.in_contract && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) { let span = let_stmt.pattern.span(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index 72ef2442477a..fcb7ac94c264 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -24,8 +24,8 @@ use crate::{ traits::TraitConstraint, }, macros_api::{ - HirExpression, HirLiteral, HirStatement, Path, PathKind, Signedness, UnaryOp, - UnresolvedType, UnresolvedTypeData, + HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, + UnaryOp, UnresolvedType, UnresolvedTypeData, }, node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, Generics, Type, TypeBinding, TypeVariable, TypeVariableKind, @@ -192,17 +192,17 @@ impl<'context> Elaborator<'context> { } let expected_generic_count = struct_type.borrow().generics.len(); - // if !self.in_contract - // && self - // .interner - // .struct_attributes(&struct_type.borrow().id) - // .iter() - // .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) - // { - // self.push_err(ResolverError::AbiAttributeOutsideContract { - // span: struct_type.borrow().name.span(), - // }); - // } + if !self.in_contract + && self + .interner + .struct_attributes(&struct_type.borrow().id) + .iter() + .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) + { + self.push_err(ResolverError::AbiAttributeOutsideContract { + span: struct_type.borrow().name.span(), + }); + } self.verify_generics_count(expected_generic_count, &mut args, span, || { struct_type.borrow().to_string() }); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs index 6767f471f16f..ed12ba21398a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -1,306 +1,41 @@ use crate::{ - ast::{ - ArrayLiteral, AssignStatement, ConstrainStatement, ConstructorExpression, IfExpression, - InfixExpression, Lambda, - }, - macros_api::{ - BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, - ForLoopStatement, ForRange, IndexExpression, LetStatement, Literal, MemberAccessExpression, - MethodCallExpression, PrefixExpression, Statement, StatementKind, - }, - node_interner::ExprId, + macros_api::Path, + token::{SpannedToken, Token, Tokens}, }; use super::Elaborator; impl<'a> Elaborator<'a> { - pub fn find_unquoted_exprs_in_block( - &mut self, - block: &mut BlockExpression, - unquoted_exprs: &mut Vec, - ) { - for statement in &mut block.statements { - self.find_unquoted_exprs_in_statement(statement, unquoted_exprs); - } - } - - fn find_unquoted_exprs_in_statement( - &mut self, - statement: &mut Statement, - unquoted_exprs: &mut Vec, - ) { - match &mut statement.kind { - StatementKind::Let(let_) => self.find_unquoted_exprs_in_let(let_, unquoted_exprs), - StatementKind::Constrain(constrain) => { - self.find_unquoted_exprs_in_constrain(constrain, unquoted_exprs); - } - StatementKind::Expression(expr) => { - self.find_unquoted_exprs_in_expr(expr, unquoted_exprs); - } - StatementKind::Assign(assign) => { - self.find_unquoted_exprs_in_assign(assign, unquoted_exprs); - } - StatementKind::For(for_) => self.find_unquoted_exprs_in_for(for_, unquoted_exprs), - StatementKind::Break => (), - StatementKind::Continue => (), - StatementKind::Comptime(comptime) => { - self.find_unquoted_exprs_in_statement(comptime, unquoted_exprs); - } - StatementKind::Semi(expr) => self.find_unquoted_exprs_in_expr(expr, unquoted_exprs), - StatementKind::Error => (), - } - } - - fn find_unquoted_exprs_in_constrain( - &mut self, - constrain: &mut ConstrainStatement, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut constrain.0, unquoted_exprs); - if let Some(msg) = constrain.1.as_mut() { - self.find_unquoted_exprs_in_expr(msg, unquoted_exprs); - } - } - - fn find_unquoted_exprs_in_let( - &mut self, - let_: &mut LetStatement, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut let_.expression, unquoted_exprs); - } - - fn find_unquoted_exprs_in_assign( - &mut self, - assign: &mut AssignStatement, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut assign.expression, unquoted_exprs); - } - - fn find_unquoted_exprs_in_for( - &mut self, - for_: &mut ForLoopStatement, - unquoted_exprs: &mut Vec, - ) { - match &mut for_.range { - ForRange::Range(start, end) => { - self.find_unquoted_exprs_in_expr(start, unquoted_exprs); - self.find_unquoted_exprs_in_expr(end, unquoted_exprs); - } - ForRange::Array(array) => { - self.find_unquoted_exprs_in_expr(array, unquoted_exprs); - } - }; - self.find_unquoted_exprs_in_expr(&mut for_.block, unquoted_exprs); - } - - fn find_unquoted_exprs_in_expr( - &mut self, - expr: &mut Expression, - unquoted_exprs: &mut Vec, - ) { - match &mut expr.kind { - ExpressionKind::Literal(literal) => { - self.find_unquoted_exprs_in_literal(literal, unquoted_exprs); - } - ExpressionKind::Block(block) => { - self.find_unquoted_exprs_in_block(block, unquoted_exprs); - } - ExpressionKind::Prefix(prefix) => { - self.find_unquoted_exprs_in_prefix(prefix, unquoted_exprs); - } - ExpressionKind::Index(index) => { - self.find_unquoted_exprs_in_index(index, unquoted_exprs); - } - ExpressionKind::Call(call) => self.find_unquoted_exprs_in_call(call, unquoted_exprs), - ExpressionKind::MethodCall(call) => { - self.find_unquoted_exprs_in_method_call(call, unquoted_exprs); - } - ExpressionKind::Constructor(constructor) => { - self.find_unquoted_exprs_in_constructor(constructor, unquoted_exprs); - } - ExpressionKind::MemberAccess(access) => { - self.find_unquoted_exprs_in_access(access, unquoted_exprs); - } - ExpressionKind::Cast(cast) => self.find_unquoted_exprs_in_cast(cast, unquoted_exprs), - ExpressionKind::Infix(infix) => { - self.find_unquoted_exprs_in_infix(infix, unquoted_exprs); - } - ExpressionKind::If(if_) => self.find_unquoted_exprs_in_if(if_, unquoted_exprs), - ExpressionKind::Variable(_, _) => (), - ExpressionKind::Tuple(tuple) => { - self.find_unquoted_exprs_in_tuple(tuple, unquoted_exprs); - } - ExpressionKind::Lambda(lambda) => { - self.find_unquoted_exprs_in_lambda(lambda, unquoted_exprs); - } - ExpressionKind::Parenthesized(expr) => { - self.find_unquoted_exprs_in_expr(expr, unquoted_exprs); - } - ExpressionKind::Quote(quote, _) => { - self.find_unquoted_exprs_in_block(quote, unquoted_exprs); - } - ExpressionKind::Comptime(block, _) => { - self.find_unquoted_exprs_in_block(block, unquoted_exprs); - } - ExpressionKind::Resolved(_) => (), - ExpressionKind::Error => (), - ExpressionKind::UnquoteMarker(_) => (), - ExpressionKind::Unquote(unquoted) => { - // Avoid an expensive clone for unquoted - let empty_expr = Expression::new(ExpressionKind::Error, unquoted.span); - let unquote = std::mem::replace(unquoted.as_mut(), empty_expr); - self.replace_unquote(expr, unquote, unquoted_exprs); - } - } - } - - fn find_unquoted_exprs_in_literal( - &mut self, - literal: &mut Literal, - unquoted_exprs: &mut Vec, - ) { - match literal { - Literal::Array(array) | Literal::Slice(array) => match array { - ArrayLiteral::Standard(elements) => { - for element in elements { - self.find_unquoted_exprs_in_expr(element, unquoted_exprs); + /// Go through the given tokens looking for a '$' token followed by a variable to unquote. + /// Each time these two tokens are found, they are replaced by a new UnquoteMarker token + /// containing the ExprId of the resolved variable to unquote. + pub fn find_unquoted_exprs_tokens(&mut self, tokens: Tokens) -> Tokens { + let token_count = tokens.0.len(); + let mut new_tokens = Vec::with_capacity(token_count); + let mut tokens = tokens.0.into_iter(); + + while let Some(token) = tokens.next() { + let is_unquote = matches!(token.token(), Token::DollarSign); + new_tokens.push(token); + + if is_unquote { + if let Some(next) = tokens.next() { + let span = next.to_span(); + + match next.into_token() { + Token::Ident(name) => { + // Don't want the leading `$` anymore + new_tokens.pop(); + let path = Path::from_single(name, span); + let (expr_id, _) = self.elaborate_variable(path, None); + new_tokens.push(SpannedToken::new(Token::UnquoteMarker(expr_id), span)); + } + other_next => new_tokens.push(SpannedToken::new(other_next, span)), } } - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_unquoted_exprs_in_expr(repeated_element, unquoted_exprs); - self.find_unquoted_exprs_in_expr(length, unquoted_exprs); - } - }, - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), - } - } - - fn find_unquoted_exprs_in_prefix( - &mut self, - prefix: &mut PrefixExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut prefix.rhs, unquoted_exprs); - } - - fn find_unquoted_exprs_in_index( - &mut self, - index: &mut IndexExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut index.collection, unquoted_exprs); - self.find_unquoted_exprs_in_expr(&mut index.index, unquoted_exprs); - } - - fn find_unquoted_exprs_in_call( - &mut self, - call: &mut CallExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut call.func, unquoted_exprs); - - for arg in &mut call.arguments { - self.find_unquoted_exprs_in_expr(arg, unquoted_exprs); - } - } - - fn find_unquoted_exprs_in_method_call( - &mut self, - call: &mut MethodCallExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut call.object, unquoted_exprs); - - for arg in &mut call.arguments { - self.find_unquoted_exprs_in_expr(arg, unquoted_exprs); - } - } - - fn find_unquoted_exprs_in_constructor( - &mut self, - constructor: &mut ConstructorExpression, - unquoted_exprs: &mut Vec, - ) { - for (_, field) in &mut constructor.fields { - self.find_unquoted_exprs_in_expr(field, unquoted_exprs); - } - } - - fn find_unquoted_exprs_in_access( - &mut self, - member_access: &mut MemberAccessExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut member_access.lhs, unquoted_exprs); - } - - fn find_unquoted_exprs_in_cast( - &mut self, - cast: &mut CastExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut cast.lhs, unquoted_exprs); - } - - fn find_unquoted_exprs_in_infix( - &mut self, - infix: &mut InfixExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut infix.lhs, unquoted_exprs); - self.find_unquoted_exprs_in_expr(&mut infix.rhs, unquoted_exprs); - } - - fn find_unquoted_exprs_in_if( - &mut self, - if_: &mut IfExpression, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut if_.condition, unquoted_exprs); - self.find_unquoted_exprs_in_expr(&mut if_.consequence, unquoted_exprs); - - if let Some(alternate) = if_.alternative.as_mut() { - self.find_unquoted_exprs_in_expr(alternate, unquoted_exprs); - } - } - - fn find_unquoted_exprs_in_tuple( - &mut self, - tuple: &mut [Expression], - unquoted_exprs: &mut Vec, - ) { - for field in tuple { - self.find_unquoted_exprs_in_expr(field, unquoted_exprs); + } } - } - - fn find_unquoted_exprs_in_lambda( - &mut self, - lambda: &mut Lambda, - unquoted_exprs: &mut Vec, - ) { - self.find_unquoted_exprs_in_expr(&mut lambda.body, unquoted_exprs); - } - /// Elaborate and store the unquoted expression in the given vector, then - /// replace it with an unquote expression with an UnquoteMarker expression to mark the position - /// to replace it with later. - fn replace_unquote( - &mut self, - expr: &mut Expression, - unquoted: Expression, - unquoted_exprs: &mut Vec, - ) { - let (expr_id, _) = self.elaborate_expression(unquoted); - let unquote_marker_id = unquoted_exprs.len(); - unquoted_exprs.push(expr_id); - expr.kind = ExpressionKind::UnquoteMarker(unquote_marker_id); + Tokens(new_tokens) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs index 05962420f8a6..4eab12af3085 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -1,5 +1,11 @@ -use crate::{hir::def_collector::dc_crate::CompilationError, Type}; +use std::rc::Rc; + +use crate::{ + hir::def_collector::dc_crate::CompilationError, parser::ParserError, token::Tokens, Type, +}; use acvm::{acir::AcirField, FieldElement}; +use fm::FileId; +use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic, Location}; use super::value::Value; @@ -35,6 +41,7 @@ pub enum InterpreterError { NonStructInConstructor { typ: Type, location: Location }, CannotInlineMacro { value: Value, location: Location }, UnquoteFoundDuringEvaluation { location: Location }, + FailedToParseMacro { error: ParserError, tokens: Rc, rule: &'static str, file: FileId }, Unimplemented { item: String, location: Location }, @@ -97,6 +104,9 @@ impl InterpreterError { | InterpreterError::Unimplemented { location, .. } | InterpreterError::BreakNotInLoop { location, .. } | InterpreterError::ContinueNotInLoop { location, .. } => *location, + InterpreterError::FailedToParseMacro { error, file, .. } => { + Location::new(error.span(), *file) + } InterpreterError::Break | InterpreterError::Continue => { panic!("Tried to get the location of Break/Continue error!") } @@ -258,6 +268,31 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let secondary = "This is a bug".into(); CustomDiagnostic::simple_error(msg, secondary, location.span) } + InterpreterError::FailedToParseMacro { error, tokens, rule, file: _ } => { + let message = format!("Failed to parse macro's token stream into {rule}"); + let tokens = vecmap(&tokens.0, ToString::to_string).join(" "); + + // 10 is an aribtrary number of tokens here chosen to fit roughly onto one line + let token_stream = if tokens.len() > 10 { + format!("The resulting token stream was: {tokens}") + } else { + format!( + "The resulting token stream was: (stream starts on next line)\n {tokens}" + ) + }; + + let push_the_problem_on_the_library_author = "To avoid this error in the future, try adding input validation to your macro. Erroring out early with an `assert` can be a good way to provide a user-friendly error message".into(); + + let mut diagnostic = CustomDiagnostic::from(error); + // Swap the parser's primary note to become the secondary note so that it is + // more clear this error originates from failing to parse a macro. + let secondary = std::mem::take(&mut diagnostic.message); + diagnostic.add_secondary(secondary, error.span()); + diagnostic.message = message; + diagnostic.add_note(token_stream); + diagnostic.add_note(push_the_problem_on_the_library_author); + diagnostic + } InterpreterError::Unimplemented { item, location } => { let msg = format!("{item} is currently unimplemented"); CustomDiagnostic::simple_error(msg, String::new(), location.span) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 21b6897f5ddc..5e236a2b9808 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -7,7 +7,7 @@ use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness}; -use crate::hir_def::expr::HirQuoted; +use crate::token::Tokens; use crate::{ hir_def::{ expr::{ @@ -26,8 +26,6 @@ use crate::{ Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, }; -use self::unquote::UnquoteArgs; - use super::errors::{IResult, InterpreterError}; use super::value::Value; @@ -100,14 +98,7 @@ impl<'a> Interpreter<'a> { .expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); if let Some(builtin) = func_attrs.builtin() { - match builtin.as_str() { - "array_len" => builtin::array_len(&arguments), - "as_slice" => builtin::as_slice(arguments), - _ => { - let item = format!("Comptime evaluation for builtin function {builtin}"); - Err(InterpreterError::Unimplemented { item, location }) - } - } + builtin::call_builtin(self.interner, builtin, arguments, location) } else if let Some(foreign) = func_attrs.foreign() { let item = format!("Comptime evaluation for foreign functions like {foreign}"); Err(InterpreterError::Unimplemented { item, location }) @@ -343,9 +334,9 @@ impl<'a> Interpreter<'a> { HirExpression::If(if_) => self.evaluate_if(if_, id), HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), - HirExpression::Quote(block) => self.evaluate_quote(block, id), + HirExpression::Quote(tokens) => self.evaluate_quote(tokens, id), HirExpression::Comptime(block) => self.evaluate_block(block), - HirExpression::Unquote(block) => { + HirExpression::Unquote(tokens) => { // An Unquote expression being found is indicative of a macro being // expanded within another comptime fn which we don't currently support. let location = self.interner.expr_location(&id); @@ -936,6 +927,18 @@ impl<'a> Interpreter<'a> { fn evaluate_access(&mut self, access: HirMemberAccess, id: ExprId) -> IResult { let (fields, struct_type) = match self.evaluate(access.lhs)? { Value::Struct(fields, typ) => (fields, typ), + Value::Tuple(fields) => { + let (fields, field_types): (HashMap, Value>, Vec) = fields + .into_iter() + .enumerate() + .map(|(i, field)| { + let field_type = field.get_type().into_owned(); + let key_val_pair = (Rc::new(i.to_string()), field); + (key_val_pair, field_type) + }) + .unzip(); + (fields, Type::Tuple(field_types)) + } value => { let location = self.interner.expr_location(&id); return Err(InterpreterError::NonTupleOrStructInMemberAccess { value, location }); @@ -1136,13 +1139,10 @@ impl<'a> Interpreter<'a> { Ok(Value::Closure(lambda, environment, typ)) } - fn evaluate_quote(&mut self, mut quoted: HirQuoted, expr_id: ExprId) -> IResult { - let file = self.interner.expr_location(&expr_id).file; - let values = try_vecmap(quoted.unquoted_exprs, |value| self.evaluate(value))?; - let args = UnquoteArgs { values, file }; - - self.substitute_unquoted_values_into_block(&mut quoted.quoted_block, &args); - Ok(Value::Code(Rc::new(quoted.quoted_block))) + fn evaluate_quote(&mut self, mut tokens: Tokens, expr_id: ExprId) -> IResult { + let location = self.interner.expr_location(&expr_id); + tokens = self.substitute_unquoted_values_into_tokens(tokens, location)?; + Ok(Value::Code(Rc::new(tokens))) } pub fn evaluate_statement(&mut self, statement: StmtId) -> IResult { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 9216ba271c1f..cccc9c6d545d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -1,11 +1,36 @@ +use std::rc::Rc; + use noirc_errors::Location; use crate::{ - hir::comptime::{errors::IResult, Value}, - Type, + hir::comptime::{errors::IResult, InterpreterError, Value}, + lexer::Lexer, + macros_api::NodeInterner, + token::{SpannedToken, Token, Tokens}, + QuotedType, Type, }; -pub(super) fn array_len(arguments: &[(Value, Location)]) -> IResult { +pub(super) fn call_builtin( + interner: &NodeInterner, + name: &str, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + match name { + "array_len" => array_len(&arguments), + "as_slice" => as_slice(arguments), + "slice_push_back" => slice_push_back(arguments), + "type_def_as_type" => type_def_as_type(interner, arguments), + "type_def_generics" => type_def_generics(interner, arguments), + "type_def_fields" => type_def_fields(interner, arguments), + _ => { + let item = format!("Comptime evaluation for builtin function {name}"); + Err(InterpreterError::Unimplemented { item, location }) + } + } +} + +fn array_len(arguments: &[(Value, Location)]) -> IResult { assert_eq!(arguments.len(), 1, "ICE: `array_len` should only receive a single argument"); match &arguments[0].0 { Value::Array(values, _) | Value::Slice(values, _) => Ok(Value::U32(values.len() as u32)), @@ -14,7 +39,7 @@ pub(super) fn array_len(arguments: &[(Value, Location)]) -> IResult { } } -pub(super) fn as_slice(mut arguments: Vec<(Value, Location)>) -> IResult { +fn as_slice(mut arguments: Vec<(Value, Location)>) -> IResult { assert_eq!(arguments.len(), 1, "ICE: `as_slice` should only receive a single argument"); let (array, _) = arguments.pop().unwrap(); match array { @@ -23,3 +48,129 @@ pub(super) fn as_slice(mut arguments: Vec<(Value, Location)>) -> IResult _ => unreachable!("ICE: Cannot convert types other than arrays into slices"), } } + +fn slice_push_back(mut arguments: Vec<(Value, Location)>) -> IResult { + assert_eq!(arguments.len(), 2, "ICE: `slice_push_back` should only receive two arguments"); + let (element, _) = arguments.pop().unwrap(); + let (slice, _) = arguments.pop().unwrap(); + match slice { + Value::Slice(mut values, typ) => { + values.push_back(element); + Ok(Value::Slice(values, typ)) + } + // Type checking should prevent this branch being taken. + _ => unreachable!("ICE: `slice_push_back` expects a slice as its first argument"), + } +} + +/// fn as_type(self) -> Quoted +fn type_def_as_type( + interner: &NodeInterner, + mut arguments: Vec<(Value, Location)>, +) -> IResult { + assert_eq!(arguments.len(), 1, "ICE: `generics` should only receive a single argument"); + let (type_def, span) = match arguments.pop() { + Some((Value::TypeDefinition(id), location)) => (id, location.span), + other => { + unreachable!("ICE: `as_type` expected a `TypeDefinition` argument, found {other:?}") + } + }; + + let struct_def = interner.get_struct(type_def); + let struct_def = struct_def.borrow(); + let make_token = |name| SpannedToken::new(Token::Str(name), span); + + let mut tokens = vec![make_token(struct_def.name.to_string())]; + + for (i, generic) in struct_def.generics.iter().enumerate() { + if i != 0 { + tokens.push(SpannedToken::new(Token::Comma, span)); + } + tokens.push(make_token(generic.borrow().to_string())); + } + + Ok(Value::Code(Rc::new(Tokens(tokens)))) +} + +/// fn generics(self) -> [Quoted] +fn type_def_generics( + interner: &NodeInterner, + mut arguments: Vec<(Value, Location)>, +) -> IResult { + assert_eq!(arguments.len(), 1, "ICE: `generics` should only receive a single argument"); + let (type_def, span) = match arguments.pop() { + Some((Value::TypeDefinition(id), location)) => (id, location.span), + other => { + unreachable!("ICE: `as_type` expected a `TypeDefinition` argument, found {other:?}") + } + }; + + let struct_def = interner.get_struct(type_def); + + let generics = struct_def + .borrow() + .generics + .iter() + .map(|generic| { + let name = SpannedToken::new(Token::Str(generic.borrow().to_string()), span); + Value::Code(Rc::new(Tokens(vec![name]))) + }) + .collect(); + + let typ = Type::Slice(Box::new(Type::Quoted(QuotedType::Quoted))); + Ok(Value::Slice(generics, typ)) +} + +/// fn fields(self) -> [(Quoted, Quoted)] +/// Returns (name, type) pairs of each field of this TypeDefinition +fn type_def_fields( + interner: &NodeInterner, + mut arguments: Vec<(Value, Location)>, +) -> IResult { + assert_eq!(arguments.len(), 1, "ICE: `generics` should only receive a single argument"); + let (type_def, span) = match arguments.pop() { + Some((Value::TypeDefinition(id), location)) => (id, location.span), + other => { + unreachable!("ICE: `as_type` expected a `TypeDefinition` argument, found {other:?}") + } + }; + + let struct_def = interner.get_struct(type_def); + let struct_def = struct_def.borrow(); + + let make_token = |name| SpannedToken::new(Token::Str(name), span); + let make_quoted = |tokens| Value::Code(Rc::new(Tokens(tokens))); + + let mut fields = im::Vector::new(); + + for (name, typ) in struct_def.get_fields_as_written() { + let name = make_quoted(vec![make_token(name)]); + let typ = Value::Code(Rc::new(type_to_tokens(&typ)?)); + fields.push_back(Value::Tuple(vec![name, typ])); + } + + let typ = Type::Slice(Box::new(Type::Tuple(vec![ + Type::Quoted(QuotedType::Quoted), + Type::Quoted(QuotedType::Quoted), + ]))); + Ok(Value::Slice(fields, typ)) +} + +/// FIXME(https://github.com/noir-lang/noir/issues/5309): This code is temporary. +/// It will produce poor results for type variables and will result in incorrect +/// spans on the returned tokens. +fn type_to_tokens(typ: &Type) -> IResult { + let (mut tokens, mut errors) = Lexer::lex(&typ.to_string()); + + if let Some(last) = tokens.0.last() { + if matches!(last.token(), Token::EOF) { + tokens.0.pop(); + } + } + + if !errors.is_empty() { + let error = errors.swap_remove(0); + todo!("Got lexer error: {error}") + } + Ok(tokens) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs index 58ca345ca90b..a1ceb27afb28 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs @@ -1,287 +1,42 @@ -use fm::FileId; use noirc_errors::Location; use crate::{ - ast::{ - ArrayLiteral, AssignStatement, ConstrainStatement, ConstructorExpression, IfExpression, - InfixExpression, Lambda, - }, - hir::comptime::{errors::IResult, Value}, - macros_api::{ - BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, - ForLoopStatement, ForRange, IndexExpression, LetStatement, Literal, MemberAccessExpression, - MethodCallExpression, PrefixExpression, Statement, StatementKind, - }, + hir::comptime::{errors::IResult, value::unwrap_rc, Value}, + token::{SpannedToken, Token, Tokens}, }; use super::Interpreter; -pub(super) struct UnquoteArgs { - pub(super) values: Vec, - pub(super) file: FileId, -} - impl<'a> Interpreter<'a> { - pub(super) fn substitute_unquoted_values_into_block( - &mut self, - block: &mut BlockExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - for statement in &mut block.statements { - self.substitute_unquoted_into_statement(statement, args)?; - } - Ok(()) - } - - fn substitute_unquoted_into_statement( - &mut self, - statement: &mut Statement, - args: &UnquoteArgs, - ) -> IResult<()> { - match &mut statement.kind { - StatementKind::Let(let_) => self.substitute_unquoted_into_let(let_, args), - StatementKind::Constrain(constrain) => { - self.substitute_unquoted_into_constrain(constrain, args) - } - StatementKind::Expression(expr) => self.substitute_unquoted_into_expr(expr, args), - StatementKind::Assign(assign) => self.substitute_unquoted_into_assign(assign, args), - StatementKind::For(for_) => self.substitute_unquoted_into_for(for_, args), - StatementKind::Break => Ok(()), - StatementKind::Continue => Ok(()), - StatementKind::Comptime(comptime) => { - self.substitute_unquoted_into_statement(comptime, args) - } - StatementKind::Semi(expr) => self.substitute_unquoted_into_expr(expr, args), - StatementKind::Error => Ok(()), - } - } - - fn substitute_unquoted_into_constrain( - &mut self, - constrain: &mut ConstrainStatement, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut constrain.0, args)?; - if let Some(msg) = constrain.1.as_mut() { - self.substitute_unquoted_into_expr(msg, args)?; - } - Ok(()) - } - - fn substitute_unquoted_into_let( - &mut self, - let_: &mut LetStatement, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut let_.expression, args) - } - - fn substitute_unquoted_into_assign( - &mut self, - assign: &mut AssignStatement, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut assign.expression, args) - } - - fn substitute_unquoted_into_for( - &mut self, - for_: &mut ForLoopStatement, - args: &UnquoteArgs, - ) -> IResult<()> { - match &mut for_.range { - ForRange::Range(start, end) => { - self.substitute_unquoted_into_expr(start, args)?; - self.substitute_unquoted_into_expr(end, args)?; - } - ForRange::Array(array) => { - self.substitute_unquoted_into_expr(array, args)?; - } - }; - self.substitute_unquoted_into_expr(&mut for_.block, args) - } - - fn substitute_unquoted_into_expr( - &mut self, - expr: &mut Expression, - args: &UnquoteArgs, - ) -> IResult<()> { - match &mut expr.kind { - ExpressionKind::Literal(literal) => { - self.substitute_unquoted_into_literal(literal, args) - } - ExpressionKind::Block(block) => self.substitute_unquoted_values_into_block(block, args), - ExpressionKind::Prefix(prefix) => self.substitute_unquoted_into_prefix(prefix, args), - ExpressionKind::Index(index) => self.substitute_unquoted_into_index(index, args), - ExpressionKind::Call(call) => self.substitute_unquoted_into_call(call, args), - ExpressionKind::MethodCall(call) => { - self.substitute_unquoted_into_method_call(call, args) - } - ExpressionKind::Constructor(constructor) => { - self.substitute_unquoted_into_constructor(constructor, args) - } - ExpressionKind::MemberAccess(access) => { - self.substitute_unquoted_into_access(access, args) - } - ExpressionKind::Cast(cast) => self.substitute_unquoted_into_cast(cast, args), - ExpressionKind::Infix(infix) => self.substitute_unquoted_into_infix(infix, args), - ExpressionKind::If(if_) => self.substitute_unquoted_into_if(if_, args), - ExpressionKind::Variable(_, _) => Ok(()), - ExpressionKind::Tuple(tuple) => self.substitute_unquoted_into_tuple(tuple, args), - ExpressionKind::Lambda(lambda) => self.substitute_unquoted_into_lambda(lambda, args), - ExpressionKind::Parenthesized(expr) => self.substitute_unquoted_into_expr(expr, args), - ExpressionKind::Quote(quote, _) => { - self.substitute_unquoted_values_into_block(quote, args) - } - ExpressionKind::Unquote(unquote) => self.substitute_unquoted_into_expr(unquote, args), - ExpressionKind::Comptime(comptime, _) => { - self.substitute_unquoted_values_into_block(comptime, args) - } - ExpressionKind::Resolved(_) => Ok(()), - ExpressionKind::Error => Ok(()), - ExpressionKind::UnquoteMarker(index) => { - let location = Location::new(expr.span, args.file); - *expr = args.values[*index].clone().into_expression(self.interner, location)?; - Ok(()) - } - } - } - - fn substitute_unquoted_into_literal( - &mut self, - literal: &mut Literal, - args: &UnquoteArgs, - ) -> IResult<()> { - match literal { - Literal::Array(array) | Literal::Slice(array) => match array { - ArrayLiteral::Standard(elements) => { - for element in elements { - self.substitute_unquoted_into_expr(element, args)?; + /// Evaluates any expressions within UnquoteMarkers in the given token list + /// and replaces the expression held by the marker with the evaluated value + /// in expression form. + pub(super) fn substitute_unquoted_values_into_tokens( + &mut self, + tokens: Tokens, + location: Location, + ) -> IResult { + let mut new_tokens = Vec::with_capacity(tokens.0.len()); + + for token in tokens.0 { + let span = token.to_span(); + match token.token() { + Token::UnquoteMarker(id) => { + match self.evaluate(*id)? { + // If the value is already quoted we don't want to change the token stream by + // turning it into a Quoted block (which would add `quote`, `{`, and `}` tokens). + Value::Code(stream) => new_tokens.extend(unwrap_rc(stream).0), + value => { + let new_id = value.into_hir_expression(self.interner, location)?; + let new_token = Token::UnquoteMarker(new_id); + new_tokens.push(SpannedToken::new(new_token, span)); + } } - Ok(()) - } - ArrayLiteral::Repeated { repeated_element, length } => { - self.substitute_unquoted_into_expr(repeated_element, args)?; - self.substitute_unquoted_into_expr(length, args)?; - Ok(()) } - }, - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => Ok(()), - } - } - - fn substitute_unquoted_into_prefix( - &mut self, - prefix: &mut PrefixExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut prefix.rhs, args) - } - - fn substitute_unquoted_into_index( - &mut self, - index: &mut IndexExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut index.collection, args)?; - self.substitute_unquoted_into_expr(&mut index.index, args) - } - - fn substitute_unquoted_into_call( - &mut self, - call: &mut CallExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut call.func, args)?; - for arg in &mut call.arguments { - self.substitute_unquoted_into_expr(arg, args)?; - } - Ok(()) - } - - fn substitute_unquoted_into_method_call( - &mut self, - call: &mut MethodCallExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut call.object, args)?; - for arg in &mut call.arguments { - self.substitute_unquoted_into_expr(arg, args)?; - } - Ok(()) - } - - fn substitute_unquoted_into_constructor( - &mut self, - constructor: &mut ConstructorExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - for (_, field) in &mut constructor.fields { - self.substitute_unquoted_into_expr(field, args)?; - } - Ok(()) - } - - fn substitute_unquoted_into_access( - &mut self, - access: &mut MemberAccessExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut access.lhs, args) - } - - fn substitute_unquoted_into_cast( - &mut self, - cast: &mut CastExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut cast.lhs, args) - } - - fn substitute_unquoted_into_infix( - &mut self, - infix: &mut InfixExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut infix.lhs, args)?; - self.substitute_unquoted_into_expr(&mut infix.rhs, args) - } - - fn substitute_unquoted_into_if( - &mut self, - if_: &mut IfExpression, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut if_.condition, args)?; - self.substitute_unquoted_into_expr(&mut if_.consequence, args)?; - - if let Some(alternative) = if_.alternative.as_mut() { - self.substitute_unquoted_into_expr(alternative, args)?; - } - Ok(()) - } - - fn substitute_unquoted_into_tuple( - &mut self, - tuple: &mut [Expression], - args: &UnquoteArgs, - ) -> IResult<()> { - for field in tuple { - self.substitute_unquoted_into_expr(field, args)?; + _ => new_tokens.push(token), + } } - Ok(()) - } - fn substitute_unquoted_into_lambda( - &mut self, - lambda: &mut Lambda, - args: &UnquoteArgs, - ) -> IResult<()> { - self.substitute_unquoted_into_expr(&mut lambda.body, args) + Ok(Tokens(new_tokens)) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs index f2e21e608ead..770c523bd7fb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -100,7 +100,7 @@ impl<'interner> Interpreter<'interner> { // missed it somehow. In the future we may allow users to manually write unquote // expressions in their code but for now this is unreachable. HirExpression::Unquote(block) => { - unreachable!("Found unquote block while scanning: {block}") + unreachable!("Found unquote block while scanning: {block:?}") } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs index a75d4fd2b24d..d51d69f9226d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -1,20 +1,21 @@ use std::{borrow::Cow, fmt::Display, rc::Rc}; use acvm::{AcirField, FieldElement}; +use chumsky::Parser; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use crate::{ - ast::{ - ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, Signedness, - }, + ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness}, hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, node_interner::{ExprId, FuncId}, + parser, + token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, }; use rustc_hash::FxHashMap as HashMap; @@ -42,7 +43,7 @@ pub enum Value { Pointer(Shared), Array(Vector, Type), Slice(Vector, Type), - Code(Rc), + Code(Rc), TypeDefinition(StructId), } @@ -72,7 +73,7 @@ impl Value { Value::Struct(_, typ) => return Cow::Borrowed(typ), Value::Array(_, typ) => return Cow::Borrowed(typ), Value::Slice(_, typ) => return Cow::Borrowed(typ), - Value::Code(_) => Type::Quoted(QuotedType::Expr), + Value::Code(_) => Type::Quoted(QuotedType::Quoted), Value::TypeDefinition(_) => Type::Quoted(QuotedType::TypeDefinition), Value::Pointer(element) => { let element = element.borrow().get_type().into_owned(); @@ -174,7 +175,22 @@ impl Value { try_vecmap(elements, |element| element.into_expression(interner, location))?; ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(elements))) } - Value::Code(block) => ExpressionKind::Block(unwrap_rc(block)), + Value::Code(tokens) => { + // Wrap the tokens in '{' and '}' so that we can parse statements as well. + let mut tokens_to_parse = tokens.as_ref().clone(); + tokens_to_parse.0.insert(0, SpannedToken::new(Token::LeftBrace, location.span)); + tokens_to_parse.0.push(SpannedToken::new(Token::RightBrace, location.span)); + + return match parser::expression().parse(tokens_to_parse) { + Ok(expr) => Ok(expr), + Err(mut errors) => { + let error = errors.swap_remove(0); + let file = location.file; + let rule = "an expression"; + Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) + } + }; + } Value::Pointer(_) | Value::TypeDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } @@ -306,7 +322,7 @@ impl Value { } /// Unwraps an Rc value without cloning the inner value if the reference count is 1. Clones otherwise. -fn unwrap_rc(rc: Rc) -> T { +pub(crate) fn unwrap_rc(rc: Rc) -> T { Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()) } @@ -351,7 +367,13 @@ impl Display for Value { let values = vecmap(values, ToString::to_string); write!(f, "&[{}]", values.join(", ")) } - Value::Code(block) => write!(f, "quote {block}"), + Value::Code(tokens) => { + write!(f, "quote {{")?; + for token in tokens.0.iter() { + write!(f, " {token}")?; + } + write!(f, " }}") + } Value::TypeDefinition(_) => write!(f, "(type definition)"), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 97de66be817c..5706e62e193a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -17,7 +17,7 @@ use crate::hir_def::expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCapturedVar, HirCastExpression, HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirLiteral, HirMemberAccess, - HirMethodCallExpression, HirPrefixExpression, HirQuoted, ImplKind, + HirMethodCallExpression, HirPrefixExpression, ImplKind, }; use crate::hir_def::function::FunctionBody; @@ -1634,10 +1634,7 @@ impl<'a> Resolver<'a> { ExpressionKind::Parenthesized(sub_expr) => return self.resolve_expression(*sub_expr), // The quoted expression isn't resolved since we don't want errors if variables aren't defined - ExpressionKind::Quote(block, _) => { - let quoted = HirQuoted { quoted_block: block, unquoted_exprs: Vec::new() }; - HirExpression::Quote(quoted) - } + ExpressionKind::Quote(block) => HirExpression::Quote(block), ExpressionKind::Comptime(block, _) => { HirExpression::Comptime(self.resolve_block(block)) } @@ -1648,9 +1645,6 @@ impl<'a> Resolver<'a> { self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); HirExpression::Literal(HirLiteral::Unit) } - ExpressionKind::UnquoteMarker(index) => { - unreachable!("UnquoteMarker({index}) remaining in runtime code") - } }; // If these lines are ever changed, make sure to change the early return diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs index bd32ba2fce54..d9d021aee3f7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -143,7 +143,7 @@ pub enum TypeCheckError { }, #[error("Strings do not support indexed assignment")] StringIndexAssign { span: Span }, - #[error("Macro calls may only return Expr values")] + #[error("Macro calls may only return `Quoted` values")] MacroReturningNonExpr { typ: Type, span: Span }, } @@ -338,8 +338,8 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { Diagnostic::simple_error(msg, "".into(), *span) }, TypeCheckError::MacroReturningNonExpr { typ, span } => Diagnostic::simple_error( - format!("Expected macro call to return an `Expr` but found a(n) {typ}"), - "Macro calls must return quoted expressions, otherwise there is no code to insert".into(), + format!("Expected macro call to return a `Quoted` but found a(n) `{typ}`"), + "Macro calls must return quoted values, otherwise there is no code to insert".into(), *span, ), } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs index a1302fd15ff7..4ded04ec2a4e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -307,13 +307,13 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } - HirExpression::Quote(_) => Type::Quoted(crate::QuotedType::Expr), + HirExpression::Quote(_) => Type::Quoted(crate::QuotedType::Quoted), HirExpression::Comptime(block) => self.check_block(block), // Unquote should be inserted & removed by the comptime interpreter. // Even if we allowed it here, we wouldn't know what type to give to the result. HirExpression::Unquote(block) => { - unreachable!("Unquote remaining during type checking {block}") + unreachable!("Unquote remaining during type checking {block:?}") } }; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs index 9547aef866bb..8de4f118774a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs @@ -4,6 +4,7 @@ use noirc_errors::Location; use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId}; +use crate::token::Tokens; use crate::Shared; use super::stmt::HirPattern; @@ -33,8 +34,8 @@ pub enum HirExpression { If(HirIfExpression), Tuple(Vec), Lambda(HirLambda), - Quote(HirQuoted), - Unquote(crate::ast::BlockExpression), + Quote(Tokens), + Unquote(Tokens), Comptime(HirBlockExpression), Error, } @@ -291,13 +292,3 @@ pub struct HirLambda { pub body: ExprId, pub captures: Vec, } - -#[derive(Debug, Clone)] -pub struct HirQuoted { - pub quoted_block: crate::ast::BlockExpression, - - /// Each expression here corresponds to a `ExpressionKind::UnquoteMarker(index)` in `quoted_block`. - /// The values of these expressions after evaluation will be inlined into the position - /// indicated by their corresponding UnquoteMarker with that index. - pub unquoted_exprs: Vec, -} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 772558ec31ab..86d1fafd5021 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -190,9 +190,10 @@ impl Type { #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub enum QuotedType { Expr, - TypeDefinition, + Quoted, TopLevelItem, Type, + TypeDefinition, } /// A list of TypeVariableIds to bind to a type. Storing the @@ -295,6 +296,16 @@ impl StructType { }) } + /// Returns the name and raw types of each field of this type. + /// This will not substitute any generic arguments so a generic field like `x` + /// in `struct Foo { x: T }` will return a `("x", T)` pair. + /// + /// This method is almost never what is wanted for type checking or monomorphization, + /// prefer to use `get_fields` whenever possible. + pub fn get_fields_as_written(&self) -> Vec<(String, Type)> { + vecmap(&self.fields, |(name, typ)| (name.0.contents.clone(), typ.clone())) + } + pub fn field_names(&self) -> BTreeSet { self.fields.iter().map(|(name, _)| name.clone()).collect() } @@ -804,10 +815,11 @@ impl Type { | Type::FmtString(_, _) | Type::Error => true, - Type::MutableReference(_) - | Type::Forall(_, _) - | Type::Quoted(_) - | Type::TraitAsType(..) => false, + // Quoted objects only exist at compile-time where the only execution + // environment is the interpreter. In this environment, they are valid. + Type::Quoted(_) => true, + + Type::MutableReference(_) | Type::Forall(_, _) | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { let alias = alias.borrow(); @@ -1008,9 +1020,10 @@ impl std::fmt::Display for QuotedType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { QuotedType::Expr => write!(f, "Expr"), - QuotedType::TypeDefinition => write!(f, "TypeDefinition"), + QuotedType::Quoted => write!(f, "Quoted"), QuotedType::TopLevelItem => write!(f, "TopLevelItem"), QuotedType::Type => write!(f, "Type"), + QuotedType::TypeDefinition => write!(f, "TypeDefinition"), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs index 73c75af4cd7b..2452e034c1c5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/errors.rs @@ -27,6 +27,10 @@ pub enum LexerErrorKind { "'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character." )] InvalidEscape { escaped: char, span: Span }, + #[error("Invalid quote delimiter `{delimiter}`, valid delimiters are `{{`, `[`, and `(`")] + InvalidQuoteDelimiter { delimiter: SpannedToken }, + #[error("Expected `{end_delim}` to close this {start_delim}")] + UnclosedQuote { start_delim: SpannedToken, end_delim: Token }, } impl From for ParserError { @@ -47,6 +51,8 @@ impl LexerErrorKind { LexerErrorKind::UnterminatedBlockComment { span } => *span, LexerErrorKind::UnterminatedStringLiteral { span } => *span, LexerErrorKind::InvalidEscape { span, .. } => *span, + LexerErrorKind::InvalidQuoteDelimiter { delimiter } => delimiter.to_span(), + LexerErrorKind::UnclosedQuote { start_delim, .. } => start_delim.to_span(), } } @@ -92,6 +98,12 @@ impl LexerErrorKind { ("Unterminated string literal".to_string(), "Unterminated string literal".to_string(), *span), LexerErrorKind::InvalidEscape { escaped, span } => (format!("'\\{escaped}' is not a valid escape sequence. Use '\\' for a literal backslash character."), "Invalid escape sequence".to_string(), *span), + LexerErrorKind::InvalidQuoteDelimiter { delimiter } => { + (format!("Invalid quote delimiter `{delimiter}`"), "Valid delimiters are `{`, `[`, and `(`".to_string(), delimiter.to_span()) + }, + LexerErrorKind::UnclosedQuote { start_delim, end_delim } => { + ("Unclosed `quote` expression".to_string(), format!("Expected a `{end_delim}` to close this `{start_delim}`"), start_delim.to_span()) + } } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs index 3d052e22e361..d2bc3d0f5192 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs @@ -145,6 +145,7 @@ impl<'a> Lexer<'a> { Some('"') => self.eat_string_literal(), Some('f') => self.eat_format_string_or_alpha_numeric(), Some('r') => self.eat_raw_string_or_alpha_numeric(), + Some('q') => self.eat_quote_or_alpha_numeric(), Some('#') => self.eat_attribute(), Some(ch) if ch.is_ascii_alphanumeric() || ch == '_' => self.eat_alpha_numeric(ch), Some(ch) => { @@ -310,14 +311,25 @@ impl<'a> Lexer<'a> { //XXX(low): Can increase performance if we use iterator semantic and utilize some of the methods on String. See below // https://doc.rust-lang.org/stable/std/primitive.str.html#method.rsplit fn eat_word(&mut self, initial_char: char) -> SpannedTokenResult { - let start = self.position; + let (start, word, end) = self.lex_word(initial_char); + self.lookup_word_token(word, start, end) + } + /// Lex the next word in the input stream. Returns (start position, word, end position) + fn lex_word(&mut self, initial_char: char) -> (Position, String, Position) { + let start = self.position; let word = self.eat_while(Some(initial_char), |ch| { ch.is_ascii_alphabetic() || ch.is_numeric() || ch == '_' }); + (start, word, self.position) + } - let end = self.position; - + fn lookup_word_token( + &self, + word: String, + start: Position, + end: Position, + ) -> SpannedTokenResult { // Check if word either an identifier or a keyword if let Some(keyword_token) = Keyword::lookup_keyword(&word) { return Ok(keyword_token.into_span(start, end)); @@ -509,6 +521,50 @@ impl<'a> Lexer<'a> { } } + fn eat_quote_or_alpha_numeric(&mut self) -> SpannedTokenResult { + let (start, word, end) = self.lex_word('q'); + if word != "quote" { + return self.lookup_word_token(word, start, end); + } + + let delimiter = self.next_token()?; + let (start_delim, end_delim) = match delimiter.token() { + Token::LeftBrace => (Token::LeftBrace, Token::RightBrace), + Token::LeftBracket => (Token::LeftBracket, Token::RightBracket), + Token::LeftParen => (Token::LeftParen, Token::RightParen), + _ => return Err(LexerErrorKind::InvalidQuoteDelimiter { delimiter }), + }; + + let mut tokens = Vec::new(); + + // Keep track of each nested delimiter we need to close. + let mut nested_delimiters = vec![delimiter]; + + while !nested_delimiters.is_empty() { + let token = self.next_token()?; + + if *token.token() == start_delim { + nested_delimiters.push(token.clone()); + } else if *token.token() == end_delim { + nested_delimiters.pop(); + } else if *token.token() == Token::EOF { + let start_delim = + nested_delimiters.pop().expect("If this were empty, we wouldn't be looping"); + return Err(LexerErrorKind::UnclosedQuote { start_delim, end_delim }); + } + + tokens.push(token); + } + + // Pop the closing delimiter from the token stream + if !tokens.is_empty() { + tokens.pop(); + } + + let end = self.position; + Ok(Token::Quote(Tokens(tokens)).into_span(start, end)) + } + fn parse_comment(&mut self, start: u32) -> SpannedTokenResult { let doc_style = match self.peek_char() { Some('!') => { @@ -604,6 +660,8 @@ impl<'a> Iterator for Lexer<'a> { #[cfg(test)] mod tests { + use iter_extended::vecmap; + use super::*; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; @@ -1232,4 +1290,45 @@ mod tests { } } } + + #[test] + fn test_quote() { + // cases is a vector of pairs of (test string, expected # of tokens in token stream) + let cases = vec![ + ("quote {}", 0), + ("quote { a.b }", 3), + ("quote { ) ( }", 2), // invalid syntax is fine in a quote + ("quote { { } }", 2), // Nested `{` and `}` shouldn't close the quote as long as they are matched. + ("quote { 1 { 2 { 3 { 4 { 5 } 4 4 } 3 3 } 2 2 } 1 1 }", 21), + ("quote [ } } ]", 2), // In addition to `{}`, `[]`, and `()` can also be used as delimiters. + ("quote [ } foo[] } ]", 5), + ("quote ( } () } )", 4), + ]; + + for (source, expected_stream_length) in cases { + let mut tokens = vecmap(Lexer::new(source), |result| result.unwrap().into_token()); + + // All examples should be a single TokenStream token followed by an EOF token. + assert_eq!(tokens.len(), 2, "Unexpected token count: {tokens:?}"); + + tokens.pop(); + match tokens.pop().unwrap() { + Token::Quote(stream) => assert_eq!(stream.0.len(), expected_stream_length), + other => panic!("test_quote test failure! Expected a single TokenStream token, got {other} for input `{source}`") + } + } + } + + #[test] + fn test_unclosed_quote() { + let cases = vec!["quote {", "quote { { }", "quote [ []", "quote (((((((())))"]; + + for source in cases { + // `quote` is not itself a keyword so if the token stream fails to + // parse we don't expect any valid tokens from the quote construct + for token in Lexer::new(source) { + assert!(token.is_err(), "Expected Err, found {token:?}"); + } + } + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index ec589a816f6c..6830ee528d6d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -2,7 +2,7 @@ use acvm::{acir::AcirField, FieldElement}; use noirc_errors::{Position, Span, Spanned}; use std::{fmt, iter::Map, vec::IntoIter}; -use crate::lexer::errors::LexerErrorKind; +use crate::{lexer::errors::LexerErrorKind, node_interner::ExprId}; /// Represents a token in noir's grammar - a word, number, /// or symbol that can be used in noir's syntax. This is the @@ -23,6 +23,7 @@ pub enum BorrowedToken<'input> { Attribute(Attribute), LineComment(&'input str, Option), BlockComment(&'input str, Option), + Quote(&'input Tokens), /// < Less, /// <= @@ -94,6 +95,11 @@ pub enum BorrowedToken<'input> { Whitespace(&'input str), + /// This is an implementation detail on how macros are implemented by quoting token streams. + /// This token marks where an unquote operation is performed. The ExprId argument is the + /// resolved variable which is being unquoted at this position in the token stream. + UnquoteMarker(ExprId), + /// An invalid character is one that is not in noir's language or grammar. /// /// We don't report invalid tokens in the source as errors until parsing to @@ -117,6 +123,8 @@ pub enum Token { Attribute(Attribute), LineComment(String, Option), BlockComment(String, Option), + // A `quote { ... }` along with the tokens in its token stream. + Quote(Tokens), /// < Less, /// <= @@ -188,6 +196,11 @@ pub enum Token { Whitespace(String), + /// This is an implementation detail on how macros are implemented by quoting token streams. + /// This token marks where an unquote operation is performed. The ExprId argument is the + /// resolved variable which is being unquoted at this position in the token stream. + UnquoteMarker(ExprId), + /// An invalid character is one that is not in noir's language or grammar. /// /// We don't report invalid tokens in the source as errors until parsing to @@ -209,6 +222,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::Attribute(ref a) => BorrowedToken::Attribute(a.clone()), Token::LineComment(ref s, _style) => BorrowedToken::LineComment(s, *_style), Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), + Token::Quote(stream) => BorrowedToken::Quote(stream), Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, @@ -246,6 +260,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::EOF => BorrowedToken::EOF, Token::Invalid(c) => BorrowedToken::Invalid(*c), Token::Whitespace(ref s) => BorrowedToken::Whitespace(s), + Token::UnquoteMarker(id) => BorrowedToken::UnquoteMarker(*id), } } @@ -255,7 +270,7 @@ pub enum DocStyle { Inner, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SpannedToken(Spanned); impl PartialEq for Token { @@ -321,6 +336,13 @@ impl fmt::Display for Token { Token::Attribute(ref a) => write!(f, "{a}"), Token::LineComment(ref s, _style) => write!(f, "//{s}"), Token::BlockComment(ref s, _style) => write!(f, "/*{s}*/"), + Token::Quote(ref stream) => { + write!(f, "quote {{")?; + for token in stream.0.iter() { + write!(f, " {token}")?; + } + write!(f, "}}") + } Token::IntType(ref i) => write!(f, "{i}"), Token::Less => write!(f, "<"), Token::LessEqual => write!(f, "<="), @@ -358,6 +380,7 @@ impl fmt::Display for Token { Token::EOF => write!(f, "end of input"), Token::Invalid(c) => write!(f, "{c}"), Token::Whitespace(ref s) => write!(f, "{s}"), + Token::UnquoteMarker(_) => write!(f, "(UnquoteMarker)"), } } } @@ -370,6 +393,8 @@ pub enum TokenKind { Literal, Keyword, Attribute, + Quote, + UnquoteMarker, } impl fmt::Display for TokenKind { @@ -380,13 +405,15 @@ impl fmt::Display for TokenKind { TokenKind::Literal => write!(f, "literal"), TokenKind::Keyword => write!(f, "keyword"), TokenKind::Attribute => write!(f, "attribute"), + TokenKind::Quote => write!(f, "quote"), + TokenKind::UnquoteMarker => write!(f, "macro result"), } } } impl Token { pub fn kind(&self) -> TokenKind { - match *self { + match self { Token::Ident(_) => TokenKind::Ident, Token::Int(_) | Token::Bool(_) @@ -395,7 +422,9 @@ impl Token { | Token::FmtStr(_) => TokenKind::Literal, Token::Keyword(_) => TokenKind::Keyword, Token::Attribute(_) => TokenKind::Attribute, - ref tok => TokenKind::Token(tok.clone()), + Token::UnquoteMarker(_) => TokenKind::UnquoteMarker, + Token::Quote(_) => TokenKind::Quote, + tok => TokenKind::Token(tok.clone()), } } @@ -859,7 +888,7 @@ pub enum Keyword { Mod, Mut, Pub, - Quote, + Quoted, Return, ReturnData, String, @@ -907,7 +936,7 @@ impl fmt::Display for Keyword { Keyword::Mod => write!(f, "mod"), Keyword::Mut => write!(f, "mut"), Keyword::Pub => write!(f, "pub"), - Keyword::Quote => write!(f, "quote"), + Keyword::Quoted => write!(f, "Quoted"), Keyword::Return => write!(f, "return"), Keyword::ReturnData => write!(f, "return_data"), Keyword::String => write!(f, "str"), @@ -958,7 +987,7 @@ impl Keyword { "mod" => Keyword::Mod, "mut" => Keyword::Mut, "pub" => Keyword::Pub, - "quote" => Keyword::Quote, + "Quoted" => Keyword::Quoted, "return" => Keyword::Return, "return_data" => Keyword::ReturnData, "str" => Keyword::String, @@ -984,6 +1013,7 @@ impl Keyword { } } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Tokens(pub Vec); type TokenMapIter = Map, fn(SpannedToken) -> (Token, Span)>; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs index 2db570540d62..df61c138c021 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -6,6 +6,7 @@ use crate::hir::comptime::InterpreterError; pub enum MonomorphizationError { UnknownArrayLength { location: Location }, TypeAnnotationsNeeded { location: Location }, + InternalError { message: &'static str, location: Location }, InterpreterError(InterpreterError), } @@ -13,6 +14,7 @@ impl MonomorphizationError { fn location(&self) -> Location { match self { MonomorphizationError::UnknownArrayLength { location } + | MonomorphizationError::InternalError { location, .. } | MonomorphizationError::TypeAnnotationsNeeded { location } => *location, MonomorphizationError::InterpreterError(error) => error.get_location(), } @@ -36,6 +38,7 @@ impl MonomorphizationError { } MonomorphizationError::TypeAnnotationsNeeded { .. } => "Type annotations needed", MonomorphizationError::InterpreterError(error) => return (&error).into(), + MonomorphizationError::InternalError { message, .. } => message, }; let location = self.location(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index faf8141df223..a95fc0e7ace0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -241,9 +241,7 @@ impl<'interner> Monomorphizer<'interner> { Definition::Oracle(opcode.to_string()) } FunctionKind::Recursive => { - let id = - self.queue_function(id, expr_id, typ, turbofish_generics, trait_method); - Definition::Function(id) + unreachable!("Only main can be specified as recursive, which should already be checked"); } } } @@ -891,7 +889,11 @@ impl<'interner> Monomorphizer<'interner> { DefinitionKind::Local(_) => match self.lookup_captured_expr(ident.id) { Some(expr) => expr, None => { - let ident = self.local_ident(&ident)?.unwrap(); + let Some(ident) = self.local_ident(&ident)? else { + let location = self.interner.id_location(expr_id); + let message = "ICE: Variable not found during monomorphization"; + return Err(MonomorphizationError::InternalError { location, message }); + }; ast::Expression::Ident(ident) } }, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index a76cbe4da943..78b2c056dfd5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -303,7 +303,7 @@ impl StmtId { } } -#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone, PartialOrd, Ord)] pub struct ExprId(Index); impl ExprId { @@ -624,15 +624,6 @@ impl NodeInterner { f(&mut value); } - pub fn update_struct_attributes( - &mut self, - type_id: StructId, - f: impl FnOnce(&mut StructAttributes), - ) { - let value = self.struct_attributes.get_mut(&type_id).unwrap(); - f(value); - } - pub fn update_trait(&mut self, trait_id: TraitId, f: impl FnOnce(&mut Trait)) { let value = self.traits.get_mut(&trait_id).unwrap(); f(value); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop index 5768e3c1d179..5bf48a764d63 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop +++ b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop @@ -80,7 +80,6 @@ extern { "mod" => BorrowedToken::Keyword(noir_token::Keyword::Mod), "mut" => BorrowedToken::Keyword(noir_token::Keyword::Mut), "pub" => BorrowedToken::Keyword(noir_token::Keyword::Pub), - "quote" => BorrowedToken::Keyword(noir_token::Keyword::Quote), "return" => BorrowedToken::Keyword(noir_token::Keyword::Return), "return_data" => BorrowedToken::Keyword(noir_token::Keyword::ReturnData), "str" => BorrowedToken::Keyword(noir_token::Keyword::String), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs index 9e60383afee3..72b1ea05ec26 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -22,7 +22,7 @@ use chumsky::primitive::Container; pub use errors::ParserError; pub use errors::ParserErrorReason; use noirc_errors::Span; -pub use parser::parse_program; +pub use parser::{expression, parse_program}; #[derive(Debug, Clone)] pub(crate) enum TopLevelStatement { @@ -45,7 +45,7 @@ pub trait NoirParser: Parser + Sized + Clone { impl NoirParser for P where P: Parser + Clone {} // ExprParser just serves as a type alias for NoirParser + Clone -trait ExprParser: NoirParser {} +pub trait ExprParser: NoirParser {} impl

ExprParser for P where P: NoirParser {} fn parenthesized(parser: P) -> impl NoirParser diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs index e8838c587721..0ae810fe4d95 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -23,7 +23,7 @@ //! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. -use self::primitives::{keyword, mutable_reference, variable}; +use self::primitives::{keyword, macro_quote_marker, mutable_reference, variable}; use self::types::{generic_type_args, maybe_comp_time, parse_type}; use super::{ @@ -691,7 +691,7 @@ fn optional_visibility() -> impl NoirParser { }) } -fn expression() -> impl ExprParser { +pub fn expression() -> impl ExprParser { recursive(|expr| { expression_with_precedence( Precedence::Lowest, @@ -1074,11 +1074,12 @@ where }, lambdas::lambda(expr_parser.clone()), block(statement.clone()).map(ExpressionKind::Block), - comptime_expr(statement.clone()), - quote(statement), + comptime_expr(statement), + quote(), unquote(expr_parser.clone()), variable(), literal(), + macro_quote_marker(), )) .map_with_span(Expression::new) .or(parenthesized(expr_parser.clone()).map_with_span(|sub_expr, span| { @@ -1101,19 +1102,18 @@ where .labelled(ParsingRuleLabel::Atom) } -fn quote<'a, P>(statement: P) -> impl NoirParser + 'a -where - P: NoirParser + 'a, -{ - keyword(Keyword::Quote).ignore_then(spanned(block(statement))).validate( - |(block, block_span), span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("quoted expressions"), - span, - )); - ExpressionKind::Quote(block, block_span) - }, - ) +fn quote() -> impl NoirParser { + token_kind(TokenKind::Quote).validate(|token, span, emit| { + let tokens = match token { + Token::Quote(tokens) => tokens, + _ => unreachable!("token_kind(Quote) should guarantee parsing only a quote token"), + }; + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("quoted expressions"), + span, + )); + ExpressionKind::Quote(tokens) + }) } /// unquote: '$' variable @@ -1642,4 +1642,19 @@ mod test { check_cases_with_errors(&cases[..], block(fresh_statement())); } + + #[test] + fn test_quote() { + let cases = vec![ + "quote {}", + "quote { a.b }", + "quote { ) ( }", // invalid syntax is fine in a quote + "quote { { } }", // Nested `{` and `}` shouldn't close the quote as long as they are matched. + "quote { 1 { 2 { 3 { 4 { 5 } 4 4 } 3 3 } 2 2 } 1 1 }", + ]; + parse_all(quote(), cases); + + let failing = vec!["quote {}}", "quote a", "quote { { { } } } }"]; + parse_all_failing(quote(), failing); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs index 9da19c0a185d..88f9e591aba5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -94,6 +94,13 @@ pub(super) fn variable_no_turbofish() -> impl NoirParser { path().map(|path| ExpressionKind::Variable(path, None)) } +pub(super) fn macro_quote_marker() -> impl NoirParser { + token_kind(TokenKind::UnquoteMarker).map(|token| match token { + Token::UnquoteMarker(expr_id) => ExpressionKind::Resolved(expr_id), + other => unreachable!("Non-unquote-marker parsed as an unquote marker: {other:?}"), + }) +} + #[cfg(test)] mod test { use crate::parser::parser::{ diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs index a961b32f9d79..14840bafa049 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -26,6 +26,7 @@ pub(super) fn parse_type_inner<'a>( expr_type(), type_definition_type(), top_level_item_type(), + type_of_quoted_types(), quoted_type(), format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), @@ -93,11 +94,17 @@ fn top_level_item_type() -> impl NoirParser { } /// This is the type `Type` - the type of a quoted noir type. -fn quoted_type() -> impl NoirParser { +fn type_of_quoted_types() -> impl NoirParser { keyword(Keyword::TypeType) .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Type).with_span(span)) } +/// This is the type of a quoted, unparsed token stream. +fn quoted_type() -> impl NoirParser { + keyword(Keyword::Quoted) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Quoted).with_span(span)) +} + pub(super) fn string_type() -> impl NoirParser { keyword(Keyword::String) .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 1acf1fbf3f2d..f4845625b874 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -14,6 +14,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; +use crate::hir::comptime::InterpreterError; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::errors::{DefCollectorErrorKind, DuplicateType}; use crate::hir::def_map::ModuleData; @@ -1444,3 +1445,26 @@ fn specify_method_types_with_turbofish() { let errors = get_program_errors(src); assert_eq!(errors.len(), 0); } + +#[test] +fn quote_code_fragments() { + // This test ensures we can quote (and unquote/splice) code fragments + // which by themselves are not valid code. They only need to be valid + // by the time they are unquoted into the macro's call site. + let src = r#" + fn main() { + comptime { + concat!(quote { assert( }, quote { false); }); + } + } + + comptime fn concat(a: Quoted, b: Quoted) -> Quoted { + quote { $a $b } + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + use InterpreterError::FailingConstraint; + assert!(matches!(&errors[0].0, CompilationError::InterpreterError(FailingConstraint { .. }))); +} diff --git a/noir/noir-repo/docs/docs/how_to/how-to-oracles.md b/noir/noir-repo/docs/docs/how_to/how-to-oracles.md index d6834c09c847..2d2ed5c94b95 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-oracles.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-oracles.md @@ -141,10 +141,10 @@ server.addMethod("resolve_function_call", async (params) => { if params.function !== "getSqrt" { throw Error("Unexpected foreign call") }; - const values = params.inputs[0].map((field) => { + const values = params.inputs[0].Array.map((field) => { return `${Math.sqrt(parseInt(field, 16))}`; }); - return { values }; + return { values: [{ Array: values }] }; }); ``` @@ -236,9 +236,9 @@ const foreignCallHandler = async (name, input) => { // notice that the "inputs" parameter contains *all* the inputs // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] const oracleReturn = await client.request(name, [ - input[0].map((i) => i.toString("hex")), + { Array: input[0].map((i) => i.toString("hex")) }, ]); - return { values: oracleReturn }; + return [oracleReturn.values[0].Array]; }; // the rest of your NoirJS code diff --git a/noir/noir-repo/noir_stdlib/src/hash.nr b/noir/noir-repo/noir_stdlib/src/hash.nr index d93261c576d8..62b47b672410 100644 --- a/noir/noir-repo/noir_stdlib/src/hash.nr +++ b/noir/noir-repo/noir_stdlib/src/hash.nr @@ -36,7 +36,7 @@ pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { } } -pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { +fn pedersen_commitment_with_separator_noir(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N]; for i in 0..N { points[i] = EmbeddedCurveScalar::from_field(input[i]); @@ -46,6 +46,11 @@ pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: values[2] as bool } } +pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { + let values = __pedersen_commitment_with_separator(input, separator); + EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: false } +} + // docs:start:pedersen_hash pub fn pedersen_hash(input: [Field; N]) -> Field // docs:end:pedersen_hash @@ -64,15 +69,21 @@ fn derive_generators(domain_separator_bytes: [u8; M], starting_index: u32) #[field(bn254)] fn __derive_generators(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] {} -pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field { +fn pedersen_hash_with_separator_noir(input: [Field; N], separator: u32) -> Field { let v1 = pedersen_commitment_with_separator(input, separator); - let length_generator: [EmbeddedCurvePoint; 1] = derive_generators("pedersen_hash_length".as_bytes(), 0); + let length_generator : [EmbeddedCurvePoint; 1] = derive_generators("pedersen_hash_length".as_bytes(), 0); multi_scalar_mul( [length_generator[0], v1], [EmbeddedCurveScalar { lo: N as Field, hi: 0 }, EmbeddedCurveScalar { lo: 1, hi: 0 }] )[0] } +#[foreign(pedersen_hash)] +pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} + +#[foreign(pedersen_commitment)] +fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} + pub fn hash_to_field(inputs: [Field]) -> Field { let mut sum = 0; @@ -246,3 +257,11 @@ impl Hash for (A, B, C, D, E) where A: Hash, B: Hash, C: Hash, D: self.4.hash(state); } } + +#[test] +fn assert_pedersen_noir() { + // TODO: make this a fuzzer test once fuzzer supports curve-specific blackbox functions. + let input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq(pedersen_hash_with_separator(input, 4), pedersen_hash_with_separator_noir(input, 4)); + assert_eq(pedersen_commitment_with_separator(input, 4), pedersen_commitment_with_separator_noir(input, 4)); +} diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index ad47171fa468..1a756f441ba8 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -26,6 +26,7 @@ mod prelude; mod uint128; mod bigint; mod runtime; +mod meta; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident diff --git a/noir/noir-repo/noir_stdlib/src/meta.nr b/noir/noir-repo/noir_stdlib/src/meta.nr new file mode 100644 index 000000000000..1825888130b4 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta.nr @@ -0,0 +1 @@ +mod type_def; diff --git a/noir/noir-repo/noir_stdlib/src/meta/type_def.nr b/noir/noir-repo/noir_stdlib/src/meta/type_def.nr new file mode 100644 index 000000000000..b93544859215 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/type_def.nr @@ -0,0 +1,16 @@ +impl TypeDefinition { + /// Return a syntactic version of this type definition as a type. + /// For example, `as_type(quote { type Foo { ... } })` would return `Foo` + #[builtin(type_def_as_type)] + fn as_type(self) -> Quoted {} + + /// Return each generic on this type. The names of these generics are unchanged + /// so users may need to keep name collisions in mind if this is used directly in a macro. + #[builtin(type_def_generics)] + fn generics(self) -> [Quoted] {} + + /// Returns (name, type) pairs of each field in this type. Each type is as-is + /// with any generic arguments unchanged. + #[builtin(type_def_fields)] + fn fields(self) -> [(Quoted, Quoted)] {} +} diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/Nargo.toml similarity index 70% rename from noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/Nargo.toml index dc9bf7b38736..099545a9e719 100644 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "verify_honk_proof" +name = "comptime_type_definition" type = "bin" authors = [""] compiler_version = ">=0.31.0" diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr new file mode 100644 index 000000000000..025f6a0b0bfa --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr @@ -0,0 +1,13 @@ +fn main() {} + +#[my_comptime_fn] +struct MyType { + field1: [A; 10], + field2: (B, C), +} + +comptime fn my_comptime_fn(typ: TypeDefinition) { + let _ = typ.as_type(); + assert_eq(typ.generics().len(), 3); + assert_eq(typ.fields().len(), 2); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/derive_impl/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/derive_impl/Nargo.toml new file mode 100644 index 000000000000..26a6020a6b1f --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/derive_impl/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "derive_impl" +type = "bin" +authors = [""] +compiler_version = ">=0.30.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr new file mode 100644 index 000000000000..abad6d4f8e12 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr @@ -0,0 +1,44 @@ +comptime fn derive_default(typ: TypeDefinition) -> Quoted { + let generics: [Quoted] = typ.generics(); + assert_eq( + generics.len(), 0, "derive_default: Deriving Default on generic types is currently unimplemented" + ); + + let type_name = typ.as_type(); + let fields = typ.fields(); + + let fields = join(make_field_exprs(fields)); + + quote { + impl Default for $type_name { + fn default() -> Self { + Self { $fields } + } + } + } +} + +#[derive_default] +struct Foo { + x: Field, + y: u32, +} + +comptime fn make_field_exprs(fields: [(Quoted, Quoted)]) -> [Quoted] { + let mut result = &[]; + for my_field in fields { + let name = my_field.0; + result = result.push_back(quote { $name: Default::default(), }); + } + result +} + +comptime fn join(slice: [Quoted]) -> Quoted { + let mut result = quote {}; + for elem in slice { + result = quote { $result $elem }; + } + result +} + +fn main() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/macros/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/macros/src/main.nr index f466226d575f..1b00a084c619 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/macros/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/macros/src/main.nr @@ -1,10 +1,8 @@ -comptime fn my_macro(x: Field, y: Field) -> Expr { +comptime fn my_macro(x: Field, y: Field) -> Quoted { // Current version of macros in Noir are not hygienic // so we can quote a and b here and expect them to resolve // to the a and b in main at the callsite of my_macro. - quote { - $x + $y + a + b - } + quote { $x + $y + a + b } } fn main() { diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml deleted file mode 100644 index 1ebc77c5a5fd..000000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml +++ /dev/null @@ -1,4 +0,0 @@ -key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" -proof = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632","0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc","0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62","0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c","0x000000000000000000000000000000b0804efd6573805f991458295f510a2004","0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e","0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47","0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15","0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd","0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383","0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4","0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98","0x00000000000000000000000000000044d7ca77b464f03aa44f6f8d49a0d3ada5","0x00000000000000000000000000000000002a36959f550517d82d0af666bcd7dc","0x0000000000000000000000000000000566b28c19f0b1732b95e0381bc5d6dbdd","0x00000000000000000000000000000000002511360b7a8c6a823559f0ac9eb02b","0x000000000000000000000000000000f968b227a358a305607f3efc933823d288","0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08","0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f","0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1","0x00000000000000000000000000000058035b1ed115023f42bf4ee93d2dc29dcb","0x00000000000000000000000000000000002de4b004225be4e68938b0db546287","0x0000000000000000000000000000003d18d72585ef033ab3663d1944abb2054a","0x0000000000000000000000000000000000149a1974c0c2b5f0639970cda1af83","0x000000000000000000000000000000bb1eb2b1fc10b55295ed6c1ae54e8a40da","0x000000000000000000000000000000000026da80059472ac8c64e437d6fe6134","0x000000000000000000000000000000d1f101b72ee710423ca44548910676a4fe","0x00000000000000000000000000000000000323378ad6b5aec67af99e522095a0","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x2622384e4b4688a3ad115007c1c09de1a141aeca06c31925898cf746038a5897","0x2f743e893a3880004db1ff3492279d89c025b9815f16e129d15f7a3687b6f833","0x03e05487307f18e3afb90cc524e56809e478039d317a3757433bfc8e06a32b73","0x099ba7011747dd2d8b5ac03ed02b93c9803d51899677409931d5b1571c3041b5","0x189ef108e334c5173619eac1067b99526a5cc6e47cbffaa3c117f0c3eb8bebd4","0x0b5f77b69ac2955ecc44a73e18b2ea8403224cf769657d53acc9a5d302d0b86e","0x1b81353a160e985e8a1fb09d3a3827fe68d03585757530dcec1b8038ac829a21","0x175e75cef1b974011de38e6e631f42bffd4dcb6fad6680930388cffaa60d940e","0x1631945a2aa39032cfa8cf379d18a983d4b5a487adab67252c6514b35bc88095","0x181b639e465a6f9842c5d75f6f5b855a065f498595146df3bd2b9c0ef66042a1","0x0c6e5af7add3e12f610c13d8066896d08882a7c50cfe33676fda8a75e250e9b9","0x28f94cd060c45a2e6b423831302deb456d0964879db5008a2be0957a2c749e2a","0x1c81fb20cea508580aa962e5b4736a43382816e7abac7e478e6c080cf896798d","0x23dea53784aa14dcf7e1cce5ee480796e67b2dd69a8e20c5c09558001640edfa","0x149c2548f8b0d96fefecab53e31aa3902341c903fa0ef863ef64610315de993b","0x16ad81b3129ccebe1682d14b726bc9b86acd0f0be8c304594ce5a87e756add27","0x2c1ef938516edccc0cd1d4d812644d72b6ead3c85e1c8500fc54e77e5652b23f","0x0eecb7fba3395b21197cb24bb9b733b1985d81f35a1ee944714ffd781a7bd136","0x06e2a96ecf1e8419198eca10133954f3560102467f40a234cf071d23c6cf411a","0x1e6bfa2adcbdc50313408ef28a77b76dd915fa372c093c4484ba662695a3eadc","0x28ccaf4d4759c1f4bb49429b961a59cdefbc445017ffa807e90c54b27e1ee657","0x22803d537311e757a146ae7a2fc396d42d67f27e73efca82e3e324dc493da4de","0x196255f687cede05f326204bfaead7a54f8d48b67ce8522cb8af6a7fffaffcb6","0x147ea42988386b944f006be242ccc6b099fadd7f450955d252768667bbaee4f9","0x1f9ccb05e508b1d08c79c11acbc0677fdc18d5d40827e2e1eaae60fee51b940f","0x28ea76870d22eea72821da25f9b7a89341347afcd6c077387986a82dc8afa833","0x0e6ef82d3e5a318a9c6233dffbb00d130599f4ac979a89b034ce9d930b11165a","0x2e97fa9299a218c982504199ada3278270b9cb566bf46fe1ecc1d151e06b8745","0x1a41ac9b1032ac24c11720407c253a866e9c75a4ec233f15f968b206ea1e5d0e","0x0b31b541bb044c1bc2428c2a57ba29438f620050d1628389ff1fa90c494d7c58","0x050fec8d69f182768a9b34eca8c3f4695dad8bc20a10904090cfe18777d44d25","0x069283ac40daaafff76c3679f54a0aa773c8d71152fbb9c3219906113fc4f683","0x25c3ec4e8b90214aafe3b5416abf11a98bd34b8acb449df8424f159ddf858bc1","0x1a3884f3a922d0da758cb7ed9a5ddc3c3c2132dde8d913753fa3e6b766be5697","0x222d05a0fce0565bf9cc490f97bd4eff53858f2ca6afe9d91c5c8d7de8076f39","0x054698b045b439467a3067a8dc2b4d020b2bb44df3d98a19f9cfb04c9ee5ffd1","0x0e39d66cded0f3df40e04124e36c827bcaf15fbe9fb6e9bbc3af889f8bd1ebf0","0x145aea47dc97ec35ac67f135aac37f8bc6eaf149551a2f48901529d10e25c860","0x1894877b2769ae2c288738f8aa33acfc7ca9a7d1e26a76908ca2909bf25aa59a","0x27e8c702be67be467f052abd180464a468b7d5d5d8a4961e56e8561f7863c91a","0x0326d3e4607d54a30c7fa99d1609f386aeb8c8094cabd7397246074f634dcec8","0x17eb8f62b5ba2dad391e3f81d3a6b9d03ff723a7d6a4d77b98b18ddd0debf4fd","0x1a5d3e8a27c1f69d6e4558b3c89cd9347c62182ce90fb6e34392bc4e7b7c178c","0x2293034bed3d33d5ad0d150f64d493c9be554f640103621f9ae56034a7323d84","0x13d75ffbb9d2ceb2daa6d42f3618d4ea9775befa1cf5f9df141dfebf794abc35","0x2ec339c42fbb2d50221ec907779e72be3eab2960d110a90d36cc6c0afcf5857e","0x15e9c913fa84a2657571831d5d7a90f6534ca67a1617b4063fa5bf09f46cd7a2","0x10f56fbe9fefd59d2acd49fa641fedcfb65d96d54cf47207e2c8ab34f22bbabe","0x117fa3859a400040ebe8dee4a60ddcb04484ff5cfb5294c6530354c3c8cb35f3","0x123260b824df2f8bbe6a351ba2fa94c61aa754741eb198b768a699b2d1cc2b6f","0x1e51d9a653adc6b67287d35bb60584261f57363177c6b54a56dbd39834d851ba","0x18a9b2e2fce77bdb5e41215e2caeb7e77e946dbb2f381c8e7974709e03a6c216","0x2b2640870195a40e374cfa834e37ad9a5e17cb687bd2119a63ac02c3769b0f1e","0x2da73263fef362dfc79dd1066fd7ec294b765e2533f3ac4320e8d1540f2639a8","0x0cc9f299e5291bb1bc0951ce510a634c418af9f9802a291fe6d951768c0a1b2d","0x02a940acb788df42cc9219531776d45465be19087fc3f523fe92df771e5efc10","0x2d5976cc5540e761824bdacf69a2dddabe104fdbb235985ae9080b488f642fa9","0x284c18d1574d2cb7b4ee45b3ff30176eff2ab9c7b7f60cd8a87cef599379244d","0x12a38d659bf38da09af8f445505baa16bcb036d83173f6f45a7e46cac511e5a1","0x0852ef710b2396ba5b7fd69a95b336908d3a368262ec41e0d972564f784201a4","0x240c467a31ed3bb7c4cef09407750d2d89b3750e6cebb4aaa9d0f1f92be77249","0x04edf7595087745abc11fe7780afd4754c5013725653a4cec31f039b77e7b3c7","0x080d04b50ae3acd787f33f8f4a639a58677b5c04ef8a352fd4dd9236883f0e81","0x0cd745e7540fe230038f024ab1269177599ad94e8d8099a010eb7eebd3e41ec8","0x25e2394f90f5b3e3046b8876a6b3ef19a03ef9e9aeae4813fcb14907decc0393","0x03df12a6e39c606d70d3d470aff710d9daa86dece773a6f6f057725b57d6d115","0x0f744082aecf54f55db19dfbe56a81c17b3eb48417305c129beb6c97a22c705b","0x244a80d6d82e82fc416e8e4694deb4e08b81c32bb90cb2f96ff3f687298322d1","0x251eb4d8692f49523e3972096264ee770b295fb62a970fbfdd8aa1fff661ef50","0x0c4d9200120430618493a9151d632faa95c9ae842b7d97103a4afb3330cafbed","0x09e970a55dd7335db16a3823b6489c77cb7785f674cb7c924994ee121122e514","0x19e5bd1113959463be673ee72103bfe7559f423c632fbf9701ff099e165c429b","0x071eb2916ba30652a328c98353f69f239c41a4913c34931f18e91e5414b3270a","0x2a0cd2ebac904b7ebd82b6509dfcaf9ecf32175758c691d01f4fb32dad6371c4","0x1aa43a3009417d95904ebecd4189545e52ca7e9c7dfa3bde5f255ddefed5c754","0x29fd7a93212d60af81b810dad13a240bbbe16966a4977408b1d64c5d692b50b4","0x000000000000000000000000000000bef7cad70fa62891e6329cb7c17d0c5459","0x0000000000000000000000000000000000209177f2a04609421c1f23c04b454e","0x00000000000000000000000000000060dec389686170618e2490100f3fcf39e2","0x0000000000000000000000000000000000213368873145aad5f93798c31730af","0x000000000000000000000000000000c0f21a470488d9cbe53650d941c25cd569","0x000000000000000000000000000000000016d6f88e6b319553f5948886a6bd5e","0x000000000000000000000000000000d6dbb8a54a071e01c46d648c8c555ec352","0x0000000000000000000000000000000000130a7ce06ad74eb6c83f5565e2f821","0x00000000000000000000000000000058ca3aa788bd6ff37a5da3ecefdc896601","0x00000000000000000000000000000000001381bddcf8fb976cc52fee0d920598","0x00000000000000000000000000000082bdd94acd10edf22e09b1a42be500f8f8","0x00000000000000000000000000000000002f27815e28b2bc0699336893abdc0f","0x000000000000000000000000000000eb1d6973a54f8848f4c0630370d6181e49","0x000000000000000000000000000000000000129c1889d64ab66303bf17bfc864","0x000000000000000000000000000000155918aa9f6d352b847bf860a261266282","0x0000000000000000000000000000000000216e687d2f85a811f67573cbf311ba","0x0000000000000000000000000000002d2662f79a7ba21a95f44e67ed0b5abf3b","0x00000000000000000000000000000000001351870a81dc6edff235df110fe798","0x000000000000000000000000000000b113a55b86f59b21fe419ed8518dfddfc6","0x00000000000000000000000000000000002f26cd920f79b0d72a49897acc521c","0x0000000000000000000000000000002a4e1689c65dcae73ed1a33b03c611a7fe","0x00000000000000000000000000000000001c5093a8ae791c00fdd763c95800c5","0x0000000000000000000000000000006231d049ec3683c06ec6b00348e0669c61","0x0000000000000000000000000000000000237bfd7ec06c28f22ce84db9bb17ed","0x0000000000000000000000000000008afa7fa0842467bded20491950c3c1cde0","0x00000000000000000000000000000000000194ab5c71154605b8483cb40d00b8","0x00000000000000000000000000000066709af193591e93e8be3b833f63cb8597","0x000000000000000000000000000000000008ab9091bb9225b00ca0c011dff12f"] -public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] -verification_key = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84","0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae","0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16","0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1","0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c","0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7","0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8","0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c","0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5","0x00000000000000000000000000000000002002681bb417184b2df070a16a3858","0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511","0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223","0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7","0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c","0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130","0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f","0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3","0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592","0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3","0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1","0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0","0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c","0x0000000000000000000000000000009f825dde88092070747180d581c342444a","0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01","0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff","0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9","0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1","0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b","0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2","0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f","0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0","0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349","0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8","0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2","0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556","0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d","0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb","0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d","0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8","0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862","0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e","0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830","0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f","0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe","0x000000000000000000000000000000231147211b3c75e1f47d150e4bbd2fb22e","0x00000000000000000000000000000000000d19ee104a10d3c701cfd87473cbbe","0x0000000000000000000000000000006705f3f382637d00f698e2c5c94ed05ae9","0x00000000000000000000000000000000000b9c792da28bb60601dd7ce4b74e68","0x000000000000000000000000000000ac5acc8cc21e4ddb225c510670f80c80b3","0x00000000000000000000000000000000002da9d3fa57343e6998aba19429b9fa","0x0000000000000000000000000000004bacbf54b7c17a560df0af18b6d0d527be","0x00000000000000000000000000000000000faea33aeca2025b22c288964b21eb","0x000000000000000000000000000000492e756298d68d6e95de096055cc0336c3","0x00000000000000000000000000000000001a12a12f004859e5a3675c7315121b","0x000000000000000000000000000000893d521d512f30e6d32afbbc0cecd8ee00","0x00000000000000000000000000000000001674b3c1ef12c6da690631e0d86c04","0x000000000000000000000000000000aa6cb02a52e7a613873d4ac9b411349945","0x00000000000000000000000000000000001ecb1fe9c493add46751f9940f73e1","0x00000000000000000000000000000045b3d362ca82cba69fb2b9c733a5b8c351","0x000000000000000000000000000000000019a683586af466e331945b732d2f8c","0x000000000000000000000000000000fc79b052dfdfe67c0ecfc06b4267ffd694","0x00000000000000000000000000000000001336a70c396393038d5e9913744ac2","0x0000000000000000000000000000005450d29af1e9438e91cd33ddeb2548226e","0x000000000000000000000000000000000000993a602891cfd0e6f6ecf7404933","0x000000000000000000000000000000498efddab90a32e9b2db729ed6e9b40192","0x00000000000000000000000000000000002425efebe9628c63ca6fc28bdb5901","0x000000000000000000000000000000d8488157f875a21ab5f93f1c2b641f3de9","0x0000000000000000000000000000000000290f95ada3936604dc4b14df7504e3","0x0000000000000000000000000000005d6902187f3ed60dcce06fca211b40329a","0x00000000000000000000000000000000002b5870a6ba0b20aaa0178e5adfbc36","0x000000000000000000000000000000e5c2519171fa0e548fc3c4966ffc1ce570","0x00000000000000000000000000000000001cb8d8f4793b7debbdc429389dbf2d","0x000000000000000000000000000000a3ee22dd60456277b86c32a18982dcb185","0x00000000000000000000000000000000002493c99a3d068b03f8f2b8d28b57ce","0x000000000000000000000000000000f6c3731486320082c20ec71bbdc92196c1","0x00000000000000000000000000000000001ded39c4c8366469843cd63f09ecac","0x000000000000000000000000000000494997477ab161763e46601d95844837ef","0x00000000000000000000000000000000002e0cddbc5712d79b59cb3b41ebbcdd","0x000000000000000000000000000000426db4c64531d350750df62dbbc41a1bd9","0x0000000000000000000000000000000000303126892f664d8d505964d14315ec","0x00000000000000000000000000000076a6b2c6040c0c62bd59acfe3e3e125672","0x000000000000000000000000000000000000874a5ad262eecc6b565e0b085074","0x000000000000000000000000000000ef082fb517183c9c6841c2b8ef2ca1df04","0x0000000000000000000000000000000000127b2a745a1b74968c3edc18982b9b","0x000000000000000000000000000000c9efd4f8c3d56e1eb23d789a8f710d5be6","0x000000000000000000000000000000000015a18748490ff4c2b1871081954e86","0x000000000000000000000000000000a0011ef987dc016ab110eacd554a1d8bbf","0x00000000000000000000000000000000002097c84955059442a95df075833071","0x000000000000000000000000000000d38e9426ad3085b68b00a93c17897c2877","0x00000000000000000000000000000000002aecd48089890ea0798eb952c66824","0x00000000000000000000000000000078d8a9ce405ce559f441f2e71477ff3ddb","0x00000000000000000000000000000000001216bdb2f0d961bb8a7a23331d2150","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb","0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56","0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc","0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4"] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr deleted file mode 100644 index 961fff7b8a75..000000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr +++ /dev/null @@ -1,19 +0,0 @@ -// This circuit aggregates a single Honk proof from `assert_statement_recursive`. -fn main( - verification_key: [Field; 103], - // This is the proof without public inputs attached. - // - // This means: the size of this does not change with the number of public inputs. - proof: [Field; 153], - public_inputs: pub [Field; 1], - // This is currently not public. It is fine given that the vk is a part of the circuit definition. - // I believe we want to eventually make it public too though. - key_hash: Field -) { - std::verify_proof( - verification_key.as_slice(), - proof.as_slice(), - public_inputs.as_slice(), - key_hash - ); -} diff --git a/noir/noir-repo/tooling/acvm_cli/Cargo.toml b/noir/noir-repo/tooling/acvm_cli/Cargo.toml index a592f2d65f3f..1cfd1f3b2705 100644 --- a/noir/noir-repo/tooling/acvm_cli/Cargo.toml +++ b/noir/noir-repo/tooling/acvm_cli/Cargo.toml @@ -20,7 +20,7 @@ path = "src/main.rs" [dependencies] thiserror.workspace = true toml.workspace = true -color-eyre.workspace = true +color-eyre = "0.6.2" clap.workspace = true acvm.workspace = true nargo.workspace = true diff --git a/noir/noir-repo/tooling/lsp/src/solver.rs b/noir/noir-repo/tooling/lsp/src/solver.rs index 3c2d7499880f..9d1185e3a799 100644 --- a/noir/noir-repo/tooling/lsp/src/solver.rs +++ b/noir/noir-repo/tooling/lsp/src/solver.rs @@ -16,6 +16,22 @@ impl BlackBoxFunctionSolver for WrapperSolver { self.0.schnorr_verify(public_key_x, public_key_y, signature, message) } + fn pedersen_commitment( + &self, + inputs: &[acvm::FieldElement], + domain_separator: u32, + ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { + self.0.pedersen_commitment(inputs, domain_separator) + } + + fn pedersen_hash( + &self, + inputs: &[acvm::FieldElement], + domain_separator: u32, + ) -> Result { + self.0.pedersen_hash(inputs, domain_separator) + } + fn multi_scalar_mul( &self, points: &[acvm::FieldElement], diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index e0e54449a6f0..b9d7d7e3e487 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -43,16 +43,11 @@ prettytable-rs = "0.10" rayon = "1.8.0" thiserror.workspace = true tower.workspace = true -async-lsp = { workspace = true, features = [ - "client-monitor", - "stdio", - "tracing", - "tokio", -] } +async-lsp = { workspace = true, features = ["client-monitor", "stdio", "tracing", "tokio"] } const_format.workspace = true similar-asserts.workspace = true termcolor = "1.1.2" -color-eyre.workspace = true +color-eyre = "0.6.2" tokio = { version = "1.0", features = ["io-std", "rt"] } dap.workspace = true clap-markdown = { git = "https://github.com/noir-lang/clap-markdown", rev = "450d759532c88f0dba70891ceecdbc9ff8f25d2b", optional = true } diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs index 5badd76605a5..7fd396d69613 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs @@ -1,7 +1,6 @@ use std::io::Write; use std::{collections::BTreeMap, path::PathBuf}; -use acvm::blackbox_solver::StubbedBlackBoxSolver; use fm::FileManager; use noirc_driver::{check_crate, compile_no_check, file_manager_with_stdlib, CompileOptions}; use noirc_frontend::hir::FunctionNameMatch; @@ -56,7 +55,7 @@ fn run_stdlib_tests() { let status = if test_function_has_no_arguments { run_test( - &StubbedBlackBoxSolver, + &bn254_blackbox_solver::Bn254BlackBoxSolver, &mut context, &test_function, false, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs index ec8ac4abec79..015644c15cb2 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs @@ -168,9 +168,7 @@ pub(crate) fn rewrite( format!("{path_string}{turbofish}") } ExpressionKind::Lambda(_) => visitor.slice(span).to_string(), - ExpressionKind::Quote(block, block_span) => { - format!("quote {}", rewrite_block(visitor, block, block_span)) - } + ExpressionKind::Quote(_) => visitor.slice(span).to_string(), ExpressionKind::Comptime(block, block_span) => { format!("comptime {}", rewrite_block(visitor, block, block_span)) } @@ -185,7 +183,6 @@ pub(crate) fn rewrite( format!("$({})", rewrite_sub_expr(visitor, shape, *expr)) } } - ExpressionKind::UnquoteMarker(_) => unreachable!("UnquoteMarker in runtime code"), } } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs index a5d042dc71e6..3cfee4f46ad8 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs @@ -44,7 +44,7 @@ impl super::FmtVisitor<'_> { if !func.def.generics.is_empty() { let full_span = name_span.end()..params_open; - let start = name_span.end(); + let start = self.span_before(full_span.clone(), Token::Less).start(); let end = self.span_after(full_span, Token::Greater).start(); let generics = func.def.generics; diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr index 3d231cd3f7fb..961e67faf1cf 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/fn.nr @@ -61,3 +61,7 @@ fn main( ) {} pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} + +fn whitespace_before_generics(foo: T) {} + +fn more_whitespace_before_generics(foo: T) {} diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr b/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr index 1c6d201fa391..03806b0fef9c 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/input/fn.nr @@ -44,3 +44,8 @@ fn main( ) {} pub fn from_baz(x: [Field; crate::foo::MAGIC_NUMBER]) {} + +fn whitespace_before_generics < T > (foo: T) {} + +fn more_whitespace_before_generics < +T > (foo: T) {} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 3ce97f89ec2c..2bb142c9fa00 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -41,7 +41,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "portal:../../../../barretenberg/ts", + "@aztec/bb.js": "0.41.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts index ce2c27124912..d07681dd8c11 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts @@ -45,10 +45,7 @@ export class BarretenbergVerifierBackend implements VerifierBackend { const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); const api = await Barretenberg.new(this.options); - const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes( - this.acirUncompressedBytecode, - /*honkRecursion=*/ false, // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): Remove this flag - ); + const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); const crs = await Crs.new(subgroupSize + 1); await api.commonInitSlabAllocator(subgroupSize); await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); diff --git a/noir/noir-repo/tooling/noirc_abi/src/lib.rs b/noir/noir-repo/tooling/noirc_abi/src/lib.rs index c3e1ade04fad..b3d80099137b 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/lib.rs @@ -171,7 +171,7 @@ pub struct AbiReturnType { pub visibility: AbiVisibility, } -#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(arbitrary::Arbitrary))] pub struct Abi { /// An ordered list of the arguments to the program's `main` function, specifying their types and visibility. diff --git a/noir/noir-repo/tooling/profiler/Cargo.toml b/noir/noir-repo/tooling/profiler/Cargo.toml deleted file mode 100644 index d33e99f1a4ce..000000000000 --- a/noir/noir-repo/tooling/profiler/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "noir_profiler" -description = "Profiler for noir circuits" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -rust-version.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[[bin]] -name = "noir-profiler" -path = "src/main.rs" - -[dependencies] -color-eyre.workspace = true -clap.workspace = true -noirc_artifacts.workspace = true -nargo.workspace = true -const_format.workspace = true -serde.workspace = true -serde_json.workspace = true -fm.workspace = true -codespan-reporting.workspace = true -inferno = "0.11.19" -im.workspace = true -acir.workspace = true -noirc_errors.workspace = true - -# Logs -tracing-subscriber.workspace = true -tracing-appender = "0.2.3" - -[dev-dependencies] -noirc_abi.workspace = true -noirc_driver.workspace = true -tempfile.workspace = true - -[features] -default = ["bn254"] -bn254 = ["acir/bn254"] diff --git a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs b/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs deleted file mode 100644 index 4f9d64fa1b3a..000000000000 --- a/noir/noir-repo/tooling/profiler/src/cli/gates_flamegraph_cmd.rs +++ /dev/null @@ -1,485 +0,0 @@ -use std::collections::BTreeMap; -use std::io::BufWriter; -use std::path::{Path, PathBuf}; -use std::process::Command; - -use clap::Args; -use codespan_reporting::files::Files; -use color_eyre::eyre::{self, Context}; -use inferno::flamegraph::{from_lines, Options}; -use serde::{Deserialize, Serialize}; - -use acir::circuit::OpcodeLocation; -use nargo::errors::Location; -use noirc_artifacts::{debug::DebugArtifact, program::ProgramArtifact}; -use noirc_errors::reporter::line_and_column_from_span; - -#[derive(Debug, Clone, Args)] -pub(crate) struct GatesFlamegraphCommand { - /// The path to the artifact JSON file - #[clap(long, short)] - artifact_path: String, - - /// Path to the noir backend binary - #[clap(long, short)] - backend_path: String, - - /// The output folder for the flamegraph svg files - #[clap(long, short)] - output: String, -} - -trait GatesProvider { - fn get_gates(&self, artifact_path: &Path) -> eyre::Result; -} - -struct BackendGatesProvider { - backend_path: PathBuf, -} - -impl GatesProvider for BackendGatesProvider { - fn get_gates(&self, artifact_path: &Path) -> eyre::Result { - let backend_gates_response = - Command::new(&self.backend_path).arg("gates").arg("-b").arg(artifact_path).output()?; - - // Parse the backend gates command stdout as json - let backend_gates_response: BackendGatesResponse = - serde_json::from_slice(&backend_gates_response.stdout)?; - Ok(backend_gates_response) - } -} - -trait FlamegraphGenerator { - fn generate_flamegraph<'lines, I: IntoIterator>( - &self, - folded_lines: I, - artifact_name: &str, - function_name: &str, - output_path: &Path, - ) -> eyre::Result<()>; -} - -struct InfernoFlamegraphGenerator {} - -impl FlamegraphGenerator for InfernoFlamegraphGenerator { - fn generate_flamegraph<'lines, I: IntoIterator>( - &self, - folded_lines: I, - artifact_name: &str, - function_name: &str, - output_path: &Path, - ) -> eyre::Result<()> { - let flamegraph_file = std::fs::File::create(output_path)?; - let flamegraph_writer = BufWriter::new(flamegraph_file); - - let mut options = Options::default(); - options.hash = true; - options.deterministic = true; - options.title = format!("{}-{}", artifact_name, function_name); - options.subtitle = Some("Sample = Gate".to_string()); - options.frame_height = 24; - options.color_diffusion = true; - - from_lines(&mut options, folded_lines, flamegraph_writer)?; - - Ok(()) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct BackendGatesReport { - acir_opcodes: usize, - circuit_size: usize, - gates_per_opcode: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -struct BackendGatesResponse { - functions: Vec, -} - -struct FoldedStackItem { - total_gates: usize, - nested_items: BTreeMap, -} - -pub(crate) fn run(args: GatesFlamegraphCommand) -> eyre::Result<()> { - run_with_provider( - &PathBuf::from(args.artifact_path), - &BackendGatesProvider { backend_path: PathBuf::from(args.backend_path) }, - &InfernoFlamegraphGenerator {}, - &PathBuf::from(args.output), - ) -} - -fn run_with_provider( - artifact_path: &Path, - gates_provider: &Provider, - flamegraph_generator: &Generator, - output_path: &Path, -) -> eyre::Result<()> { - let program = - read_program_from_file(artifact_path).context("Error reading program from file")?; - - let backend_gates_response = - gates_provider.get_gates(artifact_path).context("Error querying backend for gates")?; - - let function_names = program.names.clone(); - - let debug_artifact: DebugArtifact = program.into(); - - for (func_idx, (func_gates, func_name)) in - backend_gates_response.functions.into_iter().zip(function_names).enumerate() - { - println!( - "Opcode count: {}, Total gates by opcodes: {}, Circuit size: {}", - func_gates.acir_opcodes, - func_gates.gates_per_opcode.iter().sum::(), - func_gates.circuit_size - ); - - // Create a nested hashmap with the stack items, folding the gates for all the callsites that are equal - let mut folded_stack_items = BTreeMap::new(); - - func_gates.gates_per_opcode.into_iter().enumerate().for_each(|(opcode_index, gates)| { - let call_stack = &debug_artifact.debug_symbols[func_idx] - .locations - .get(&OpcodeLocation::Acir(opcode_index)); - let location_names = if let Some(call_stack) = call_stack { - call_stack - .iter() - .map(|location| location_to_callsite_label(*location, &debug_artifact)) - .collect::>() - } else { - vec!["unknown".to_string()] - }; - - add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, gates); - }); - let folded_lines = to_folded_sorted_lines(&folded_stack_items, Default::default()); - - flamegraph_generator.generate_flamegraph( - folded_lines.iter().map(|as_string| as_string.as_str()), - artifact_path.to_str().unwrap(), - &func_name, - &Path::new(&output_path).join(Path::new(&format!("{}.svg", &func_name))), - )?; - } - - Ok(()) -} - -pub(crate) fn read_program_from_file>( - circuit_path: P, -) -> eyre::Result { - let file_path = circuit_path.as_ref().with_extension("json"); - - let input_string = std::fs::read(file_path)?; - let program = serde_json::from_slice(&input_string)?; - - Ok(program) -} - -fn location_to_callsite_label<'files>( - location: Location, - files: &'files impl Files<'files, FileId = fm::FileId>, -) -> String { - let filename = - Path::new(&files.name(location.file).expect("should have a file path").to_string()) - .file_name() - .map(|os_str| os_str.to_string_lossy().to_string()) - .unwrap_or("invalid_path".to_string()); - let source = files.source(location.file).expect("should have a file source"); - - let code_slice = source - .as_ref() - .chars() - .skip(location.span.start() as usize) - .take(location.span.end() as usize - location.span.start() as usize) - .collect::(); - - // ";" is used for frame separation, and is not allowed by inferno - // Check code slice for ";" and replace it with 'GREEK QUESTION MARK' (U+037E) - let code_slice = code_slice.replace(';', "\u{037E}"); - - let (line, column) = line_and_column_from_span(source.as_ref(), &location.span); - - format!("{}:{}:{}::{}", filename, line, column, code_slice) -} - -fn add_locations_to_folded_stack_items( - stack_items: &mut BTreeMap, - locations: Vec, - gates: usize, -) { - let mut child_map = stack_items; - for (index, location) in locations.iter().enumerate() { - let current_item = child_map - .entry(location.clone()) - .or_insert(FoldedStackItem { total_gates: 0, nested_items: BTreeMap::new() }); - - child_map = &mut current_item.nested_items; - - if index == locations.len() - 1 { - current_item.total_gates += gates; - } - } -} - -/// Creates a vector of lines in the format that inferno expects from a nested hashmap of stack items -/// The lines have to be sorted in the following way, exploring the graph in a depth-first manner: -/// main 100 -/// main::foo 0 -/// main::foo::bar 200 -/// main::baz 27 -/// main::baz::qux 800 -fn to_folded_sorted_lines( - folded_stack_items: &BTreeMap, - parent_stacks: im::Vector, -) -> Vec { - folded_stack_items - .iter() - .flat_map(move |(location, folded_stack_item)| { - let frame_list: Vec = - parent_stacks.iter().cloned().chain(std::iter::once(location.clone())).collect(); - let line: String = - format!("{} {}", frame_list.join(";"), folded_stack_item.total_gates); - - let mut new_parent_stacks = parent_stacks.clone(); - new_parent_stacks.push_back(location.clone()); - - let child_lines: Vec = - to_folded_sorted_lines(&folded_stack_item.nested_items, new_parent_stacks); - - std::iter::once(line).chain(child_lines) - }) - .collect() -} - -#[cfg(test)] -mod tests { - use acir::circuit::{OpcodeLocation, Program}; - use color_eyre::eyre::{self}; - use fm::{FileId, FileManager}; - use noirc_artifacts::program::ProgramArtifact; - use noirc_driver::DebugFile; - use noirc_errors::{ - debug_info::{DebugInfo, ProgramDebugInfo}, - Location, Span, - }; - use std::{ - cell::RefCell, - collections::{BTreeMap, HashMap}, - path::{Path, PathBuf}, - }; - use tempfile::TempDir; - - use super::{BackendGatesReport, BackendGatesResponse, GatesProvider}; - - struct TestGateProvider { - mock_responses: HashMap, - } - - impl GatesProvider for TestGateProvider { - fn get_gates(&self, artifact_path: &std::path::Path) -> eyre::Result { - let response = self - .mock_responses - .get(artifact_path) - .expect("should have a mock response for the artifact path"); - - Ok(response.clone()) - } - } - - #[derive(Default)] - struct TestFlamegraphGenerator { - lines_received: RefCell>>, - } - - impl super::FlamegraphGenerator for TestFlamegraphGenerator { - fn generate_flamegraph<'lines, I: IntoIterator>( - &self, - folded_lines: I, - _artifact_name: &str, - _function_name: &str, - _output_path: &std::path::Path, - ) -> eyre::Result<()> { - let lines = folded_lines.into_iter().map(|line| line.to_string()).collect(); - self.lines_received.borrow_mut().push(lines); - Ok(()) - } - } - - fn find_spans_for(source: &str, needle: &str) -> Vec { - let mut spans = Vec::new(); - let mut start = 0; - while let Some(start_idx) = source[start..].find(needle) { - let start_idx = start + start_idx; - let end_idx = start_idx + needle.len(); - spans.push(Span::inclusive(start_idx as u32, end_idx as u32 - 1)); - start = end_idx; - } - spans - } - - struct TestCase { - expected_folded_sorted_lines: Vec>, - debug_symbols: ProgramDebugInfo, - file_map: BTreeMap, - gates_report: BackendGatesResponse, - } - - fn simple_test_case(temp_dir: &TempDir) -> TestCase { - let source_code = r##" - fn main() { - foo(); - bar(); - whatever(); - } - fn foo() { - baz(); - } - fn bar () { - whatever() - } - fn baz () { - whatever() - } - "##; - - let source_file_name = Path::new("main.nr"); - let mut fm = FileManager::new(temp_dir.path()); - let file_id = fm.add_file_with_source(source_file_name, source_code.to_string()).unwrap(); - - let main_declaration_location = - Location::new(find_spans_for(source_code, "fn main()")[0], file_id); - let main_foo_call_location = - Location::new(find_spans_for(source_code, "foo()")[0], file_id); - let main_bar_call_location = - Location::new(find_spans_for(source_code, "bar()")[0], file_id); - let main_whatever_call_location = - Location::new(find_spans_for(source_code, "whatever()")[0], file_id); - let foo_baz_call_location = Location::new(find_spans_for(source_code, "baz()")[0], file_id); - let bar_whatever_call_location = - Location::new(find_spans_for(source_code, "whatever()")[1], file_id); - let baz_whatever_call_location = - Location::new(find_spans_for(source_code, "whatever()")[2], file_id); - - let mut opcode_locations = BTreeMap::>::new(); - // main::foo::baz::whatever - opcode_locations.insert( - OpcodeLocation::Acir(0), - vec![ - main_declaration_location, - main_foo_call_location, - foo_baz_call_location, - baz_whatever_call_location, - ], - ); - - // main::bar::whatever - opcode_locations.insert( - OpcodeLocation::Acir(1), - vec![main_declaration_location, main_bar_call_location, bar_whatever_call_location], - ); - // main::whatever - opcode_locations.insert( - OpcodeLocation::Acir(2), - vec![main_declaration_location, main_whatever_call_location], - ); - - let file_map = BTreeMap::from_iter(vec![( - file_id, - DebugFile { source: source_code.to_string(), path: source_file_name.to_path_buf() }, - )]); - - let debug_symbols = ProgramDebugInfo { - debug_infos: vec![DebugInfo::new( - opcode_locations, - BTreeMap::default(), - BTreeMap::default(), - BTreeMap::default(), - )], - }; - - let backend_gates_response = BackendGatesResponse { - functions: vec![BackendGatesReport { - acir_opcodes: 3, - circuit_size: 100, - gates_per_opcode: vec![10, 20, 30], - }], - }; - - let expected_folded_sorted_lines = vec![ - "main.nr:2:9::fn main() 0".to_string(), - "main.nr:2:9::fn main();main.nr:3:13::foo() 0".to_string(), - "main.nr:2:9::fn main();main.nr:3:13::foo();main.nr:8:13::baz() 0".to_string(), - "main.nr:2:9::fn main();main.nr:3:13::foo();main.nr:8:13::baz();main.nr:14:13::whatever() 10".to_string(), - "main.nr:2:9::fn main();main.nr:4:13::bar() 0".to_string(), - "main.nr:2:9::fn main();main.nr:4:13::bar();main.nr:11:13::whatever() 20".to_string(), - "main.nr:2:9::fn main();main.nr:5:13::whatever() 30".to_string(), - ]; - - TestCase { - expected_folded_sorted_lines: vec![expected_folded_sorted_lines], - debug_symbols, - file_map, - gates_report: backend_gates_response, - } - } - - #[test] - fn test_flamegraph() { - let temp_dir = tempfile::tempdir().unwrap(); - - let test_cases = vec![simple_test_case(&temp_dir)]; - let artifact_names: Vec<_> = - test_cases.iter().enumerate().map(|(idx, _)| format!("test{}.json", idx)).collect(); - - let test_cases_with_names: Vec<_> = test_cases.into_iter().zip(artifact_names).collect(); - - let mut mock_responses: HashMap = HashMap::new(); - // Collect mock responses - for (test_case, artifact_name) in test_cases_with_names.iter() { - mock_responses.insert( - temp_dir.path().join(artifact_name.clone()), - test_case.gates_report.clone(), - ); - } - - let provider = TestGateProvider { mock_responses }; - - for (test_case, artifact_name) in test_cases_with_names.iter() { - let artifact_path = temp_dir.path().join(artifact_name.clone()); - - let artifact = ProgramArtifact { - noir_version: "0.0.0".to_string(), - hash: 27, - abi: noirc_abi::Abi::default(), - bytecode: Program::default(), - debug_symbols: test_case.debug_symbols.clone(), - file_map: test_case.file_map.clone(), - names: vec!["main".to_string()], - }; - - // Write the artifact to a file - let artifact_file = std::fs::File::create(&artifact_path).unwrap(); - serde_json::to_writer(artifact_file, &artifact).unwrap(); - - let flamegraph_generator = TestFlamegraphGenerator::default(); - - super::run_with_provider( - &artifact_path, - &provider, - &flamegraph_generator, - temp_dir.path(), - ) - .expect("should run without errors"); - - // Check that the flamegraph generator was called with the correct folded sorted lines - let calls_received = flamegraph_generator.lines_received.borrow().clone(); - - assert_eq!(calls_received, test_case.expected_folded_sorted_lines); - } - } -} diff --git a/noir/noir-repo/tooling/profiler/src/cli/mod.rs b/noir/noir-repo/tooling/profiler/src/cli/mod.rs deleted file mode 100644 index d54a3f6167c8..000000000000 --- a/noir/noir-repo/tooling/profiler/src/cli/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -use clap::{Parser, Subcommand}; -use color_eyre::eyre; -use const_format::formatcp; - -mod gates_flamegraph_cmd; - -const PROFILER_VERSION: &str = env!("CARGO_PKG_VERSION"); - -static VERSION_STRING: &str = formatcp!("version = {}\n", PROFILER_VERSION,); - -#[derive(Parser, Debug)] -#[command(name="Noir profiler", author, version=VERSION_STRING, about, long_about = None)] -struct ProfilerCli { - #[command(subcommand)] - command: GatesFlamegraphCommand, -} - -#[non_exhaustive] -#[derive(Subcommand, Clone, Debug)] -enum GatesFlamegraphCommand { - GatesFlamegraph(gates_flamegraph_cmd::GatesFlamegraphCommand), -} - -pub(crate) fn start_cli() -> eyre::Result<()> { - let ProfilerCli { command } = ProfilerCli::parse(); - - match command { - GatesFlamegraphCommand::GatesFlamegraph(args) => gates_flamegraph_cmd::run(args), - } - .map_err(|err| eyre::eyre!("{}", err))?; - - Ok(()) -} diff --git a/noir/noir-repo/tooling/profiler/src/main.rs b/noir/noir-repo/tooling/profiler/src/main.rs deleted file mode 100644 index 8e08644de23d..000000000000 --- a/noir/noir-repo/tooling/profiler/src/main.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![forbid(unsafe_code)] -#![warn(unreachable_pub)] -#![warn(clippy::semicolon_if_nothing_returned)] -#![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] - -mod cli; - -use std::env; - -use tracing_appender::rolling; -use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; - -fn main() { - // Setup tracing - if let Ok(log_dir) = env::var("PROFILER_LOG_DIR") { - let debug_file = rolling::daily(log_dir, "profiler-log"); - tracing_subscriber::fmt() - .with_span_events(FmtSpan::ACTIVE) - .with_writer(debug_file) - .with_ansi(false) - .with_env_filter(EnvFilter::from_default_env()) - .init(); - } else { - tracing_subscriber::fmt() - .with_span_events(FmtSpan::ACTIVE) - .with_ansi(true) - .with_env_filter(EnvFilter::from_env("NOIR_LOG")) - .init(); - } - - if let Err(report) = cli::start_cli() { - eprintln!("{report}"); - std::process::exit(1); - } -} diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index 181b6b3b206f..a947ec56aba0 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,18 +221,19 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" +"@aztec/bb.js@npm:0.41.0": + version: 0.41.0 + resolution: "@aztec/bb.js@npm:0.41.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: ./dest/node/main.js + bb.js: dest/node/main.js + checksum: e5e0095eaff3de45726366726337b131bb6ff7cf2cb53be705572c7d6715dae4c948bf86a03cfad68bc98c0c2d83e64cbe3723cc72260c8dbfa262af8cb81f9b languageName: node - linkType: soft + linkType: hard "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4395,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.41.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3