From 0f72560cca0531cb1ee8f4e9441f5b271e22cebc Mon Sep 17 00:00:00 2001 From: AztecBot Date: Wed, 10 Jul 2024 11:34:38 +0000 Subject: [PATCH 1/7] chore: apply sync fixes --- .aztec-sync-commit | 2 +- .github/workflows/mirror-external_libs.yml | 8 + acvm-repo/acir/codegen/acir.cpp | 151 ++++++++++++++- acvm-repo/acir/src/circuit/mod.rs | 29 ++- acvm-repo/acir/src/circuit/opcodes.rs | 4 +- .../opcodes/black_box_function_call.rs | 180 ++++++++++-------- acvm-repo/acir/src/lib.rs | 6 +- .../acir/tests/test_program_serialization.rs | 69 ++++--- .../compiler/optimizers/redundant_range.rs | 28 +-- acvm-repo/acvm/src/pwg/blackbox/aes128.rs | 6 +- acvm-repo/acvm/src/pwg/blackbox/bigint.rs | 6 +- .../src/pwg/blackbox/embedded_curve_ops.rs | 42 ++-- acvm-repo/acvm/src/pwg/blackbox/hash.rs | 32 ++-- acvm-repo/acvm/src/pwg/blackbox/logic.rs | 34 ++-- acvm-repo/acvm/src/pwg/blackbox/mod.rs | 30 +-- acvm-repo/acvm/src/pwg/blackbox/pedersen.rs | 14 +- acvm-repo/acvm/src/pwg/blackbox/range.rs | 8 +- .../acvm/src/pwg/blackbox/signature/ecdsa.rs | 16 +- .../src/pwg/blackbox/signature/schnorr.rs | 14 +- acvm-repo/acvm/src/pwg/blackbox/utils.rs | 10 +- acvm-repo/acvm/src/pwg/brillig.rs | 108 ++++++----- acvm-repo/acvm/src/pwg/mod.rs | 17 +- .../acvm_js/test/shared/multi_scalar_mul.ts | 6 +- .../acvm_js/test/shared/schnorr_verify.ts | 26 +-- compiler/noirc_evaluator/src/ssa.rs | 1 + .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 28 +-- .../ssa/acir_gen/acir_ir/generated_acir.rs | 4 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 144 ++++++++------ .../check_for_underconstrained_values.rs | 6 +- .../noirc_evaluator/src/ssa/checks/mod.rs | 1 + .../noirc_evaluator/src/ssa/ir/instruction.rs | 14 +- .../src/ssa/opt/constant_folding.rs | 69 ++++++- .../noirc_evaluator/src/ssa/opt/inlining.rs | 57 ++++-- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 1 - .../src/ssa/opt/remove_enable_side_effects.rs | 100 +++++++++- compiler/noirc_frontend/src/ast/statement.rs | 7 + .../src/elaborator/expressions.rs | 34 +--- compiler/noirc_frontend/src/elaborator/mod.rs | 155 +++++++-------- .../noirc_frontend/src/elaborator/patterns.rs | 62 +++++- .../noirc_frontend/src/elaborator/scope.rs | 5 +- .../src/elaborator/statements.rs | 6 +- .../noirc_frontend/src/elaborator/types.rs | 47 ++++- .../noirc_frontend/src/hir/comptime/errors.rs | 8 + .../src/hir/comptime/interpreter.rs | 2 +- .../noirc_frontend/src/hir/comptime/value.rs | 7 +- .../src/hir/def_collector/dc_crate.rs | 28 ++- .../src/hir/def_collector/dc_mod.rs | 8 +- .../src/hir/resolution/resolver.rs | 3 +- .../noirc_frontend/src/hir/type_check/mod.rs | 8 +- .../noirc_frontend/src/hir_def/function.rs | 6 +- compiler/noirc_frontend/src/locations.rs | 70 ++++--- .../src/monomorphization/mod.rs | 28 ++- compiler/noirc_frontend/src/node_interner.rs | 40 ++-- compiler/noirc_frontend/src/parser/mod.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 15 +- compiler/noirc_frontend/src/tests.rs | 126 ++++++++++++ docs/docs/noir/concepts/data_types/slices.mdx | 70 ++++++- docs/docs/noir/standard_library/traits.md | 30 +++ noir_stdlib/src/append.nr | 35 ++++ noir_stdlib/src/embedded_curve_ops.nr | 20 +- noir_stdlib/src/hash/mod.nr | 2 + noir_stdlib/src/hash/poseidon/bn254.nr | 16 ++ noir_stdlib/src/hash/poseidon2.nr | 1 + noir_stdlib/src/lib.nr | 1 + noir_stdlib/src/sha256.nr | 2 + noir_stdlib/src/sha512.nr | 2 + noir_stdlib/src/slice.nr | 29 +++ .../slice_join/Nargo.toml | 7 + .../slice_join/src/main.nr | 16 ++ .../Nargo.toml | 7 + .../src/main.nr | 14 ++ .../regression_5435/Nargo.toml | 7 + .../regression_5435/Prover.toml | 2 + .../regression_5435/src/main.nr | 18 ++ tooling/fuzzer/src/dictionary/mod.rs | 12 +- tooling/lsp/src/requests/goto_definition.rs | 5 + tooling/lsp/src/requests/references.rs | 2 +- tooling/lsp/src/requests/rename.rs | 99 +++++++--- .../test_programs/local_variable/Nargo.toml | 6 + .../test_programs/local_variable/src/main.nr | 5 + .../test_programs/rename_function/src/main.nr | 6 + .../test_programs/rename_global/Nargo.toml | 6 + .../test_programs/rename_global/src/main.nr | 22 +++ .../test_programs/rename_struct/src/main.nr | 6 + .../rename_type_alias/Nargo.toml | 6 + .../rename_type_alias/src/main.nr | 24 +++ tooling/nargo_cli/build.rs | 3 +- .../src/backend.ts | 46 +++-- tooling/profiler/src/opcode_formatter.rs | 2 +- 89 files changed, 1774 insertions(+), 693 deletions(-) create mode 100644 .github/workflows/mirror-external_libs.yml rename compiler/noirc_evaluator/src/ssa/{opt => checks}/check_for_underconstrained_values.rs (98%) create mode 100644 compiler/noirc_evaluator/src/ssa/checks/mod.rs create mode 100644 noir_stdlib/src/append.nr create mode 100644 test_programs/compile_success_empty/slice_join/Nargo.toml create mode 100644 test_programs/compile_success_empty/slice_join/src/main.nr create mode 100644 test_programs/compile_success_empty/unquote_multiple_items_from_annotation/Nargo.toml create mode 100644 test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr create mode 100644 test_programs/execution_success/regression_5435/Nargo.toml create mode 100644 test_programs/execution_success/regression_5435/Prover.toml create mode 100644 test_programs/execution_success/regression_5435/src/main.nr create mode 100644 tooling/lsp/test_programs/local_variable/Nargo.toml create mode 100644 tooling/lsp/test_programs/local_variable/src/main.nr create mode 100644 tooling/lsp/test_programs/rename_global/Nargo.toml create mode 100644 tooling/lsp/test_programs/rename_global/src/main.nr create mode 100644 tooling/lsp/test_programs/rename_type_alias/Nargo.toml create mode 100644 tooling/lsp/test_programs/rename_type_alias/src/main.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 34501cf4146..18e53f2bd3b 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -c7b1ae40593c24530723f344111459a51ad5f0e5 +e44ef7042c87d3c78a14413ad7d54e4ed642ad89 diff --git a/.github/workflows/mirror-external_libs.yml b/.github/workflows/mirror-external_libs.yml new file mode 100644 index 00000000000..e577ac0ed92 --- /dev/null +++ b/.github/workflows/mirror-external_libs.yml @@ -0,0 +1,8 @@ +name: Mirror Repositories +on: + workflow_dispatch: {} +jobs: + lint: + runs-on: ubuntu-latest + steps: + - run: echo Dummy workflow TODO \ No newline at end of file diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 47e184a6332..c1160930571 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -13,8 +13,33 @@ namespace Program { static Witness bincodeDeserialize(std::vector); }; + struct ConstantOrWitnessEnum { + + struct Constant { + std::string value; + + friend bool operator==(const Constant&, const Constant&); + std::vector bincodeSerialize() const; + static Constant bincodeDeserialize(std::vector); + }; + + struct Witness { + Program::Witness value; + + friend bool operator==(const Witness&, const Witness&); + std::vector bincodeSerialize() const; + static Witness bincodeDeserialize(std::vector); + }; + + std::variant value; + + friend bool operator==(const ConstantOrWitnessEnum&, const ConstantOrWitnessEnum&); + std::vector bincodeSerialize() const; + static ConstantOrWitnessEnum bincodeDeserialize(std::vector); + }; + struct FunctionInput { - Program::Witness witness; + Program::ConstantOrWitnessEnum input; uint32_t num_bits; friend bool operator==(const FunctionInput&, const FunctionInput&); @@ -5716,6 +5741,124 @@ Program::Circuit serde::Deserializable::deserialize(Deserializ return obj; } +namespace Program { + + inline bool operator==(const ConstantOrWitnessEnum &lhs, const ConstantOrWitnessEnum &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector ConstantOrWitnessEnum::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline ConstantOrWitnessEnum ConstantOrWitnessEnum::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Program + +template <> +template +void serde::Serializable::serialize(const Program::ConstantOrWitnessEnum &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Program::ConstantOrWitnessEnum serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + Program::ConstantOrWitnessEnum obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + +namespace Program { + + inline bool operator==(const ConstantOrWitnessEnum::Constant &lhs, const ConstantOrWitnessEnum::Constant &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector ConstantOrWitnessEnum::Constant::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline ConstantOrWitnessEnum::Constant ConstantOrWitnessEnum::Constant::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Program + +template <> +template +void serde::Serializable::serialize(const Program::ConstantOrWitnessEnum::Constant &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.value, serializer); +} + +template <> +template +Program::ConstantOrWitnessEnum::Constant serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ConstantOrWitnessEnum::Constant obj; + obj.value = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Program { + + inline bool operator==(const ConstantOrWitnessEnum::Witness &lhs, const ConstantOrWitnessEnum::Witness &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector ConstantOrWitnessEnum::Witness::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline ConstantOrWitnessEnum::Witness ConstantOrWitnessEnum::Witness::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Program + +template <> +template +void serde::Serializable::serialize(const Program::ConstantOrWitnessEnum::Witness &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.value, serializer); +} + +template <> +template +Program::ConstantOrWitnessEnum::Witness serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ConstantOrWitnessEnum::Witness obj; + obj.value = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Program { inline bool operator==(const Directive &lhs, const Directive &rhs) { @@ -6086,7 +6229,7 @@ Program::ExpressionWidth::Bounded serde::Deserializable template void serde::Serializable::serialize(const Program::FunctionInput &obj, Serializer &serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.witness, serializer); + serde::Serializable::serialize(obj.input, serializer); serde::Serializable::serialize(obj.num_bits, serializer); serializer.decrease_container_depth(); } @@ -6122,7 +6265,7 @@ template Program::FunctionInput serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); Program::FunctionInput obj; - obj.witness = serde::Deserializable::deserialize(deserializer); + obj.input = serde::Deserializable::deserialize(deserializer); obj.num_bits = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index 7f3c1890717..115ecbf7992 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -360,7 +360,7 @@ mod tests { use std::collections::BTreeSet; use super::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, }; use crate::{ @@ -372,34 +372,29 @@ mod tests { fn and_opcode() -> Opcode { Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AND { - lhs: FunctionInput { witness: Witness(1), num_bits: 4 }, - rhs: FunctionInput { witness: Witness(2), num_bits: 4 }, + lhs: FunctionInput::witness(Witness(1), 4), + rhs: FunctionInput::witness(Witness(2), 4), output: Witness(3), }) } fn range_opcode() -> Opcode { Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(1), num_bits: 8 }, + input: FunctionInput::witness(Witness(1), 8), }) } fn keccakf1600_opcode() -> Opcode { - let inputs: Box<[FunctionInput; 25]> = Box::new(std::array::from_fn(|i| FunctionInput { - witness: Witness(i as u32 + 1), - num_bits: 8, - })); + let inputs: Box<[FunctionInput; 25]> = + Box::new(std::array::from_fn(|i| FunctionInput::witness(Witness(i as u32 + 1), 8))); let outputs: Box<[Witness; 25]> = Box::new(std::array::from_fn(|i| Witness(i as u32 + 26))); Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Keccakf1600 { inputs, outputs }) } fn schnorr_verify_opcode() -> Opcode { - let public_key_x = - FunctionInput { witness: Witness(1), num_bits: FieldElement::max_num_bits() }; - let public_key_y = - FunctionInput { witness: Witness(2), num_bits: FieldElement::max_num_bits() }; - let signature: Box<[FunctionInput; 64]> = Box::new(std::array::from_fn(|i| { - FunctionInput { witness: Witness(i as u32 + 3), num_bits: 8 } - })); - let message: Vec = vec![FunctionInput { witness: Witness(67), num_bits: 8 }]; + let public_key_x = FunctionInput::witness(Witness(1), FieldElement::max_num_bits()); + let public_key_y = FunctionInput::witness(Witness(2), FieldElement::max_num_bits()); + let signature: Box<[FunctionInput; 64]> = + Box::new(std::array::from_fn(|i| FunctionInput::witness(Witness(i as u32 + 3), 8))); + let message: Vec> = vec![FunctionInput::witness(Witness(67), 8)]; let output = Witness(68); Opcode::BlackBoxFuncCall(BlackBoxFuncCall::SchnorrVerify { @@ -425,7 +420,7 @@ mod tests { }; let program = Program { functions: vec![circuit], unconstrained_functions: Vec::new() }; - fn read_write Deserialize<'a>>( + fn read_write Deserialize<'a> + AcirField>( program: Program, ) -> (Program, Program) { let bytes = Program::serialize_program(&program); diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index 984422c5e3a..d303f9fbbab 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; mod black_box_function_call; mod memory_operation; -pub use black_box_function_call::{BlackBoxFuncCall, FunctionInput}; +pub use black_box_function_call::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}; pub use memory_operation::{BlockId, MemOp}; #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -67,7 +67,7 @@ pub enum Opcode { /// /// Aztec's Barretenberg uses BN254 as the main curve and Grumpkin as the /// embedded curve. - BlackBoxFuncCall(BlackBoxFuncCall), + BlackBoxFuncCall(BlackBoxFuncCall), /// This opcode is a specialization of a Brillig opcode. Instead of having /// some generic assembly code like Brillig, a directive has a hardcoded diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index b8be81fcdef..7c560a0a346 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -4,124 +4,161 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; // Note: Some functions will not use all of the witness // So we need to supply how many bits of the witness is needed + #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct FunctionInput { - pub witness: Witness, +pub enum ConstantOrWitnessEnum { + Constant(F), + Witness(Witness), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct FunctionInput { + pub input: ConstantOrWitnessEnum, pub num_bits: u32, } +impl FunctionInput { + pub fn to_witness(&self) -> Witness { + match self.input { + ConstantOrWitnessEnum::Constant(_) => unreachable!("ICE - Expected Witness"), + ConstantOrWitnessEnum::Witness(witness) => witness, + } + } + + pub fn num_bits(&self) -> u32 { + self.num_bits + } + + pub fn witness(witness: Witness, num_bits: u32) -> FunctionInput { + FunctionInput { input: ConstantOrWitnessEnum::Witness(witness), num_bits } + } + + pub fn constant(value: F, num_bits: u32) -> FunctionInput { + FunctionInput { input: ConstantOrWitnessEnum::Constant(value), num_bits } + } +} + +impl std::fmt::Display for FunctionInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.input { + ConstantOrWitnessEnum::Constant(constant) => write!(f, "{constant}"), + ConstantOrWitnessEnum::Witness(witness) => write!(f, "{}", witness.0), + } + } +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum BlackBoxFuncCall { +pub enum BlackBoxFuncCall { AES128Encrypt { - inputs: Vec, - iv: Box<[FunctionInput; 16]>, - key: Box<[FunctionInput; 16]>, + inputs: Vec>, + iv: Box<[FunctionInput; 16]>, + key: Box<[FunctionInput; 16]>, outputs: Vec, }, AND { - lhs: FunctionInput, - rhs: FunctionInput, + lhs: FunctionInput, + rhs: FunctionInput, output: Witness, }, XOR { - lhs: FunctionInput, - rhs: FunctionInput, + lhs: FunctionInput, + rhs: FunctionInput, output: Witness, }, RANGE { - input: FunctionInput, + input: FunctionInput, }, SHA256 { - inputs: Vec, + inputs: Vec>, outputs: Box<[Witness; 32]>, }, Blake2s { - inputs: Vec, + inputs: Vec>, outputs: Box<[Witness; 32]>, }, Blake3 { - inputs: Vec, + inputs: Vec>, outputs: Box<[Witness; 32]>, }, SchnorrVerify { - public_key_x: FunctionInput, - public_key_y: FunctionInput, + public_key_x: FunctionInput, + public_key_y: FunctionInput, #[serde( serialize_with = "serialize_big_array", deserialize_with = "deserialize_big_array_into_box" )] - signature: Box<[FunctionInput; 64]>, - message: Vec, + signature: Box<[FunctionInput; 64]>, + message: Vec>, output: Witness, }, /// Will be deprecated PedersenCommitment { - inputs: Vec, + inputs: Vec>, domain_separator: u32, outputs: (Witness, Witness), }, /// Will be deprecated PedersenHash { - inputs: Vec, + inputs: Vec>, domain_separator: u32, output: Witness, }, EcdsaSecp256k1 { - public_key_x: Box<[FunctionInput; 32]>, - public_key_y: Box<[FunctionInput; 32]>, + public_key_x: Box<[FunctionInput; 32]>, + public_key_y: Box<[FunctionInput; 32]>, #[serde( serialize_with = "serialize_big_array", deserialize_with = "deserialize_big_array_into_box" )] - signature: Box<[FunctionInput; 64]>, - hashed_message: Box<[FunctionInput; 32]>, + signature: Box<[FunctionInput; 64]>, + hashed_message: Box<[FunctionInput; 32]>, output: Witness, }, EcdsaSecp256r1 { - public_key_x: Box<[FunctionInput; 32]>, - public_key_y: Box<[FunctionInput; 32]>, + public_key_x: Box<[FunctionInput; 32]>, + public_key_y: Box<[FunctionInput; 32]>, #[serde( serialize_with = "serialize_big_array", deserialize_with = "deserialize_big_array_into_box" )] - signature: Box<[FunctionInput; 64]>, - hashed_message: Box<[FunctionInput; 32]>, + signature: Box<[FunctionInput; 64]>, + hashed_message: Box<[FunctionInput; 32]>, output: Witness, }, MultiScalarMul { - points: Vec, - scalars: Vec, + points: Vec>, + scalars: Vec>, outputs: (Witness, Witness, Witness), }, EmbeddedCurveAdd { - input1: Box<[FunctionInput; 3]>, - input2: Box<[FunctionInput; 3]>, + input1: Box<[FunctionInput; 3]>, + input2: Box<[FunctionInput; 3]>, outputs: (Witness, Witness, Witness), }, Keccak256 { - inputs: Vec, + inputs: Vec>, /// This is the number of bytes to take /// from the input. Note: if `var_message_size` /// is more than the number of bytes in the input, /// then an error is returned. - var_message_size: FunctionInput, + var_message_size: FunctionInput, outputs: Box<[Witness; 32]>, }, Keccakf1600 { - inputs: Box<[FunctionInput; 25]>, + inputs: Box<[FunctionInput; 25]>, outputs: Box<[Witness; 25]>, }, RecursiveAggregation { - verification_key: Vec, - proof: Vec, + verification_key: Vec>, + proof: Vec>, /// These represent the public inputs of the proof we are verifying /// They should be checked against in the circuit after construction /// of a new aggregation state - public_inputs: Vec, + public_inputs: Vec>, /// A key hash is used to check the validity of the verification key. /// The circuit implementing this opcode can use this hash to ensure that the /// key provided to the circuit matches the key produced by the circuit creator - key_hash: FunctionInput, + key_hash: FunctionInput, }, BigIntAdd { lhs: u32, @@ -144,7 +181,7 @@ pub enum BlackBoxFuncCall { output: u32, }, BigIntFromLeBytes { - inputs: Vec, + inputs: Vec>, modulus: Vec, output: u32, }, @@ -156,7 +193,7 @@ pub enum BlackBoxFuncCall { /// outputting the permuted state. Poseidon2Permutation { /// Input state for the permutation of Poseidon2 - inputs: Vec, + inputs: Vec>, /// Permuted state outputs: Vec, /// State length (in number of field elements) @@ -172,15 +209,15 @@ pub enum BlackBoxFuncCall { /// * `outputs` - result of the input compressed into 256 bits Sha256Compression { /// 512 bits of the input message, represented by 16 u32s - inputs: Box<[FunctionInput; 16]>, + inputs: Box<[FunctionInput; 16]>, /// Vector of 8 u32s used to compress the input - hash_values: Box<[FunctionInput; 8]>, + hash_values: Box<[FunctionInput; 8]>, /// Output of the compression, represented by 8 u32s outputs: Box<[Witness; 8]>, }, } -impl BlackBoxFuncCall { +impl BlackBoxFuncCall { pub fn get_black_box_func(&self) -> BlackBoxFunc { match self { BlackBoxFuncCall::AES128Encrypt { .. } => BlackBoxFunc::AES128Encrypt, @@ -215,7 +252,7 @@ impl BlackBoxFuncCall { self.get_black_box_func().name() } - pub fn get_inputs_vec(&self) -> Vec { + pub fn get_inputs_vec(&self) -> Vec> { match self { BlackBoxFuncCall::AES128Encrypt { inputs, .. } | BlackBoxFuncCall::SHA256 { inputs, .. } @@ -240,7 +277,7 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::BigIntDiv { .. } | BlackBoxFuncCall::BigIntToLeBytes { .. } => Vec::new(), BlackBoxFuncCall::MultiScalarMul { points, scalars, .. } => { - let mut inputs: Vec = Vec::with_capacity(points.len() * 2); + let mut inputs: Vec> = Vec::with_capacity(points.len() * 2); inputs.extend(points.iter().copied()); inputs.extend(scalars.iter().copied()); inputs @@ -256,7 +293,7 @@ impl BlackBoxFuncCall { message, .. } => { - let mut inputs: Vec = + let mut inputs: Vec> = Vec::with_capacity(2 + signature.len() + message.len()); inputs.push(*public_key_x); inputs.push(*public_key_y); @@ -364,7 +401,7 @@ impl BlackBoxFuncCall { const ABBREVIATION_LIMIT: usize = 5; -fn get_inputs_string(inputs: &[FunctionInput]) -> String { +fn get_inputs_string(inputs: &[FunctionInput]) -> String { // Once a vectors length gets above this limit, // instead of listing all of their elements, we use ellipses // to abbreviate them @@ -373,7 +410,7 @@ fn get_inputs_string(inputs: &[FunctionInput]) -> String { if should_abbreviate_inputs { let mut result = String::new(); for (index, inp) in inputs.iter().enumerate() { - result += &format!("(_{}, num_bits: {})", inp.witness.witness_index(), inp.num_bits); + result += &format!("({})", inp); // Add a comma, unless it is the last entry if index != inputs.len() - 1 { result += ", "; @@ -385,14 +422,7 @@ fn get_inputs_string(inputs: &[FunctionInput]) -> String { let last = inputs.last().unwrap(); let mut result = String::new(); - - result += &format!( - "(_{}, num_bits: {})...(_{}, num_bits: {})", - first.witness.witness_index(), - first.num_bits, - last.witness.witness_index(), - last.num_bits, - ); + result += &format!("({})...({})", first, last,); result } @@ -421,7 +451,7 @@ fn get_outputs_string(outputs: &[Witness]) -> String { } } -impl std::fmt::Display for BlackBoxFuncCall { +impl std::fmt::Display for BlackBoxFuncCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BlackBoxFuncCall::PedersenCommitment { .. } => { @@ -454,13 +484,16 @@ impl std::fmt::Display for BlackBoxFuncCall { } } -impl std::fmt::Debug for BlackBoxFuncCall { +impl std::fmt::Debug for BlackBoxFuncCall { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self, f) } } -fn serialize_big_array(big_array: &[FunctionInput; 64], s: S) -> Result +fn serialize_big_array( + big_array: &[FunctionInput; 64], + s: S, +) -> Result where S: Serializer, { @@ -469,15 +502,15 @@ where (*big_array).serialize(s) } -fn deserialize_big_array_into_box<'de, D>( +fn deserialize_big_array_into_box<'de, D, F: Deserialize<'de>>( deserializer: D, -) -> Result, D::Error> +) -> Result; 64]>, D::Error> where D: Deserializer<'de>, { use serde_big_array::BigArray; - let big_array: [FunctionInput; 64] = BigArray::deserialize(deserializer)?; + let big_array: [FunctionInput; 64] = BigArray::deserialize(deserializer)?; Ok(Box::new(big_array)) } @@ -487,26 +520,21 @@ mod tests { use crate::{circuit::Opcode, native_types::Witness}; use acir_field::{AcirField, FieldElement}; - use super::{BlackBoxFuncCall, FunctionInput}; + use super::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}; fn keccakf1600_opcode() -> Opcode { - let inputs: Box<[FunctionInput; 25]> = Box::new(std::array::from_fn(|i| FunctionInput { - witness: Witness(i as u32 + 1), - num_bits: 8, - })); + let inputs: Box<[FunctionInput; 25]> = + Box::new(std::array::from_fn(|i| FunctionInput::witness(Witness(i as u32 + 1), 8))); let outputs: Box<[Witness; 25]> = Box::new(std::array::from_fn(|i| Witness(i as u32 + 26))); Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Keccakf1600 { inputs, outputs }) } fn schnorr_verify_opcode() -> Opcode { - let public_key_x = - FunctionInput { witness: Witness(1), num_bits: FieldElement::max_num_bits() }; - let public_key_y = - FunctionInput { witness: Witness(2), num_bits: FieldElement::max_num_bits() }; - let signature: Box<[FunctionInput; 64]> = Box::new(std::array::from_fn(|i| { - FunctionInput { witness: Witness(i as u32 + 3), num_bits: 8 } - })); - let message: Vec = vec![FunctionInput { witness: Witness(67), num_bits: 8 }]; + let public_key_x = FunctionInput::witness(Witness(1), FieldElement::max_num_bits()); + let public_key_y = FunctionInput::witness(Witness(2), FieldElement::max_num_bits()); + let signature: Box<[FunctionInput; 64]> = + Box::new(std::array::from_fn(|i| FunctionInput::witness(Witness(i as u32 + 3), 8))); + let message: Vec> = vec![FunctionInput::witness(Witness(67), 8)]; let output = Witness(68); Opcode::BlackBoxFuncCall(BlackBoxFuncCall::SchnorrVerify { diff --git a/acvm-repo/acir/src/lib.rs b/acvm-repo/acir/src/lib.rs index f064cfaca0e..540e0f07eb5 100644 --- a/acvm-repo/acir/src/lib.rs +++ b/acvm-repo/acir/src/lib.rs @@ -42,7 +42,7 @@ mod reflection { circuit::{ brillig::{BrilligInputs, BrilligOutputs}, directives::Directive, - opcodes::{BlackBoxFuncCall, BlockType}, + opcodes::{BlackBoxFuncCall, BlockType, ConstantOrWitnessEnum, FunctionInput}, AssertionPayload, Circuit, ExpressionOrMemory, ExpressionWidth, Opcode, OpcodeLocation, Program, }, @@ -68,7 +68,9 @@ mod reflection { tracer.trace_simple_type::>().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); - tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::>().unwrap(); + tracer.trace_simple_type::>().unwrap(); + tracer.trace_simple_type::>().unwrap(); tracer.trace_simple_type::>().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::>().unwrap(); diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 84a9aa719f2..3a42cc41d47 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -14,7 +14,7 @@ use std::collections::BTreeSet; use acir::{ circuit::{ brillig::{BrilligBytecode, BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, + opcodes::{BlackBoxFuncCall, BlockId, ConstantOrWitnessEnum, FunctionInput, MemOp}, Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, @@ -62,13 +62,13 @@ fn multi_scalar_mul_circuit() { let multi_scalar_mul: Opcode = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::MultiScalarMul { points: vec![ - FunctionInput { witness: Witness(1), num_bits: 128 }, - FunctionInput { witness: Witness(2), num_bits: 128 }, - FunctionInput { witness: Witness(3), num_bits: 1 }, + FunctionInput::witness(Witness(1), 128), + FunctionInput::witness(Witness(2), 128), + FunctionInput::witness(Witness(3), 1), ], scalars: vec![ - FunctionInput { witness: Witness(4), num_bits: 128 }, - FunctionInput { witness: Witness(5), num_bits: 128 }, + FunctionInput::witness(Witness(4), 128), + FunctionInput::witness(Witness(5), 128), ], outputs: (Witness(6), Witness(7), Witness(8)), }); @@ -91,10 +91,10 @@ fn multi_scalar_mul_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 219, 10, 0, 32, 8, 67, 243, 214, 5, 250, 232, - 62, 189, 69, 123, 176, 132, 195, 116, 50, 149, 114, 107, 0, 97, 127, 116, 2, 75, 243, 2, - 74, 53, 122, 202, 189, 211, 15, 106, 5, 13, 116, 238, 35, 221, 81, 230, 61, 249, 37, 253, - 250, 179, 79, 109, 218, 22, 67, 227, 173, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 11, 10, 0, 32, 8, 67, 43, 181, 15, 116, 232, + 142, 158, 210, 130, 149, 240, 112, 234, 212, 156, 78, 12, 39, 67, 71, 158, 142, 80, 29, 44, + 228, 66, 90, 168, 119, 189, 74, 115, 131, 174, 78, 115, 58, 124, 70, 254, 130, 59, 74, 253, + 68, 255, 255, 221, 39, 54, 221, 93, 91, 132, 193, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -102,18 +102,15 @@ fn multi_scalar_mul_circuit() { #[test] fn schnorr_verify_circuit() { - let public_key_x = - FunctionInput { witness: Witness(1), num_bits: FieldElement::max_num_bits() }; - let public_key_y = - FunctionInput { witness: Witness(2), num_bits: FieldElement::max_num_bits() }; - let signature: [FunctionInput; 64] = (3..(3 + 64)) - .map(|i| FunctionInput { witness: Witness(i), num_bits: 8 }) + let public_key_x = FunctionInput::witness(Witness(1), FieldElement::max_num_bits()); + let public_key_y = FunctionInput::witness(Witness(2), FieldElement::max_num_bits()); + let signature: [FunctionInput; 64] = (3..(3 + 64)) + .map(|i| FunctionInput::witness(Witness(i), 8)) .collect::>() .try_into() .unwrap(); - let message = ((3 + 64)..(3 + 64 + 10)) - .map(|i| FunctionInput { witness: Witness(i), num_bits: 8 }) - .collect(); + let message = + ((3 + 64)..(3 + 64 + 10)).map(|i| FunctionInput::witness(Witness(i), 8)).collect(); let output = Witness(3 + 64 + 10); let last_input = output.witness_index() - 1; @@ -137,22 +134,24 @@ fn schnorr_verify_circuit() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 210, 85, 78, 67, 81, 24, 133, 209, 226, 238, 238, - 238, 238, 238, 165, 148, 82, 102, 193, 252, 135, 64, 232, 78, 87, 147, 114, 147, 147, 5, - 47, 132, 252, 251, 107, 41, 212, 191, 159, 218, 107, 241, 115, 236, 226, 111, 237, 181, - 178, 173, 246, 186, 107, 175, 157, 29, 236, 100, 23, 27, 175, 135, 189, 236, 99, 63, 7, 56, - 200, 33, 14, 115, 132, 163, 28, 227, 56, 39, 56, 201, 41, 78, 115, 134, 179, 156, 227, 60, - 23, 184, 200, 37, 46, 115, 133, 171, 92, 227, 58, 55, 184, 201, 45, 110, 115, 135, 187, - 220, 227, 62, 15, 120, 200, 35, 30, 243, 132, 167, 60, 227, 57, 47, 120, 201, 43, 94, 243, - 134, 183, 188, 227, 61, 31, 248, 200, 39, 62, 243, 133, 175, 77, 59, 230, 123, 243, 123, - 145, 239, 44, 241, 131, 101, 126, 178, 194, 47, 86, 249, 237, 239, 86, 153, 238, 210, 92, - 122, 75, 107, 233, 44, 141, 53, 250, 234, 241, 191, 164, 167, 180, 148, 142, 210, 80, 250, - 73, 59, 233, 38, 205, 164, 151, 180, 146, 78, 210, 72, 250, 72, 27, 233, 34, 77, 164, 135, - 180, 144, 14, 210, 64, 246, 95, 46, 212, 119, 207, 230, 217, 59, 91, 103, 231, 108, 156, - 125, 183, 237, 186, 107, 207, 125, 59, 30, 218, 239, 216, 110, 167, 246, 58, 183, 211, 165, - 125, 174, 237, 114, 107, 143, 123, 59, 60, 186, 255, 179, 187, 191, 186, 115, 209, 125, 75, - 238, 90, 118, 207, 138, 59, 54, 110, 214, 184, 91, 161, 233, 158, 255, 190, 63, 71, 59, 68, - 130, 233, 3, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 211, 103, 78, 2, 81, 24, 70, 225, 193, 6, 216, 123, + 47, 216, 123, 239, 136, 136, 136, 136, 136, 187, 96, 255, 75, 32, 112, 194, 55, 201, 129, + 100, 50, 79, 244, 7, 228, 222, 243, 102, 146, 254, 167, 221, 123, 50, 97, 222, 217, 120, + 243, 116, 226, 61, 36, 15, 247, 158, 92, 120, 68, 30, 149, 199, 228, 172, 156, 147, 243, + 242, 184, 60, 33, 79, 202, 83, 242, 180, 60, 35, 207, 202, 115, 242, 188, 188, 32, 47, 202, + 75, 242, 178, 188, 34, 175, 202, 107, 242, 186, 188, 33, 111, 202, 91, 242, 182, 188, 35, + 23, 228, 93, 121, 79, 222, 151, 15, 228, 67, 249, 72, 62, 150, 79, 228, 83, 249, 76, 62, + 151, 47, 228, 75, 249, 74, 190, 150, 111, 228, 91, 249, 78, 190, 151, 31, 228, 71, 249, 73, + 126, 150, 95, 228, 87, 185, 40, 191, 201, 37, 249, 93, 46, 203, 31, 114, 69, 254, 148, 171, + 97, 58, 77, 226, 111, 95, 250, 127, 77, 254, 150, 235, 242, 143, 220, 144, 127, 229, 166, + 252, 39, 183, 194, 255, 241, 253, 45, 253, 14, 182, 201, 38, 217, 34, 27, 100, 123, 233, + 230, 242, 241, 155, 217, 20, 91, 98, 67, 108, 135, 205, 176, 21, 54, 194, 54, 216, 4, 91, + 96, 3, 180, 79, 243, 180, 78, 227, 180, 77, 211, 180, 76, 195, 180, 75, 179, 133, 164, 223, + 40, 109, 210, 36, 45, 210, 32, 237, 209, 28, 173, 209, 24, 109, 209, 20, 45, 209, 16, 237, + 208, 12, 173, 208, 8, 109, 208, 4, 45, 208, 0, 119, 207, 157, 115, 215, 220, 113, 49, 238, + 180, 20, 119, 88, 142, 59, 171, 196, 29, 85, 227, 46, 106, 113, 246, 245, 56, 235, 70, 156, + 109, 51, 206, 50, 61, 179, 244, 220, 18, 157, 231, 192, 167, 11, 75, 28, 99, 152, 25, 5, 0, + 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 7001c953d63..87a026148d7 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -1,6 +1,6 @@ use acir::{ circuit::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, Circuit, Opcode, }, native_types::Witness, @@ -74,7 +74,8 @@ impl RangeOptimizer { } Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness, num_bits }, + input: + FunctionInput { input: ConstantOrWitnessEnum::Witness(witness), num_bits }, }) => Some((*witness, *num_bits)), _ => None, @@ -105,14 +106,15 @@ impl RangeOptimizer { let mut new_order_list = Vec::with_capacity(order_list.len()); let mut optimized_opcodes = Vec::with_capacity(self.circuit.opcodes.len()); for (idx, opcode) in self.circuit.opcodes.into_iter().enumerate() { - let (witness, num_bits) = match &opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { - (input.witness, input.num_bits) - } + let (witness, num_bits) = match opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: + FunctionInput { input: ConstantOrWitnessEnum::Witness(w), num_bits: bits }, + }) => (w, bits), _ => { // If its not the range opcode, add it to the opcode // list and continue; - optimized_opcodes.push(opcode); + optimized_opcodes.push(opcode.clone()); new_order_list.push(order_list[idx]); continue; } @@ -133,7 +135,7 @@ impl RangeOptimizer { if is_lowest_bit_size { already_seen_witness.insert(witness); new_order_list.push(order_list[idx]); - optimized_opcodes.push(opcode); + optimized_opcodes.push(opcode.clone()); } } @@ -148,7 +150,7 @@ mod tests { use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ circuit::{ - opcodes::{BlackBoxFuncCall, FunctionInput}, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, Circuit, ExpressionWidth, Opcode, PublicInputs, }, native_types::{Expression, Witness}, @@ -158,7 +160,7 @@ mod tests { fn test_circuit(ranges: Vec<(Witness, u32)>) -> Circuit { fn test_range_constraint(witness: Witness, num_bits: u32) -> Opcode { Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness, num_bits }, + input: FunctionInput::witness(witness, num_bits), }) } @@ -201,7 +203,7 @@ mod tests { assert_eq!( optimized_circuit.opcodes[0], Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(1), num_bits: 16 } + input: FunctionInput::witness(Witness(1), 16) }) ); } @@ -224,13 +226,13 @@ mod tests { assert_eq!( optimized_circuit.opcodes[0], Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(1), num_bits: 16 } + input: FunctionInput::witness(Witness(1), 16) }) ); assert_eq!( optimized_circuit.opcodes[1], Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(2), num_bits: 23 } + input: FunctionInput::witness(Witness(2), 23) }) ); } diff --git a/acvm-repo/acvm/src/pwg/blackbox/aes128.rs b/acvm-repo/acvm/src/pwg/blackbox/aes128.rs index 181a78a2a6a..e3c8dc78aa6 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/aes128.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/aes128.rs @@ -11,9 +11,9 @@ use super::utils::{to_u8_array, to_u8_vec}; pub(super) fn solve_aes128_encryption_opcode( initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], - iv: &[FunctionInput; 16], - key: &[FunctionInput; 16], + inputs: &[FunctionInput], + iv: &[FunctionInput; 16], + key: &[FunctionInput; 16], outputs: &[Witness], ) -> Result<(), OpcodeResolutionError> { let scalars = to_u8_vec(initial_witness, inputs)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/bigint.rs b/acvm-repo/acvm/src/pwg/blackbox/bigint.rs index be5a4613a55..1bce4aa6c5e 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/bigint.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/bigint.rs @@ -1,9 +1,9 @@ +use crate::pwg::input_to_value; use acir::{ circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, AcirField, BlackBoxFunc, }; - use acvm_blackbox_solver::BigIntSolver; use crate::pwg::OpcodeResolutionError; @@ -20,14 +20,14 @@ pub(crate) struct AcvmBigIntSolver { impl AcvmBigIntSolver { pub(crate) fn bigint_from_bytes( &mut self, - inputs: &[FunctionInput], + inputs: &[FunctionInput], modulus: &[u8], output: u32, initial_witness: &mut WitnessMap, ) -> Result<(), OpcodeResolutionError> { let bytes = inputs .iter() - .map(|input| initial_witness.get(&input.witness).unwrap().to_u128() as u8) + .map(|input| input_to_value(initial_witness, *input).unwrap().to_u128() as u8) .collect::>(); self.bigint_solver.bigint_from_bytes(&bytes, modulus, output)?; Ok(()) diff --git a/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs b/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs index 411a6d1b737..c290faeaa4a 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/embedded_curve_ops.rs @@ -5,28 +5,28 @@ use acir::{ }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use crate::pwg::{insert_value, witness_to_value, OpcodeResolutionError}; +use crate::pwg::{input_to_value, insert_value, OpcodeResolutionError}; pub(super) fn multi_scalar_mul( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - points: &[FunctionInput], - scalars: &[FunctionInput], + points: &[FunctionInput], + scalars: &[FunctionInput], outputs: (Witness, Witness, Witness), ) -> Result<(), OpcodeResolutionError> { let points: Result, _> = - points.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); - let points: Vec<_> = points?.into_iter().cloned().collect(); + points.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + let points: Vec<_> = points?.into_iter().collect(); let scalars: Result, _> = - scalars.iter().map(|input| witness_to_value(initial_witness, input.witness)).collect(); + scalars.iter().map(|input| input_to_value(initial_witness, *input)).collect(); let mut scalars_lo = Vec::new(); let mut scalars_hi = Vec::new(); for (i, scalar) in scalars?.into_iter().enumerate() { if i % 2 == 0 { - scalars_lo.push(*scalar); + scalars_lo.push(scalar); } else { - scalars_hi.push(*scalar); + scalars_hi.push(scalar); } } // Call the backend's multi-scalar multiplication function @@ -43,18 +43,24 @@ pub(super) fn multi_scalar_mul( pub(super) fn embedded_curve_add( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - input1: [FunctionInput; 3], - input2: [FunctionInput; 3], + input1: [FunctionInput; 3], + input2: [FunctionInput; 3], outputs: (Witness, Witness, Witness), ) -> Result<(), OpcodeResolutionError> { - let input1_x = witness_to_value(initial_witness, input1[0].witness)?; - let input1_y = witness_to_value(initial_witness, input1[1].witness)?; - let input1_infinite = witness_to_value(initial_witness, input1[2].witness)?; - let input2_x = witness_to_value(initial_witness, input2[0].witness)?; - let input2_y = witness_to_value(initial_witness, input2[1].witness)?; - let input2_infinite = witness_to_value(initial_witness, input2[2].witness)?; - let (res_x, res_y, res_infinite) = - backend.ec_add(input1_x, input1_y, input1_infinite, input2_x, input2_y, input2_infinite)?; + let input1_x = input_to_value(initial_witness, input1[0])?; + let input1_y = input_to_value(initial_witness, input1[1])?; + let input1_infinite = input_to_value(initial_witness, input1[2])?; + let input2_x = input_to_value(initial_witness, input2[0])?; + let input2_y = input_to_value(initial_witness, input2[1])?; + let input2_infinite = input_to_value(initial_witness, input2[2])?; + let (res_x, res_y, res_infinite) = backend.ec_add( + &input1_x, + &input1_y, + &input1_infinite, + &input2_x, + &input2_y, + &input2_infinite, + )?; insert_value(&outputs.0, res_x, initial_witness)?; insert_value(&outputs.1, res_y, initial_witness)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/acvm-repo/acvm/src/pwg/blackbox/hash.rs index fe9bd46b091..b51139f76b7 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -5,15 +5,15 @@ use acir::{ }; use acvm_blackbox_solver::{sha256compression, BlackBoxFunctionSolver, BlackBoxResolutionError}; -use crate::pwg::{insert_value, witness_to_value}; +use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; /// Attempts to solve a 256 bit hash function opcode. /// If successful, `initial_witness` will be mutated to contain the new witness assignment. pub(super) fn solve_generic_256_hash_opcode( initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], - var_message_size: Option<&FunctionInput>, + inputs: &[FunctionInput], + var_message_size: Option<&FunctionInput>, outputs: &[Witness; 32], hash_function: fn(data: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError>, ) -> Result<(), OpcodeResolutionError> { @@ -26,16 +26,15 @@ pub(super) fn solve_generic_256_hash_opcode( /// Reads the hash function input from a [`WitnessMap`]. fn get_hash_input( initial_witness: &WitnessMap, - inputs: &[FunctionInput], - message_size: Option<&FunctionInput>, + inputs: &[FunctionInput], + message_size: Option<&FunctionInput>, ) -> Result, OpcodeResolutionError> { // Read witness assignments. let mut message_input = Vec::new(); for input in inputs.iter() { - let witness = input.witness; - let num_bits = input.num_bits as usize; + let num_bits = input.num_bits() as usize; - let witness_assignment = witness_to_value(initial_witness, witness)?; + let witness_assignment = input_to_value(initial_witness, *input)?; let bytes = witness_assignment.fetch_nearest_bytes(num_bits); message_input.extend(bytes); } @@ -43,8 +42,7 @@ fn get_hash_input( // Truncate the message if there is a `message_size` parameter given match message_size { Some(input) => { - let num_bytes_to_take = - witness_to_value(initial_witness, input.witness)?.to_u128() as usize; + let num_bytes_to_take = input_to_value(initial_witness, *input)?.to_u128() as usize; // If the number of bytes to take is more than the amount of bytes available // in the message, then we error. @@ -76,11 +74,11 @@ fn write_digest_to_outputs( fn to_u32_array( initial_witness: &WitnessMap, - inputs: &[FunctionInput; N], + inputs: &[FunctionInput; N], ) -> Result<[u32; N], OpcodeResolutionError> { let mut result = [0; N]; for (it, input) in result.iter_mut().zip(inputs) { - let witness_value = witness_to_value(initial_witness, input.witness)?; + let witness_value = input_to_value(initial_witness, *input)?; *it = witness_value.to_u128() as u32; } Ok(result) @@ -88,8 +86,8 @@ fn to_u32_array( pub(crate) fn solve_sha_256_permutation_opcode( initial_witness: &mut WitnessMap, - inputs: &[FunctionInput; 16], - hash_values: &[FunctionInput; 8], + inputs: &[FunctionInput; 16], + hash_values: &[FunctionInput; 8], outputs: &[Witness; 8], ) -> Result<(), OpcodeResolutionError> { let message = to_u32_array(initial_witness, inputs)?; @@ -107,7 +105,7 @@ pub(crate) fn solve_sha_256_permutation_opcode( pub(crate) fn solve_poseidon2_permutation_opcode( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], + inputs: &[FunctionInput], outputs: &[Witness], len: u32, ) -> Result<(), OpcodeResolutionError> { @@ -135,8 +133,8 @@ pub(crate) fn solve_poseidon2_permutation_opcode( // Read witness assignments let mut state = Vec::new(); for input in inputs.iter() { - let witness_assignment = witness_to_value(initial_witness, input.witness)?; - state.push(*witness_assignment); + let witness_assignment = input_to_value(initial_witness, *input)?; + state.push(witness_assignment); } let state = backend.poseidon2_permutation(&state, len)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/logic.rs b/acvm-repo/acvm/src/pwg/blackbox/logic.rs index 5be888c8ac6..7ce0827d932 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/logic.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/logic.rs @@ -1,4 +1,4 @@ -use crate::pwg::{insert_value, witness_to_value}; +use crate::pwg::{input_to_value, insert_value}; use crate::OpcodeResolutionError; use acir::{ circuit::opcodes::FunctionInput, @@ -11,16 +11,17 @@ use acvm_blackbox_solver::{bit_and, bit_xor}; /// the result into the supplied witness map pub(super) fn and( initial_witness: &mut WitnessMap, - lhs: &FunctionInput, - rhs: &FunctionInput, + lhs: &FunctionInput, + rhs: &FunctionInput, output: &Witness, ) -> Result<(), OpcodeResolutionError> { assert_eq!( - lhs.num_bits, rhs.num_bits, + lhs.num_bits(), + rhs.num_bits(), "number of bits specified for each input must be the same" ); - solve_logic_opcode(initial_witness, &lhs.witness, &rhs.witness, *output, |left, right| { - bit_and(left, right, lhs.num_bits) + solve_logic_opcode(initial_witness, lhs, rhs, *output, |left, right| { + bit_and(left, right, lhs.num_bits()) }) } @@ -28,30 +29,31 @@ pub(super) fn and( /// the result into the supplied witness map pub(super) fn xor( initial_witness: &mut WitnessMap, - lhs: &FunctionInput, - rhs: &FunctionInput, + lhs: &FunctionInput, + rhs: &FunctionInput, output: &Witness, ) -> Result<(), OpcodeResolutionError> { assert_eq!( - lhs.num_bits, rhs.num_bits, + lhs.num_bits(), + rhs.num_bits(), "number of bits specified for each input must be the same" ); - solve_logic_opcode(initial_witness, &lhs.witness, &rhs.witness, *output, |left, right| { - bit_xor(left, right, lhs.num_bits) + solve_logic_opcode(initial_witness, lhs, rhs, *output, |left, right| { + bit_xor(left, right, lhs.num_bits()) }) } /// Derives the rest of the witness based on the initial low level variables fn solve_logic_opcode( initial_witness: &mut WitnessMap, - a: &Witness, - b: &Witness, + a: &FunctionInput, + b: &FunctionInput, result: Witness, logic_op: impl Fn(F, F) -> F, ) -> Result<(), OpcodeResolutionError> { - let w_l_value = witness_to_value(initial_witness, *a)?; - let w_r_value = witness_to_value(initial_witness, *b)?; - let assignment = logic_op(*w_l_value, *w_r_value); + let w_l_value = input_to_value(initial_witness, *a)?; + let w_r_value = input_to_value(initial_witness, *b)?; + let assignment = logic_op(w_l_value, w_r_value); insert_value(&result, assignment, initial_witness) } diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 0c65759ebcd..def4216fe15 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -1,5 +1,5 @@ use acir::{ - circuit::opcodes::{BlackBoxFuncCall, FunctionInput}, + circuit::opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, native_types::{Witness, WitnessMap}, AcirField, }; @@ -11,7 +11,7 @@ use self::{ }; use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; -use crate::{pwg::witness_to_value, BlackBoxFunctionSolver}; +use crate::{pwg::input_to_value, BlackBoxFunctionSolver}; mod aes128; pub(crate) mod bigint; @@ -39,26 +39,33 @@ use signature::{ /// Returns the first missing assignment if any are missing fn first_missing_assignment( witness_assignments: &WitnessMap, - inputs: &[FunctionInput], + inputs: &[FunctionInput], ) -> Option { inputs.iter().find_map(|input| { - if witness_assignments.contains_key(&input.witness) { - None + if let ConstantOrWitnessEnum::Witness(witness) = input.input { + if witness_assignments.contains_key(&witness) { + None + } else { + Some(witness) + } } else { - Some(input.witness) + None } }) } /// Check if all of the inputs to the function have assignments -fn contains_all_inputs(witness_assignments: &WitnessMap, inputs: &[FunctionInput]) -> bool { - inputs.iter().all(|input| witness_assignments.contains_key(&input.witness)) +fn contains_all_inputs( + witness_assignments: &WitnessMap, + inputs: &[FunctionInput], +) -> bool { + first_missing_assignment(witness_assignments, inputs).is_none() } pub(crate) fn solve( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - bb_func: &BlackBoxFuncCall, + bb_func: &BlackBoxFuncCall, bigint_solver: &mut AcvmBigIntSolver, ) -> Result<(), OpcodeResolutionError> { let inputs = bb_func.get_inputs_vec(); @@ -99,10 +106,9 @@ pub(crate) fn solve( BlackBoxFuncCall::Keccakf1600 { inputs, outputs } => { let mut state = [0; 25]; for (it, input) in state.iter_mut().zip(inputs.as_ref()) { - let witness = input.witness; - let num_bits = input.num_bits as usize; + let num_bits = input.num_bits() as usize; assert_eq!(num_bits, 64); - let witness_assignment = witness_to_value(initial_witness, witness)?; + let witness_assignment = input_to_value(initial_witness, *input)?; let lane = witness_assignment.try_to_u64(); *it = lane.unwrap(); } diff --git a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs index f64a3a79465..b1b95393b19 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/pedersen.rs @@ -5,20 +5,20 @@ use acir::{ }; use crate::{ - pwg::{insert_value, witness_to_value, OpcodeResolutionError}, + pwg::{input_to_value, insert_value, OpcodeResolutionError}, BlackBoxFunctionSolver, }; pub(super) fn pedersen( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], + 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(); + inputs.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + let scalars: Vec<_> = scalars?.into_iter().collect(); let (res_x, res_y) = backend.pedersen_commitment(&scalars, domain_separator)?; @@ -31,13 +31,13 @@ pub(super) fn pedersen( pub(super) fn pedersen_hash( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - inputs: &[FunctionInput], + 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(); + inputs.iter().map(|input| input_to_value(initial_witness, *input)).collect(); + let scalars: Vec<_> = scalars?.into_iter().collect(); let res = backend.pedersen_hash(&scalars, domain_separator)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/range.rs b/acvm-repo/acvm/src/pwg/blackbox/range.rs index 0ca001aff7a..054730bb6c0 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -1,15 +1,15 @@ use crate::{ - pwg::{witness_to_value, ErrorLocation}, + pwg::{input_to_value, ErrorLocation}, OpcodeResolutionError, }; use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, - input: &FunctionInput, + input: &FunctionInput, ) -> Result<(), OpcodeResolutionError> { - let w_value = witness_to_value(initial_witness, input.witness)?; - if w_value.num_bits() > input.num_bits { + let w_value = input_to_value(initial_witness, *input)?; + if w_value.num_bits() > input.num_bits() { return Err(OpcodeResolutionError::UnsatisfiedConstrain { opcode_location: ErrorLocation::Unresolved, payload: None, diff --git a/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs b/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs index 707e3f26af0..db92d27b871 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/signature/ecdsa.rs @@ -15,10 +15,10 @@ use crate::{ pub(crate) fn secp256k1_prehashed( initial_witness: &mut WitnessMap, - public_key_x_inputs: &[FunctionInput; 32], - public_key_y_inputs: &[FunctionInput; 32], - signature_inputs: &[FunctionInput; 64], - hashed_message_inputs: &[FunctionInput], + public_key_x_inputs: &[FunctionInput; 32], + public_key_y_inputs: &[FunctionInput; 32], + signature_inputs: &[FunctionInput; 64], + hashed_message_inputs: &[FunctionInput], output: Witness, ) -> Result<(), OpcodeResolutionError> { let hashed_message = to_u8_vec(initial_witness, hashed_message_inputs)?; @@ -34,10 +34,10 @@ pub(crate) fn secp256k1_prehashed( pub(crate) fn secp256r1_prehashed( initial_witness: &mut WitnessMap, - public_key_x_inputs: &[FunctionInput; 32], - public_key_y_inputs: &[FunctionInput; 32], - signature_inputs: &[FunctionInput; 64], - hashed_message_inputs: &[FunctionInput], + public_key_x_inputs: &[FunctionInput; 32], + public_key_y_inputs: &[FunctionInput; 32], + signature_inputs: &[FunctionInput; 64], + hashed_message_inputs: &[FunctionInput], output: Witness, ) -> Result<(), OpcodeResolutionError> { let hashed_message = to_u8_vec(initial_witness, hashed_message_inputs)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs b/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs index 5e0ac94f8be..4f8e88373ba 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/signature/schnorr.rs @@ -1,7 +1,7 @@ use crate::{ pwg::{ blackbox::utils::{to_u8_array, to_u8_vec}, - insert_value, witness_to_value, OpcodeResolutionError, + input_to_value, insert_value, OpcodeResolutionError, }, BlackBoxFunctionSolver, }; @@ -15,14 +15,14 @@ use acir::{ pub(crate) fn schnorr_verify( backend: &impl BlackBoxFunctionSolver, initial_witness: &mut WitnessMap, - public_key_x: FunctionInput, - public_key_y: FunctionInput, - signature: &[FunctionInput; 64], - message: &[FunctionInput], + public_key_x: FunctionInput, + public_key_y: FunctionInput, + signature: &[FunctionInput; 64], + message: &[FunctionInput], output: Witness, ) -> Result<(), OpcodeResolutionError> { - let public_key_x: &F = witness_to_value(initial_witness, public_key_x.witness)?; - let public_key_y: &F = witness_to_value(initial_witness, public_key_y.witness)?; + let public_key_x: &F = &input_to_value(initial_witness, public_key_x)?; + let public_key_y: &F = &input_to_value(initial_witness, public_key_y)?; let signature = to_u8_array(initial_witness, signature)?; let message = to_u8_vec(initial_witness, message)?; diff --git a/acvm-repo/acvm/src/pwg/blackbox/utils.rs b/acvm-repo/acvm/src/pwg/blackbox/utils.rs index 6880d21a324..9b9157421e5 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/utils.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/utils.rs @@ -1,14 +1,14 @@ use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap, AcirField}; -use crate::pwg::{witness_to_value, OpcodeResolutionError}; +use crate::pwg::{input_to_value, OpcodeResolutionError}; pub(crate) fn to_u8_array( initial_witness: &WitnessMap, - inputs: &[FunctionInput; N], + inputs: &[FunctionInput; N], ) -> Result<[u8; N], OpcodeResolutionError> { let mut result = [0; N]; for (it, input) in result.iter_mut().zip(inputs) { - let witness_value_bytes = witness_to_value(initial_witness, input.witness)?.to_be_bytes(); + let witness_value_bytes = input_to_value(initial_witness, *input)?.to_be_bytes(); let byte = witness_value_bytes .last() .expect("Field element must be represented by non-zero amount of bytes"); @@ -19,11 +19,11 @@ pub(crate) fn to_u8_array( pub(crate) fn to_u8_vec( initial_witness: &WitnessMap, - inputs: &[FunctionInput], + inputs: &[FunctionInput], ) -> Result, OpcodeResolutionError> { let mut result = Vec::with_capacity(inputs.len()); for input in inputs { - let witness_value_bytes = witness_to_value(initial_witness, input.witness)?.to_be_bytes(); + let witness_value_bytes = input_to_value(initial_witness, *input)?.to_be_bytes(); let byte = witness_value_bytes .last() .expect("Field element must be represented by non-zero amount of bytes"); diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 3a639df044a..91dedac8e35 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -162,63 +162,27 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { VMStatus::Finished { .. } => Ok(BrilligSolverStatus::Finished), VMStatus::InProgress => Ok(BrilligSolverStatus::InProgress), VMStatus::Failure { reason, call_stack } => { + let call_stack = call_stack + .iter() + .map(|brillig_index| OpcodeLocation::Brillig { + acir_index: self.acir_index, + brillig_index: *brillig_index, + }) + .collect(); let payload = match reason { FailureReason::RuntimeError { message } => { Some(ResolvedAssertionPayload::String(message)) } FailureReason::Trap { revert_data_offset, revert_data_size } => { - // Since noir can only revert with strings currently, we can parse return data as a string - if revert_data_size == 0 { - None - } else { - let memory = self.vm.get_memory(); - let mut revert_values_iter = memory - [revert_data_offset..(revert_data_offset + revert_data_size)] - .iter(); - let error_selector = ErrorSelector::new( - revert_values_iter - .next() - .expect("Incorrect revert data size") - .try_into() - .expect("Error selector is not u64"), - ); - - match error_selector { - STRING_ERROR_SELECTOR => { - // If the error selector is 0, it means the error is a string - let string = revert_values_iter - .map(|memory_value| { - let as_u8: u8 = memory_value - .try_into() - .expect("String item is not u8"); - as_u8 as char - }) - .collect(); - Some(ResolvedAssertionPayload::String(string)) - } - _ => { - // If the error selector is not 0, it means the error is a custom error - Some(ResolvedAssertionPayload::Raw(RawAssertionPayload { - selector: error_selector, - data: revert_values_iter - .map(|value| value.to_field()) - .collect(), - })) - } - } - } + extract_failure_payload_from_memory( + self.vm.get_memory(), + revert_data_offset, + revert_data_size, + ) } }; - Err(OpcodeResolutionError::BrilligFunctionFailed { - payload, - call_stack: call_stack - .iter() - .map(|brillig_index| OpcodeLocation::Brillig { - acir_index: self.acir_index, - brillig_index: *brillig_index, - }) - .collect(), - }) + + Err(OpcodeResolutionError::BrilligFunctionFailed { payload, call_stack }) } VMStatus::ForeignCallWait { function, inputs } => { Ok(BrilligSolverStatus::ForeignCallWait(ForeignCallWaitInfo { function, inputs })) @@ -283,6 +247,50 @@ impl<'b, B: BlackBoxFunctionSolver, F: AcirField> BrilligSolver<'b, F, B> { } } +/// Extracts a `ResolvedAssertionPayload` from a block of memory of a Brillig VM instance. +/// +/// Returns `None` if the amount of memory requested is zero. +fn extract_failure_payload_from_memory( + memory: &[MemoryValue], + revert_data_offset: usize, + revert_data_size: usize, +) -> Option> { + // Since noir can only revert with strings currently, we can parse return data as a string + if revert_data_size == 0 { + None + } else { + let mut revert_values_iter = + memory[revert_data_offset..(revert_data_offset + revert_data_size)].iter(); + let error_selector = ErrorSelector::new( + revert_values_iter + .next() + .expect("Incorrect revert data size") + .try_into() + .expect("Error selector is not u64"), + ); + + match error_selector { + STRING_ERROR_SELECTOR => { + // If the error selector is 0, it means the error is a string + let string = revert_values_iter + .map(|memory_value| { + let as_u8: u8 = memory_value.try_into().expect("String item is not u8"); + as_u8 as char + }) + .collect(); + Some(ResolvedAssertionPayload::String(string)) + } + _ => { + // If the error selector is not 0, it means the error is a custom error + Some(ResolvedAssertionPayload::Raw(RawAssertionPayload { + selector: error_selector, + data: revert_values_iter.map(|value| value.to_field()).collect(), + })) + } + } + } +} + /// Encapsulates a request from a Brillig VM process that encounters a [foreign call opcode][acir::brillig_vm::Opcode::ForeignCall] /// where the result of the foreign call has not yet been provided. /// diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 4f88e17d109..4292d72fad5 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -5,9 +5,10 @@ use std::collections::HashMap; use acir::{ brillig::ForeignCallResult, circuit::{ - brillig::BrilligBytecode, opcodes::BlockId, AssertionPayload, ErrorSelector, - ExpressionOrMemory, Opcode, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, - STRING_ERROR_SELECTOR, + brillig::BrilligBytecode, + opcodes::{BlockId, ConstantOrWitnessEnum, FunctionInput}, + AssertionPayload, ErrorSelector, ExpressionOrMemory, Opcode, OpcodeLocation, + RawAssertionPayload, ResolvedAssertionPayload, STRING_ERROR_SELECTOR, }, native_types::{Expression, Witness, WitnessMap}, AcirField, BlackBoxFunc, @@ -629,6 +630,16 @@ pub fn witness_to_value( } } +pub fn input_to_value( + initial_witness: &WitnessMap, + input: FunctionInput, +) -> Result> { + match input.input { + ConstantOrWitnessEnum::Witness(witness) => Ok(*witness_to_value(initial_witness, witness)?), + ConstantOrWitnessEnum::Constant(value) => Ok(value), + } +} + // TODO: There is an issue open to decide on whether we need to get values from Expressions // TODO versus just getting values from Witness pub fn get_value( diff --git a/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts b/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts index 5401da76974..80fbf14e8f1 100644 --- a/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts +++ b/acvm-repo/acvm_js/test/shared/multi_scalar_mul.ts @@ -1,8 +1,8 @@ // See `multi_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 219, 10, 0, 32, 8, 67, 243, 214, 5, 250, 232, 62, 189, 69, 123, 176, 132, - 195, 116, 50, 149, 114, 107, 0, 97, 127, 116, 2, 75, 243, 2, 74, 53, 122, 202, 189, 211, 15, 106, 5, 13, 116, 238, 35, - 221, 81, 230, 61, 249, 37, 253, 250, 179, 79, 109, 218, 22, 67, 227, 173, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 141, 11, 10, 0, 32, 8, 67, 43, 181, 15, 116, 232, 142, 158, 210, 130, 149, 240, + 112, 234, 212, 156, 78, 12, 39, 67, 71, 158, 142, 80, 29, 44, 228, 66, 90, 168, 119, 189, 74, 115, 131, 174, 78, 115, + 58, 124, 70, 254, 130, 59, 74, 253, 68, 255, 255, 221, 39, 54, 221, 93, 91, 132, 193, 0, 0, 0, ]); export const initialWitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts index 05fcc47e3aa..c071c86f61f 100644 --- a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts +++ b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts @@ -1,17 +1,19 @@ // See `schnorr_verify_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 210, 85, 78, 67, 81, 24, 133, 209, 226, 238, 238, 238, 238, 238, 165, 148, 82, - 102, 193, 252, 135, 64, 232, 78, 87, 147, 114, 147, 147, 5, 47, 132, 252, 251, 107, 41, 212, 191, 159, 218, 107, 241, - 115, 236, 226, 111, 237, 181, 178, 173, 246, 186, 107, 175, 157, 29, 236, 100, 23, 27, 175, 135, 189, 236, 99, 63, 7, - 56, 200, 33, 14, 115, 132, 163, 28, 227, 56, 39, 56, 201, 41, 78, 115, 134, 179, 156, 227, 60, 23, 184, 200, 37, 46, - 115, 133, 171, 92, 227, 58, 55, 184, 201, 45, 110, 115, 135, 187, 220, 227, 62, 15, 120, 200, 35, 30, 243, 132, 167, - 60, 227, 57, 47, 120, 201, 43, 94, 243, 134, 183, 188, 227, 61, 31, 248, 200, 39, 62, 243, 133, 175, 77, 59, 230, 123, - 243, 123, 145, 239, 44, 241, 131, 101, 126, 178, 194, 47, 86, 249, 237, 239, 86, 153, 238, 210, 92, 122, 75, 107, 233, - 44, 141, 53, 250, 234, 241, 191, 164, 167, 180, 148, 142, 210, 80, 250, 73, 59, 233, 38, 205, 164, 151, 180, 146, 78, - 210, 72, 250, 72, 27, 233, 34, 77, 164, 135, 180, 144, 14, 210, 64, 246, 95, 46, 212, 119, 207, 230, 217, 59, 91, 103, - 231, 108, 156, 125, 183, 237, 186, 107, 207, 125, 59, 30, 218, 239, 216, 110, 167, 246, 58, 183, 211, 165, 125, 174, - 237, 114, 107, 143, 123, 59, 60, 186, 255, 179, 187, 191, 186, 115, 209, 125, 75, 238, 90, 118, 207, 138, 59, 54, 110, - 214, 184, 91, 161, 233, 158, 255, 190, 63, 71, 59, 68, 130, 233, 3, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 85, 211, 103, 78, 2, 81, 24, 70, 225, 193, 6, 216, 123, 47, 216, 123, 239, 136, + 136, 136, 136, 136, 187, 96, 255, 75, 32, 112, 194, 55, 201, 129, 100, 50, 79, 244, 7, 228, 222, 243, 102, 146, 254, + 167, 221, 123, 50, 97, 222, 217, 120, 243, 116, 226, 61, 36, 15, 247, 158, 92, 120, 68, 30, 149, 199, 228, 172, 156, + 147, 243, 242, 184, 60, 33, 79, 202, 83, 242, 180, 60, 35, 207, 202, 115, 242, 188, 188, 32, 47, 202, 75, 242, 178, + 188, 34, 175, 202, 107, 242, 186, 188, 33, 111, 202, 91, 242, 182, 188, 35, 23, 228, 93, 121, 79, 222, 151, 15, 228, + 67, 249, 72, 62, 150, 79, 228, 83, 249, 76, 62, 151, 47, 228, 75, 249, 74, 190, 150, 111, 228, 91, 249, 78, 190, 151, + 31, 228, 71, 249, 73, 126, 150, 95, 228, 87, 185, 40, 191, 201, 37, 249, 93, 46, 203, 31, 114, 69, 254, 148, 171, 97, + 58, 77, 226, 111, 95, 250, 127, 77, 254, 150, 235, 242, 143, 220, 144, 127, 229, 166, 252, 39, 183, 194, 255, 241, + 253, 45, 253, 14, 182, 201, 38, 217, 34, 27, 100, 123, 233, 230, 242, 241, 155, 217, 20, 91, 98, 67, 108, 135, 205, + 176, 21, 54, 194, 54, 216, 4, 91, 96, 3, 180, 79, 243, 180, 78, 227, 180, 77, 211, 180, 76, 195, 180, 75, 179, 133, + 164, 223, 40, 109, 210, 36, 45, 210, 32, 237, 209, 28, 173, 209, 24, 109, 209, 20, 45, 209, 16, 237, 208, 12, 173, + 208, 8, 109, 208, 4, 45, 208, 0, 119, 207, 157, 115, 215, 220, 113, 49, 238, 180, 20, 119, 88, 142, 59, 171, 196, 29, + 85, 227, 46, 106, 113, 246, 245, 56, 235, 70, 156, 109, 51, 206, 50, 61, 179, 244, 220, 18, 157, 231, 192, 167, 11, + 75, 28, 99, 152, 25, 5, 0, 0, ]); export const initialWitnessMap = new Map([ diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index e1182f17bcd..820374df9c1 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -36,6 +36,7 @@ use self::{ }; mod acir_gen; +mod checks; pub(super) mod function_builder; pub mod ir; mod opt; diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 928a7b105ea..74149af25ef 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1366,9 +1366,10 @@ impl AcirContext { } _ => (vec![], vec![]), }; - + // Allow constant inputs only for MSM for now + let allow_constant_inputs = name.eq(&BlackBoxFunc::MultiScalarMul); // Convert `AcirVar` to `FunctionInput` - let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?; + let inputs = self.prepare_inputs_for_black_box_func_call(inputs, allow_constant_inputs)?; // Call Black box with `FunctionInput` let mut results = vecmap(&constant_outputs, |c| self.add_constant(*c)); let outputs = self.acir_ir.call_black_box( @@ -1396,18 +1397,23 @@ impl AcirContext { fn prepare_inputs_for_black_box_func_call( &mut self, inputs: Vec, - ) -> Result>, RuntimeError> { + allow_constant_inputs: bool, + ) -> Result>>, RuntimeError> { let mut witnesses = Vec::new(); for input in inputs { let mut single_val_witnesses = Vec::new(); for (input, typ) in self.flatten(input)? { - // Intrinsics only accept Witnesses. This is not a limitation of the - // intrinsics, its just how we have defined things. Ideally, we allow - // constants too. - let witness_var = self.get_or_create_witness_var(input)?; - let witness = self.var_to_witness(witness_var)?; let num_bits = typ.bit_size::(); - single_val_witnesses.push(FunctionInput { witness, num_bits }); + match self.vars[&input].as_constant() { + Some(constant) if allow_constant_inputs => { + single_val_witnesses.push(FunctionInput::constant(*constant, num_bits)); + } + _ => { + let witness_var = self.get_or_create_witness_var(input)?; + let witness = self.var_to_witness(witness_var)?; + single_val_witnesses.push(FunctionInput::witness(witness, num_bits)); + } + } } witnesses.push(single_val_witnesses); } @@ -1896,10 +1902,10 @@ impl AcirContext { output_count: usize, predicate: AcirVar, ) -> Result, RuntimeError> { - let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?; + let inputs = self.prepare_inputs_for_black_box_func_call(inputs, false)?; let inputs = inputs .iter() - .flat_map(|input| vecmap(input, |input| input.witness)) + .flat_map(|input| vecmap(input, |input| input.to_witness())) .collect::>(); let outputs = vecmap(0..output_count, |_| self.acir_ir.next_witness_index()); diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index bcccfac8950..9d29d1d24d6 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -165,7 +165,7 @@ impl GeneratedAcir { pub(crate) fn call_black_box( &mut self, func_name: BlackBoxFunc, - inputs: &[Vec], + inputs: &[Vec>], constant_inputs: Vec, constant_outputs: Vec, output_count: usize, @@ -571,7 +571,7 @@ impl GeneratedAcir { }; let constraint = AcirOpcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness, num_bits }, + input: FunctionInput::witness(witness, num_bits), }); self.push_opcode(constraint); diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 3b7d2c1025f..cfcc7a9a997 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -976,7 +976,15 @@ impl<'a> Context<'a> { } }; - if self.handle_constant_index(instruction, dfg, index, array, store_value)? { + let array_id = dfg.resolve(array); + let array_typ = dfg.type_of_value(array_id); + // Compiler sanity checks + assert!(!array_typ.is_nested_slice(), "ICE: Nested slice type has reached ACIR generation"); + let (Type::Array(_, _) | Type::Slice(_)) = &array_typ else { + unreachable!("ICE: expected array or slice type"); + }; + + if self.handle_constant_index_wrapper(instruction, dfg, array, index, store_value)? { return Ok(()); } @@ -984,8 +992,7 @@ impl<'a> Context<'a> { // If we find one, we will use it when computing the index under the enable_side_effect predicate // If not, array_get(..) will use a fallback costing one multiplication in the worst case. // cf. https://github.com/noir-lang/noir/pull/4971 - let array_id = dfg.resolve(array); - let array_typ = dfg.type_of_value(array_id); + // For simplicity we compute the offset only for simple arrays let is_simple_array = dfg.instruction_results(instruction).len() == 1 && can_omit_element_sizes_array(&array_typ); @@ -1018,83 +1025,106 @@ impl<'a> Context<'a> { Ok(()) } - /// Handle constant index: if there is no predicate and we have the array values, - /// we can perform the operation directly on the array - fn handle_constant_index( + fn handle_constant_index_wrapper( &mut self, instruction: InstructionId, dfg: &DataFlowGraph, + array: ValueId, index: ValueId, - array_id: ValueId, store_value: Option, ) -> Result { - let index_const = dfg.get_numeric_constant(index); - let value_type = dfg.type_of_value(array_id); + let array_id = dfg.resolve(array); + let array_typ = dfg.type_of_value(array_id); // Compiler sanity checks - assert!( - !value_type.is_nested_slice(), - "ICE: Nested slice type has reached ACIR generation" - ); - let (Type::Array(_, _) | Type::Slice(_)) = &value_type else { + assert!(!array_typ.is_nested_slice(), "ICE: Nested slice type has reached ACIR generation"); + let (Type::Array(_, _) | Type::Slice(_)) = &array_typ else { unreachable!("ICE: expected array or slice type"); }; match self.convert_value(array_id, dfg) { AcirValue::Var(acir_var, _) => { - return Err(RuntimeError::InternalError(InternalError::Unexpected { + Err(RuntimeError::InternalError(InternalError::Unexpected { expected: "an array value".to_string(), found: format!("{acir_var:?}"), call_stack: self.acir_context.get_call_stack(), })) } AcirValue::Array(array) => { - if let Some(index_const) = index_const { - let array_size = array.len(); - let index = match index_const.try_to_u64() { - Some(index_const) => index_const as usize, - None => { - let call_stack = self.acir_context.get_call_stack(); - return Err(RuntimeError::TypeConversion { - from: "array index".to_string(), - into: "u64".to_string(), - call_stack, - }); - } - }; + // `AcirValue::Array` supports reading/writing to constant indices at compile-time in some cases. + if let Some(constant_index) = dfg.get_numeric_constant(index) { + let store_value = store_value.map(|value| self.convert_value(value, dfg)); + self.handle_constant_index(instruction, dfg, array, constant_index, store_value) + } else { + Ok(false) + } + } + AcirValue::DynamicArray(_) => Ok(false), + } + } - if self.acir_context.is_constant_one(&self.current_side_effects_enabled_var) { - // Report the error if side effects are enabled. - if index >= array_size { - let call_stack = self.acir_context.get_call_stack(); - return Err(RuntimeError::IndexOutOfBounds { - index, - array_size, - call_stack, - }); - } else { - let value = match store_value { - Some(store_value) => { - let store_value = self.convert_value(store_value, dfg); - AcirValue::Array(array.update(index, store_value)) - } - None => array[index].clone(), - }; + /// Handle constant index: if there is no predicate and we have the array values, + /// we can perform the operation directly on the array + fn handle_constant_index( + &mut self, + instruction: InstructionId, + dfg: &DataFlowGraph, + array: Vector, + index: FieldElement, + store_value: Option, + ) -> Result { + let array_size: usize = array.len(); + let index = match index.try_to_u64() { + Some(index_const) => index_const as usize, + None => { + let call_stack = self.acir_context.get_call_stack(); + return Err(RuntimeError::TypeConversion { + from: "array index".to_string(), + into: "u64".to_string(), + call_stack, + }); + } + }; - self.define_result(dfg, instruction, value); - return Ok(true); - } - } - // If there is a predicate and the index is not out of range, we can directly perform the read - else if index < array_size && store_value.is_none() { - self.define_result(dfg, instruction, array[index].clone()); - return Ok(true); - } + let side_effects_always_enabled = + self.acir_context.is_constant_one(&self.current_side_effects_enabled_var); + let index_out_of_bounds = index >= array_size; + + // Note that the value of `side_effects_always_enabled` doesn't affect the value which we return here for valid + // indices, just whether we return an error for invalid indices at compile time or defer until execution. + match (side_effects_always_enabled, index_out_of_bounds) { + (true, false) => { + let value = match store_value { + Some(store_value) => AcirValue::Array(array.update(index, store_value)), + None => array[index].clone(), + }; + + self.define_result(dfg, instruction, value); + Ok(true) + } + (false, false) => { + if store_value.is_none() { + // If there is a predicate and the index is not out of range, we can optimistically perform the + // read at compile time as if the predicate is true. + // + // This is as if the predicate is false, any side-effects will be disabled so the value returned + // will not affect the rest of execution. + self.define_result(dfg, instruction, array[index].clone()); + Ok(true) + } else { + // We do not do this for a array writes however. + Ok(false) } } - AcirValue::DynamicArray(_) => (), - }; - Ok(false) + // Report the error if side effects are enabled. + (true, true) => { + let call_stack = self.acir_context.get_call_stack(); + Err(RuntimeError::IndexOutOfBounds { index, array_size, call_stack }) + } + // Index is out of bounds but predicate may result in this array operation being skipped + // so we don't return an error now. + (false, true) => Ok(false), + } } /// We need to properly setup the inputs for array operations in ACIR. diff --git a/compiler/noirc_evaluator/src/ssa/opt/check_for_underconstrained_values.rs b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs similarity index 98% rename from compiler/noirc_evaluator/src/ssa/opt/check_for_underconstrained_values.rs rename to compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index 6dac99d7a09..5831faa7c4d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/check_for_underconstrained_values.rs +++ b/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -105,7 +105,7 @@ impl Context { .iter() .chain(function.returns()) .filter(|id| function.dfg.get_numeric_constant(**id).is_none()) - .copied(); + .map(|value_id| function.dfg.resolve(*value_id)); let mut connected_sets_indices: HashSet = HashSet::new(); @@ -169,13 +169,13 @@ impl Context { // Insert non-constant instruction arguments function.dfg[*instruction].for_each_value(|value_id| { if function.dfg.get_numeric_constant(value_id).is_none() { - instruction_arguments_and_results.insert(value_id); + instruction_arguments_and_results.insert(function.dfg.resolve(value_id)); } }); // And non-constant results for value_id in function.dfg.instruction_results(*instruction).iter() { if function.dfg.get_numeric_constant(*value_id).is_none() { - instruction_arguments_and_results.insert(*value_id); + instruction_arguments_and_results.insert(function.dfg.resolve(*value_id)); } } diff --git a/compiler/noirc_evaluator/src/ssa/checks/mod.rs b/compiler/noirc_evaluator/src/ssa/checks/mod.rs new file mode 100644 index 00000000000..4f1831e5bb0 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/checks/mod.rs @@ -0,0 +1 @@ +mod check_for_underconstrained_values; diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index f854e8e0693..b08283a9ceb 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -379,9 +379,17 @@ impl Instruction { { true } - Instruction::EnableSideEffects { .. } - | Instruction::ArrayGet { .. } - | Instruction::ArraySet { .. } => true, + + // `ArrayGet`s which read from "known good" indices from an array don't need a predicate. + Instruction::ArrayGet { array, index } => { + #[allow(clippy::match_like_matches_macro)] + match (dfg.type_of_value(*array), dfg.get_numeric_constant(*index)) { + (Type::Array(_, len), Some(index)) if index.to_u128() < (len as u128) => false, + _ => true, + } + } + + Instruction::EnableSideEffects { .. } | Instruction::ArraySet { .. } => true, Instruction::Call { func, .. } => match dfg[*func] { Value::Function(_) => true, diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 48bd70ff139..d7cd366e9af 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -288,7 +288,7 @@ mod test { value::{Value, ValueId}, }, }; - use acvm::acir::AcirField; + use acvm::{acir::AcirField, FieldElement}; #[test] fn simple_constant_fold() { @@ -545,6 +545,73 @@ mod test { assert_eq!(instruction, &Instruction::Cast(v0, Type::unsigned(32))); } + #[test] + fn constant_index_array_access_deduplication() { + // fn main f0 { + // b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): + // enable_side_effects v2 + // v4 = array_get v0 u32 0 + // v5 = array_get v0 v1 + // enable_side_effects v3 + // v6 = array_get v0 u32 0 + // v7 = array_get v0 v1 + // constrain v4 v6 + // } + // + // After constructing this IR, we run constant folding which should replace the second constant-index array get + // with a reference to the results to the first. This then allows us to optimize away + // the constrain instruction as both inputs are known to be equal. + // + let main_id = Id::test_new(0); + + // Compiling main + let mut builder = FunctionBuilder::new("main".into(), main_id); + + let v0 = builder.add_parameter(Type::Array(Rc::new(vec![Type::field()]), 4)); + let v1 = builder.add_parameter(Type::unsigned(32)); + let v2 = builder.add_parameter(Type::unsigned(1)); + let v3 = builder.add_parameter(Type::unsigned(1)); + + let zero = builder.numeric_constant(FieldElement::zero(), Type::length_type()); + + builder.insert_enable_side_effects_if(v2); + let v4 = builder.insert_array_get(v0, zero, Type::field()); + let _v5 = builder.insert_array_get(v0, v1, Type::field()); + + builder.insert_enable_side_effects_if(v3); + let v6 = builder.insert_array_get(v0, zero, Type::field()); + let _v7 = builder.insert_array_get(v0, v1, Type::field()); + + builder.insert_constrain(v4, v6, None); + + let ssa = builder.finish(); + + println!("{ssa}"); + + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + assert_eq!(instructions.len(), 7); + + // Expected output: + // + // fn main f0 { + // b0(v0: [Field; 4], v1: u32, v2: bool, v3: bool): + // enable_side_effects v2 + // v10 = array_get v0 u32 0 + // v11 = array_get v0 v1 + // enable_side_effects v3 + // v12 = array_get v0 v1 + // } + let ssa = ssa.fold_constants(); + + println!("{ssa}"); + + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + + assert_eq!(instructions.len(), 5); + } + #[test] fn constraint_decomposition() { // fn main f0 { diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 7dda0ac7787..09802713363 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -45,23 +45,23 @@ impl Ssa { /// This step should run after runtime separation, since it relies on the runtime of the called functions being final. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn inline_functions(self) -> Ssa { - Self::inline_functions_inner(self, true) + Self::inline_functions_inner(self, false) } // Run the inlining pass where functions marked with `InlineType::NoPredicates` as not entry points pub(crate) fn inline_functions_with_no_predicates(self) -> Ssa { - Self::inline_functions_inner(self, false) + Self::inline_functions_inner(self, true) } - fn inline_functions_inner(mut self, no_predicates_is_entry_point: bool) -> Ssa { + fn inline_functions_inner(mut self, inline_no_predicates_functions: bool) -> Ssa { let recursive_functions = find_all_recursive_functions(&self); self.functions = btree_map( - get_functions_to_inline_into(&self, no_predicates_is_entry_point), + get_functions_to_inline_into(&self, inline_no_predicates_functions), |entry_point| { let new_function = InlineContext::new( &self, entry_point, - no_predicates_is_entry_point, + inline_no_predicates_functions, recursive_functions.clone(), ) .inline_all(&self); @@ -86,7 +86,13 @@ struct InlineContext { // The FunctionId of the entry point function we're inlining into in the old, unmodified Ssa. entry_point: FunctionId, - no_predicates_is_entry_point: bool, + /// Whether the inlining pass should inline any functions marked with [`InlineType::NoPredicates`] + /// or whether these should be preserved as entrypoint functions. + /// + /// This is done as we delay inlining of functions with the attribute `#[no_predicates]` until after + /// the control flow graph has been flattened. + inline_no_predicates_functions: bool, + // We keep track of the recursive functions in the SSA to avoid inlining them in a brillig context. recursive_functions: BTreeSet, } @@ -179,7 +185,7 @@ fn find_all_recursive_functions(ssa: &Ssa) -> BTreeSet { /// - Any Acir functions with a [fold inline type][InlineType::Fold], fn get_functions_to_inline_into( ssa: &Ssa, - no_predicates_is_entry_point: bool, + inline_no_predicates_functions: bool, ) -> BTreeSet { let mut brillig_entry_points = BTreeSet::default(); let mut acir_entry_points = BTreeSet::default(); @@ -190,10 +196,9 @@ fn get_functions_to_inline_into( } // If we have not already finished the flattening pass, functions marked - // to not have predicates should be marked as entry points. - let no_predicates_is_entry_point = - no_predicates_is_entry_point && function.is_no_predicates(); - if function.runtime().is_entry_point() || no_predicates_is_entry_point { + // to not have predicates should be preserved. + let preserve_function = !inline_no_predicates_functions && function.is_no_predicates(); + if function.runtime().is_entry_point() || preserve_function { acir_entry_points.insert(*func_id); } @@ -228,7 +233,7 @@ impl InlineContext { fn new( ssa: &Ssa, entry_point: FunctionId, - no_predicates_is_entry_point: bool, + inline_no_predicates_functions: bool, recursive_functions: BTreeSet, ) -> InlineContext { let source = &ssa.functions[&entry_point]; @@ -239,7 +244,7 @@ impl InlineContext { recursion_level: 0, entry_point, call_stack: CallStack::new(), - no_predicates_is_entry_point, + inline_no_predicates_functions, recursive_functions, } } @@ -470,6 +475,8 @@ impl<'function> PerFunctionContext<'function> { /// Inline each instruction in the given block into the function being inlined into. /// This may recurse if it finds another function to inline if a call instruction is within this block. fn inline_block_instructions(&mut self, ssa: &Ssa, block_id: BasicBlockId) { + let mut side_effects_enabled: Option = None; + let block = &self.source_function.dfg[block_id]; for id in block.instructions() { match &self.source_function.dfg[*id] { @@ -477,12 +484,28 @@ impl<'function> PerFunctionContext<'function> { Some(func_id) => { if self.should_inline_call(ssa, func_id) { self.inline_function(ssa, *id, func_id, arguments); + + // This is only relevant during handling functions with `InlineType::NoPredicates` as these + // can pollute the function they're being inlined into with `Instruction::EnabledSideEffects`, + // resulting in predicates not being applied properly. + // + // Note that this doesn't cover the case in which there exists an `Instruction::EnabledSideEffects` + // within the function being inlined whilst the source function has not encountered one yet. + // In practice this isn't an issue as the last `Instruction::EnabledSideEffects` in the + // function being inlined will be to turn off predicates rather than to create one. + if let Some(condition) = side_effects_enabled { + self.context.builder.insert_enable_side_effects_if(condition); + } } else { self.push_instruction(*id); } } None => self.push_instruction(*id), }, + Instruction::EnableSideEffects { condition } => { + side_effects_enabled = Some(self.translate_value(*condition)); + self.push_instruction(*id); + } _ => self.push_instruction(*id), } } @@ -495,10 +518,10 @@ impl<'function> PerFunctionContext<'function> { // If the called function is acir, we inline if it's not an entry point // If we have not already finished the flattening pass, functions marked - // to not have predicates should be marked as entry points. - let no_predicates_is_entry_point = - self.context.no_predicates_is_entry_point && function.is_no_predicates(); - !inline_type.is_entry_point() && !no_predicates_is_entry_point + // to not have predicates should be preserved. + let preserve_function = + !self.context.inline_no_predicates_functions && function.is_no_predicates(); + !inline_type.is_entry_point() && !preserve_function } else { // If the called function is brillig, we inline only if it's into brillig and the function is not recursive ssa.functions[&self.context.entry_point].runtime() == RuntimeType::Brillig diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 56484ced290..4e5fa262696 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -7,7 +7,6 @@ mod array_set; mod as_slice_length; mod assert_constant; mod bubble_up_constrains; -mod check_for_underconstrained_values; mod constant_folding; mod defunctionalize; mod die; diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index f9a3c9a55eb..1584b848564 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -18,6 +18,7 @@ use crate::ssa::{ dfg::DataFlowGraph, function::Function, instruction::{BinaryOp, Instruction, Intrinsic}, + types::Type, value::Value, }, ssa_gen::Ssa, @@ -62,6 +63,7 @@ impl Context { ) { let instructions = function.dfg[block].take_instructions(); + let mut active_condition = function.dfg.make_constant(FieldElement::one(), Type::bool()); let mut last_side_effects_enabled_instruction = None; let mut new_instructions = Vec::with_capacity(instructions.len()); @@ -72,19 +74,26 @@ impl Context { // instructions with side effects then we can drop the instruction we're holding and // continue with the new `Instruction::EnableSideEffects`. if let Instruction::EnableSideEffects { condition } = instruction { + // If this instruction isn't changing the currently active condition then we can ignore it. + if active_condition == *condition { + continue; + } + // If we're seeing an `enable_side_effects u1 1` then we want to insert it immediately. // This is because we want to maximize the effect it will have. - if function + let condition_is_one = function .dfg .get_numeric_constant(*condition) - .map_or(false, |condition| condition.is_one()) - { + .map_or(false, |condition| condition.is_one()); + if condition_is_one { new_instructions.push(instruction_id); last_side_effects_enabled_instruction = None; + active_condition = *condition; continue; } last_side_effects_enabled_instruction = Some(instruction_id); + active_condition = *condition; continue; } @@ -172,3 +181,88 @@ impl Context { } } } + +#[cfg(test)] +mod test { + + use crate::ssa::{ + function_builder::FunctionBuilder, + ir::{ + instruction::{BinaryOp, Instruction}, + map::Id, + types::Type, + }, + }; + + #[test] + fn remove_chains_of_same_condition() { + // acir(inline) fn main f0 { + // b0(v0: Field): + // enable_side_effects u1 1 + // v4 = mul v0, Field 2 + // enable_side_effects u1 1 + // v5 = mul v0, Field 2 + // enable_side_effects u1 1 + // v6 = mul v0, Field 2 + // enable_side_effects u1 1 + // v7 = mul v0, Field 2 + // enable_side_effects u1 1 + // (no terminator instruction) + // } + // + // After constructing this IR, we run constant folding which should replace the second cast + // with a reference to the results to the first. This then allows us to optimize away + // the constrain instruction as both inputs are known to be equal. + // + // The first cast instruction is retained and will be removed in the dead instruction elimination pass. + let main_id = Id::test_new(0); + + // Compiling main + let mut builder = FunctionBuilder::new("main".into(), main_id); + let v0 = builder.add_parameter(Type::field()); + + let two = builder.numeric_constant(2u128, Type::field()); + + let one = builder.numeric_constant(1u128, Type::bool()); + + builder.insert_enable_side_effects_if(one); + builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_enable_side_effects_if(one); + builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_enable_side_effects_if(one); + builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_enable_side_effects_if(one); + builder.insert_binary(v0, BinaryOp::Mul, two); + builder.insert_enable_side_effects_if(one); + + let ssa = builder.finish(); + + println!("{ssa}"); + + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + assert_eq!(instructions.len(), 9); + + // Expected output: + // + // acir(inline) fn main f0 { + // b0(v0: Field): + // v3 = mul v0, Field 2 + // v4 = mul v0, Field 2 + // v5 = mul v0, Field 2 + // v6 = mul v0, Field 2 + // (no terminator instruction) + // } + let ssa = ssa.remove_enable_side_effects(); + + println!("{ssa}"); + + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + + assert_eq!(instructions.len(), 4); + for instruction in instructions.iter().take(4) { + assert_eq!(&main.dfg[*instruction], &Instruction::binary(BinaryOp::Mul, v0, two)); + } + } +} diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 48e34ad7fc9..3e6a140ff93 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -10,6 +10,7 @@ use super::{ BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, MethodCallExpression, UnresolvedType, }; +use crate::hir::resolution::resolver::SELF_TYPE_NAME; use crate::lexer::token::SpannedToken; use crate::macros_api::SecondaryAttribute; use crate::parser::{ParserError, ParserErrorReason}; @@ -165,6 +166,12 @@ impl StatementKind { #[derive(Eq, Debug, Clone)] pub struct Ident(pub Spanned); +impl Ident { + pub fn is_self_type_name(&self) -> bool { + self.0.contents == SELF_TYPE_NAME + } +} + impl PartialEq for Ident { fn eq(&self, other: &Ident) -> bool { self.0.contents == other.0.contents diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index e013cf7b4f1..ad79af4e63d 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -29,7 +29,7 @@ use crate::{ }, node_interner::{DefinitionKind, ExprId, FuncId, ReferenceId}, token::Tokens, - Kind, QuotedType, Shared, StructType, Type, + QuotedType, Shared, StructType, Type, }; use super::Elaborator; @@ -51,23 +51,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span), ExpressionKind::If(if_) => self.elaborate_if(*if_), ExpressionKind::Variable(variable, generics) => { - let generics = generics.map(|option_inner| { - option_inner - .into_iter() - .map(|generic| { - // All type expressions should resolve to a `Type::Constant` - if generic.is_type_expression() { - self.resolve_type_inner( - generic, - &Kind::Numeric(Box::new(Type::default_int_type())), - ) - } else { - self.resolve_type(generic) - } - }) - .collect() - }); - return self.elaborate_variable(variable, generics); + return self.elaborate_variable(variable, generics) } ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), @@ -342,14 +326,18 @@ impl<'context> Elaborator<'context> { } }; - if func_id != FuncId::dummy_id() { + let generics = if func_id != FuncId::dummy_id() { let function_type = self.interner.function_meta(&func_id).typ.clone(); self.try_add_mutable_reference_to_object( &function_type, &mut object_type, &mut object, ); - } + + self.resolve_turbofish_generics(&func_id, method_call.generics, span) + } else { + None + }; // These arguments will be given to the desugared function call. // Compared to the method arguments, they also contain the object. @@ -367,9 +355,6 @@ impl<'context> Elaborator<'context> { let location = Location::new(span, self.file); let method = method_call.method_name; - let generics = method_call.generics.map(|option_inner| { - option_inner.into_iter().map(|generic| self.resolve_type(generic)).collect() - }); let turbofish_generics = generics.clone(); let method_call = HirMethodCallExpression { method, object, arguments, location, generics }; @@ -403,6 +388,7 @@ impl<'context> Elaborator<'context> { constructor: ConstructorExpression, ) -> (HirExpression, Type) { let span = constructor.type_name.span(); + let is_self_type = constructor.type_name.last_segment().is_self_type_name(); let (r#type, struct_generics) = if let Some(struct_id) = constructor.struct_type { let typ = self.interner.get_struct(struct_id); @@ -433,7 +419,7 @@ impl<'context> Elaborator<'context> { }); let referenced = ReferenceId::Struct(struct_type.borrow().id); - let reference = ReferenceId::Variable(Location::new(span, self.file)); + let reference = ReferenceId::Reference(Location::new(span, self.file), is_self_type); self.interner.add_reference(referenced, reference); (expr, Type::Struct(struct_type, generics)) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 2065657c864..6104da582a7 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -702,8 +702,7 @@ impl<'context> Elaborator<'context> { let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics - .filter_map(|generic| self.find_generic(&generic.ident().0.contents)) - .map(|ResolvedGeneric { name, type_var, .. }| (name.clone(), type_var.clone())) + .filter_map(|generic| self.find_generic(&generic.ident().0.contents).cloned()) .collect(); let statements = std::mem::take(&mut func.def.body.statements); @@ -1288,11 +1287,11 @@ impl<'context> Elaborator<'context> { .map_err(|error| error.into_compilation_error_pair())?; if value != Value::Unit { - let item = value - .into_top_level_item(location) + let items = value + .into_top_level_items(location) .map_err(|error| error.into_compilation_error_pair())?; - self.add_item(item, generated_items, location); + self.add_items(items, generated_items, location); } Ok(()) @@ -1350,6 +1349,8 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } + self.interner.add_definition_location(ReferenceId::Global(global_id)); + self.local_module = old_module; self.file = old_file; self.current_item = old_item; @@ -1449,11 +1450,13 @@ impl<'context> Elaborator<'context> { self.generics.clear(); if let Some(trait_id) = trait_id { + let trait_name = trait_impl.trait_path.last_segment(); + let referenced = ReferenceId::Trait(trait_id); - let reference = ReferenceId::Variable(Location::new( - trait_impl.trait_path.last_segment().span(), - trait_impl.file_id, - )); + let reference = ReferenceId::Reference( + Location::new(trait_name.span(), trait_impl.file_id), + trait_name.is_self_type_name(), + ); self.interner.add_reference(referenced, reference); } } @@ -1542,79 +1545,81 @@ impl<'context> Elaborator<'context> { (comptime, items) } - fn add_item( + fn add_items( &mut self, - item: TopLevelStatement, + items: Vec, generated_items: &mut CollectedItems, location: Location, ) { - match item { - TopLevelStatement::Function(function) => { - let id = self.interner.push_empty_fn(); - let module = self.module_id(); - self.interner.push_function(id, &function.def, module, location); - let functions = vec![(self.local_module, id, function)]; - generated_items.functions.push(UnresolvedFunctions { - file_id: self.file, - functions, - trait_id: None, - self_type: None, - }); - } - TopLevelStatement::TraitImpl(mut trait_impl) => { - let methods = dc_mod::collect_trait_impl_functions( - self.interner, - &mut trait_impl, - self.crate_id, - self.file, - self.local_module, - ); + for item in items { + match item { + TopLevelStatement::Function(function) => { + let id = self.interner.push_empty_fn(); + let module = self.module_id(); + self.interner.push_function(id, &function.def, module, location); + let functions = vec![(self.local_module, id, function)]; + generated_items.functions.push(UnresolvedFunctions { + file_id: self.file, + functions, + trait_id: None, + self_type: None, + }); + } + TopLevelStatement::TraitImpl(mut trait_impl) => { + let methods = dc_mod::collect_trait_impl_functions( + self.interner, + &mut trait_impl, + self.crate_id, + self.file, + self.local_module, + ); - generated_items.trait_impls.push(UnresolvedTraitImpl { - file_id: self.file, - module_id: self.local_module, - trait_generics: trait_impl.trait_generics, - trait_path: trait_impl.trait_name, - object_type: trait_impl.object_type, - methods, - generics: trait_impl.impl_generics, - where_clause: trait_impl.where_clause, - - // These last fields are filled in later - trait_id: None, - impl_id: None, - resolved_object_type: None, - resolved_generics: Vec::new(), - resolved_trait_generics: Vec::new(), - }); - } - TopLevelStatement::Global(global) => { - let (global, error) = dc_mod::collect_global( - self.interner, - self.def_maps.get_mut(&self.crate_id).unwrap(), - global, - self.file, - self.local_module, - ); + generated_items.trait_impls.push(UnresolvedTraitImpl { + file_id: self.file, + module_id: self.local_module, + trait_generics: trait_impl.trait_generics, + trait_path: trait_impl.trait_name, + object_type: trait_impl.object_type, + methods, + generics: trait_impl.impl_generics, + where_clause: trait_impl.where_clause, + + // These last fields are filled in later + trait_id: None, + impl_id: None, + resolved_object_type: None, + resolved_generics: Vec::new(), + resolved_trait_generics: Vec::new(), + }); + } + TopLevelStatement::Global(global) => { + let (global, error) = dc_mod::collect_global( + self.interner, + self.def_maps.get_mut(&self.crate_id).unwrap(), + global, + self.file, + self.local_module, + ); - generated_items.globals.push(global); - if let Some(error) = error { - self.errors.push(error); + generated_items.globals.push(global); + if let Some(error) = error { + self.errors.push(error); + } + } + // Assume that an error has already been issued + TopLevelStatement::Error => (), + + TopLevelStatement::Module(_) + | TopLevelStatement::Import(_) + | TopLevelStatement::Struct(_) + | TopLevelStatement::Trait(_) + | TopLevelStatement::Impl(_) + | TopLevelStatement::TypeAlias(_) + | TopLevelStatement::SubModule(_) => { + let item = item.to_string(); + let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; + self.errors.push(error.into_compilation_error_pair()); } - } - // Assume that an error has already been issued - TopLevelStatement::Error => (), - - TopLevelStatement::Module(_) - | TopLevelStatement::Import(_) - | TopLevelStatement::Struct(_) - | TopLevelStatement::Trait(_) - | TopLevelStatement::Impl(_) - | TopLevelStatement::TypeAlias(_) - | TopLevelStatement::SubModule(_) => { - let item = item.to_string(); - let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; - self.errors.push(error.into_compilation_error_pair()); } } } diff --git a/compiler/noirc_frontend/src/elaborator/patterns.rs b/compiler/noirc_frontend/src/elaborator/patterns.rs index e3c854d615d..b1c9b1b37cc 100644 --- a/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -3,7 +3,7 @@ use noirc_errors::{Location, Span}; use rustc_hash::FxHashSet as HashSet; use crate::{ - ast::ERROR_IDENT, + ast::{UnresolvedType, ERROR_IDENT}, hir::{ comptime::Interpreter, def_collector::dc_crate::CompilationError, @@ -15,7 +15,9 @@ use crate::{ stmt::HirPattern, }, macros_api::{HirExpression, Ident, Path, Pattern}, - node_interner::{DefinitionId, DefinitionKind, ExprId, GlobalId, ReferenceId, TraitImplKind}, + node_interner::{ + DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, ReferenceId, TraitImplKind, + }, Shared, StructType, Type, TypeBindings, }; @@ -159,6 +161,7 @@ impl<'context> Elaborator<'context> { new_definitions: &mut Vec, ) -> HirPattern { let name_span = name.last_segment().span(); + let is_self_type = name.last_segment().is_self_type_name(); let error_identifier = |this: &mut Self| { // Must create a name here to return a HirPattern::Identifier. Allowing @@ -200,7 +203,7 @@ impl<'context> Elaborator<'context> { ); let referenced = ReferenceId::Struct(struct_type.borrow().id); - let reference = ReferenceId::Variable(Location::new(name_span, self.file)); + let reference = ReferenceId::Reference(Location::new(name_span, self.file), is_self_type); self.interner.add_reference(referenced, reference); HirPattern::Struct(expected_type, fields, location) @@ -397,15 +400,53 @@ impl<'context> Elaborator<'context> { } } + /// Resolve generics using the expected kinds of the function we are calling + pub(super) fn resolve_turbofish_generics( + &mut self, + func_id: &FuncId, + unresolved_turbofish: Option>, + span: Span, + ) -> Option> { + let direct_generics = self.interner.function_meta(func_id).direct_generics.clone(); + + unresolved_turbofish.map(|option_inner| { + if option_inner.len() != direct_generics.len() { + let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { + expected_count: direct_generics.len(), + actual_count: option_inner.len(), + span, + }; + self.push_err(type_check_err); + } + + let generics_with_types = direct_generics.iter().zip(option_inner); + vecmap(generics_with_types, |(generic, unresolved_type)| { + self.resolve_type_inner(unresolved_type, &generic.kind) + }) + }) + } + pub(super) fn elaborate_variable( &mut self, variable: Path, - generics: Option>, + unresolved_turbofish: Option>, ) -> (ExprId, Type) { let span = variable.span; let expr = self.resolve_variable(variable); let definition_id = expr.id; + let definition_kind = + self.interner.try_definition(definition_id).map(|definition| definition.kind.clone()); + + // Resolve any generics if we the variable we have resolved is a function + // and if the turbofish operator was used. + let generics = definition_kind.and_then(|definition_kind| match &definition_kind { + DefinitionKind::Function(function) => { + self.resolve_turbofish_generics(function, unresolved_turbofish, span) + } + _ => None, + }); + let id = self.interner.push_expr(HirExpression::Ident(expr.clone(), generics.clone())); self.interner.push_expr_location(id, span, self.file); @@ -437,6 +478,8 @@ impl<'context> Elaborator<'context> { // Otherwise, then it is referring to an Identifier // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; // If the expression is a singular indent, we search the resolver's current scope as normal. + let span = path.span(); + let is_self_type_name = path.last_segment().is_self_type_name(); let (hir_ident, var_scope_index) = self.get_ident_from_path(path); if hir_ident.id != DefinitionId::dummy_id() { @@ -446,7 +489,8 @@ impl<'context> Elaborator<'context> { self.interner.add_function_dependency(current_item, func_id); } - let variable = ReferenceId::Variable(hir_ident.location); + let variable = + ReferenceId::Reference(hir_ident.location, is_self_type_name); let function = ReferenceId::Function(func_id); self.interner.add_reference(function, variable); } @@ -458,7 +502,8 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, global_id); } - let variable = ReferenceId::Variable(hir_ident.location); + let variable = + ReferenceId::Reference(hir_ident.location, is_self_type_name); let global = ReferenceId::Global(global_id); self.interner.add_reference(global, variable); } @@ -475,6 +520,11 @@ impl<'context> Elaborator<'context> { DefinitionKind::Local(_) => { // only local variables can be captured by closures. self.resolve_local_variable(hir_ident.clone(), var_scope_index); + + let referenced = ReferenceId::Local(hir_ident.id); + let reference = + ReferenceId::Reference(Location::new(span, self.file), false); + self.interner.add_reference(referenced, reference); } } } diff --git a/compiler/noirc_frontend/src/elaborator/scope.rs b/compiler/noirc_frontend/src/elaborator/scope.rs index b253e272982..b7016280453 100644 --- a/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/compiler/noirc_frontend/src/elaborator/scope.rs @@ -53,7 +53,10 @@ impl<'context> Elaborator<'context> { resolver.resolve(self.def_maps, path.clone(), &mut Some(&mut references))?; for (referenced, ident) in references.iter().zip(path.segments) { - let reference = ReferenceId::Variable(Location::new(ident.span(), self.file)); + let reference = ReferenceId::Reference( + Location::new(ident.span(), self.file), + ident.is_self_type_name(), + ); self.interner.add_reference(*referenced, reference); } } else { diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 6aed0ab196d..1fc92ad28ba 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -16,7 +16,7 @@ use crate::{ macros_api::{ ForLoopStatement, ForRange, HirStatement, LetStatement, Path, Statement, StatementKind, }, - node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, + node_interner::{DefinitionId, DefinitionKind, GlobalId, ReferenceId, StmtId}, Type, }; @@ -256,6 +256,10 @@ impl<'context> Elaborator<'context> { typ.follow_bindings() }; + let referenced = ReferenceId::Local(ident.id); + let reference = ReferenceId::Reference(Location::new(span, self.file), false); + self.interner.add_reference(referenced, reference); + (HirLValue::Ident(ident.clone(), typ.clone()), typ, mutable) } LValue::MemberAccess { object, field_name, span } => { diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index b6212abb4a2..698114cfb5e 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -58,6 +58,12 @@ impl<'context> Elaborator<'context> { use crate::ast::UnresolvedTypeData::*; let span = typ.span; + let (is_self_type_name, is_synthetic) = if let Named(ref named_path, _, synthetic) = typ.typ + { + (named_path.last_segment().is_self_type_name(), synthetic) + } else { + (false, false) + }; let resolved_type = match typ.typ { FieldElement => Type::FieldElement, @@ -147,17 +153,33 @@ impl<'context> Elaborator<'context> { Resolved(id) => self.interner.get_quoted_type(id).clone(), }; - if let Type::Struct(ref struct_type, _) = resolved_type { - if let Some(unresolved_span) = typ.span { - // Record the location of the type reference - self.interner.push_type_ref_location( - resolved_type.clone(), - Location::new(unresolved_span, self.file), - ); + if let Some(unresolved_span) = typ.span { + match resolved_type { + Type::Struct(ref struct_type, _) => { + // Record the location of the type reference + self.interner.push_type_ref_location( + resolved_type.clone(), + Location::new(unresolved_span, self.file), + ); - let referenced = ReferenceId::Struct(struct_type.borrow().id); - let reference = ReferenceId::Variable(Location::new(unresolved_span, self.file)); - self.interner.add_reference(referenced, reference); + if !is_synthetic { + let referenced = ReferenceId::Struct(struct_type.borrow().id); + let reference = ReferenceId::Reference( + Location::new(unresolved_span, self.file), + is_self_type_name, + ); + self.interner.add_reference(referenced, reference); + } + } + Type::Alias(ref alias_type, _) => { + let referenced = ReferenceId::Alias(alias_type.borrow().id); + let reference = ReferenceId::Reference( + Location::new(unresolved_span, self.file), + is_self_type_name, + ); + self.interner.add_reference(referenced, reference); + } + _ => (), } } @@ -347,6 +369,11 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, id); } + let referenced = ReferenceId::Global(id); + let reference = + ReferenceId::Reference(Location::new(path.span(), self.file), false); + self.interner.add_reference(referenced, reference); + Some(Type::Constant(self.eval_global_as_array_length(id, path))) } _ => None, diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 09b9d6ba3a5..25ff50085fe 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -50,6 +50,7 @@ pub enum InterpreterError { NonComptimeFnCallInSameCrate { function: String, location: Location }, NoImpl { location: Location }, NoMatchingImplFound { error: NoMatchingImplFoundError, file: FileId }, + ImplMethodTypeMismatch { expected: Type, actual: Type, location: Location }, Unimplemented { item: String, location: Location }, @@ -114,6 +115,7 @@ impl InterpreterError { | InterpreterError::NonComptimeFnCallInSameCrate { location, .. } | InterpreterError::Unimplemented { location, .. } | InterpreterError::NoImpl { location, .. } + | InterpreterError::ImplMethodTypeMismatch { location, .. } | InterpreterError::BreakNotInLoop { location, .. } | InterpreterError::ContinueNotInLoop { location, .. } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { @@ -344,6 +346,12 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = "No impl found due to prior type error".into(); CustomDiagnostic::simple_error(msg, String::new(), location.span) } + InterpreterError::ImplMethodTypeMismatch { expected, actual, location } => { + let msg = format!( + "Impl method type {actual} does not unify with trait method type {expected}" + ); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } InterpreterError::NoMatchingImplFound { error, .. } => error.into(), InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 959163d2d61..605a35780ed 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -74,7 +74,7 @@ impl<'a> Interpreter<'a> { let trait_method = self.interner.get_trait_method_id(function); perform_instantiation_bindings(&instantiation_bindings); - let impl_bindings = perform_impl_bindings(self.interner, trait_method, function); + let impl_bindings = perform_impl_bindings(self.interner, trait_method, function, location)?; let result = self.call_function_inner(function, arguments, location); undo_instantiation_bindings(impl_bindings); undo_instantiation_bindings(instantiation_bindings); diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index adb13c4bfbc..9e15b73324f 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -326,9 +326,12 @@ impl Value { } } - pub(crate) fn into_top_level_item(self, location: Location) -> IResult { + pub(crate) fn into_top_level_items( + self, + location: Location, + ) -> IResult> { match self { - Value::Code(tokens) => parse_tokens(tokens, parser::top_level_item(), location.file), + Value::Code(tokens) => parse_tokens(tokens, parser::top_level_items(), location.file), value => Err(InterpreterError::CannotInlineMacro { value, location }), } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index cbe9f3eb7fe..43ab6224ea7 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -343,7 +343,8 @@ impl DefCollector { let file_id = current_def_map.file_id(module_id); for (referenced, ident) in references.iter().zip(&collected_import.path.segments) { - let reference = ReferenceId::Variable(Location::new(ident.span(), file_id)); + let reference = + ReferenceId::Reference(Location::new(ident.span(), file_id), false); context.def_interner.add_reference(*referenced, reference); } @@ -510,21 +511,18 @@ fn add_import_reference( return; } - match def_id { - crate::macros_api::ModuleDefId::FunctionId(func_id) => { - let variable = ReferenceId::Variable(Location::new(name.span(), file_id)); - interner.add_reference(ReferenceId::Function(func_id), variable); + let referenced = match def_id { + crate::macros_api::ModuleDefId::ModuleId(module_id) => ReferenceId::Module(module_id), + crate::macros_api::ModuleDefId::FunctionId(func_id) => ReferenceId::Function(func_id), + crate::macros_api::ModuleDefId::TypeId(struct_id) => ReferenceId::Struct(struct_id), + crate::macros_api::ModuleDefId::TraitId(trait_id) => ReferenceId::Trait(trait_id), + crate::macros_api::ModuleDefId::TypeAliasId(type_alias_id) => { + ReferenceId::Alias(type_alias_id) } - crate::macros_api::ModuleDefId::TypeId(struct_id) => { - let variable = ReferenceId::Variable(Location::new(name.span(), file_id)); - interner.add_reference(ReferenceId::Struct(struct_id), variable); - } - crate::macros_api::ModuleDefId::TraitId(trait_id) => { - let variable = ReferenceId::Variable(Location::new(name.span(), file_id)); - interner.add_reference(ReferenceId::Trait(trait_id), variable); - } - _ => (), - } + crate::macros_api::ModuleDefId::GlobalId(global_id) => ReferenceId::Global(global_id), + }; + let reference = ReferenceId::Reference(Location::new(name.span(), file_id), false); + interner.add_reference(referenced, reference); } fn inject_prelude( diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 138a37f4174..48985116f4f 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -268,7 +268,6 @@ impl<'a> ModCollector<'a> { let mut definition_errors = vec![]; for struct_definition in types { let name = struct_definition.name.clone(); - let name_location = Location::new(name.span(), self.file_id); let unresolved = UnresolvedStruct { file_id: self.file_id, @@ -319,7 +318,6 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_struct_location(id, name_location); context.def_interner.add_definition_location(ReferenceId::Struct(id)); } definition_errors @@ -366,6 +364,8 @@ impl<'a> ModCollector<'a> { } self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); + + context.def_interner.add_definition_location(ReferenceId::Alias(type_alias_id)); } errors } @@ -381,7 +381,6 @@ impl<'a> ModCollector<'a> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; for trait_definition in traits { let name = trait_definition.name.clone(); - let name_location = Location::new(name.span(), self.file_id); // Create the corresponding module for the trait namespace let trait_id = match self.push_child_module( @@ -533,7 +532,6 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_trait_location(trait_id, name_location); context.def_interner.add_definition_location(ReferenceId::Trait(trait_id)); self.def_collector.items.traits.insert(trait_id, unresolved); @@ -652,7 +650,7 @@ impl<'a> ModCollector<'a> { Ok(child_mod_id) => { // Track that the "foo" in `mod foo;` points to the module "foo" let referenced = ReferenceId::Module(child_mod_id); - let reference = ReferenceId::Variable(location); + let reference = ReferenceId::Reference(location, false); context.def_interner.add_reference(referenced, reference); errors.extend(collect_defs( diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 364d694462b..793362bb3d6 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1096,8 +1096,7 @@ impl<'a> Resolver<'a> { let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics - .filter_map(|generic| self.find_generic(&generic.ident().0.contents)) - .map(|ResolvedGeneric { name, type_var, .. }| (name.clone(), type_var.clone())) + .filter_map(|generic| self.find_generic(&generic.ident().0.contents).cloned()) .collect(); FuncMeta { diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index a9a51b636a8..1a70bade863 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -22,7 +22,7 @@ use crate::{ traits::TraitConstraint, }, node_interner::{ExprId, FuncId, GlobalId, NodeInterner}, - Kind, Type, TypeBindings, + Kind, ResolvedGeneric, Type, TypeBindings, }; pub use self::errors::Source; @@ -281,8 +281,10 @@ pub(crate) fn check_trait_impl_method_matches_declaration( } // Substitute each generic on the trait function with the corresponding generic on the impl function - for ((_, trait_fn_generic), (name, impl_fn_generic)) in - trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) + for ( + ResolvedGeneric { type_var: trait_fn_generic, .. }, + ResolvedGeneric { name, type_var: impl_fn_generic, .. }, + ) in trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) { let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone(), Kind::Normal); bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index a4a9f855c62..fa8bb55abee 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -1,8 +1,6 @@ use iter_extended::vecmap; use noirc_errors::{Location, Span}; -use std::rc::Rc; - use super::expr::{HirBlockExpression, HirExpression, HirIdent}; use super::stmt::HirPattern; use super::traits::TraitConstraint; @@ -10,7 +8,7 @@ use crate::ast::{FunctionKind, FunctionReturnType, Visibility}; use crate::graph::CrateId; use crate::macros_api::BlockExpression; use crate::node_interner::{ExprId, NodeInterner, TraitImplId}; -use crate::{ResolvedGeneric, Type, TypeVariable}; +use crate::{ResolvedGeneric, Type}; /// A Hir function is a block expression /// with a list of statements @@ -113,7 +111,7 @@ pub struct FuncMeta { /// This does not include generics from an outer scope, like those introduced by /// an `impl` block. This also does not include implicit generics added by the compiler /// such as a trait's `Self` type variable. - pub direct_generics: Vec<(Rc, TypeVariable)>, + pub direct_generics: Vec, /// All the generics used by this function, which includes any implicit generics or generics /// from outer scopes, such as those introduced by an impl. diff --git a/compiler/noirc_frontend/src/locations.rs b/compiler/noirc_frontend/src/locations.rs index c142d10319b..fcaef0a8dd6 100644 --- a/compiler/noirc_frontend/src/locations.rs +++ b/compiler/noirc_frontend/src/locations.rs @@ -33,11 +33,23 @@ impl NodeInterner { match reference { ReferenceId::Module(id) => self.module_location(&id), ReferenceId::Function(id) => self.function_modifiers(&id).name_location, - ReferenceId::Struct(id) => self.struct_location(&id), - ReferenceId::Trait(id) => self.trait_location(&id), + ReferenceId::Struct(id) => { + let struct_type = self.get_struct(id); + let struct_type = struct_type.borrow(); + Location::new(struct_type.name.span(), struct_type.location.file) + } + ReferenceId::Trait(id) => { + let trait_type = self.get_trait(id); + Location::new(trait_type.name.span(), trait_type.location.file) + } ReferenceId::Global(id) => self.get_global(id).location, - ReferenceId::Alias(id) => self.get_type_alias(id).borrow().location, - ReferenceId::Variable(location) => location, + ReferenceId::Alias(id) => { + let alias_type = self.get_type_alias(id); + let alias_type = alias_type.borrow(); + Location::new(alias_type.name.span(), alias_type.location.file) + } + ReferenceId::Local(id) => self.definition(id).location, + ReferenceId::Reference(location, _) => location, } } @@ -83,47 +95,57 @@ impl NodeInterner { .map(|node_index| self.reference_location(self.reference_graph[node_index])) } - // Is the given location known to this interner? - pub fn is_location_known(&self, location: Location) -> bool { - self.location_indices.get_node_from_location(location).is_some() + // Returns the `ReferenceId` that exists at a given location, if any. + pub fn reference_at_location(&self, location: Location) -> Option { + self.location_indices.get_node_from_location(location)?; + + let node_index = self.location_indices.get_node_from_location(location)?; + Some(self.reference_graph[node_index]) } // Starting at the given location, find the node referenced by it. Then, gather // all locations that reference that node, and return all of them - // (the references and optionally the reference node if `include_reference` is true). + // (the references and optionally the referenced node if `include_referencedd` is true). + // If `include_self_type_name` is true, references where "Self" is written are returned, + // otherwise they are not. // Returns `None` if the location is not known to this interner. pub fn find_all_references( &self, location: Location, - include_reference: bool, + include_referenced: bool, + include_self_type_name: bool, ) -> Option> { let node_index = self.location_indices.get_node_from_location(location)?; let reference_node = self.reference_graph[node_index]; - let found_locations: Vec = match reference_node { - ReferenceId::Alias(_) | ReferenceId::Global(_) | ReferenceId::Module(_) => todo!(), - ReferenceId::Function(_) | ReferenceId::Struct(_) | ReferenceId::Trait(_) => { - self.find_all_references_for_index(node_index, include_reference) - } - - ReferenceId::Variable(_) => { - let referenced_node_index = self.referenced_index(node_index)?; - self.find_all_references_for_index(referenced_node_index, include_reference) - } + let referenced_node_index = if let ReferenceId::Reference(_, _) = reference_node { + self.referenced_index(node_index)? + } else { + node_index }; + + let found_locations = self.find_all_references_for_index( + referenced_node_index, + include_referenced, + include_self_type_name, + ); + Some(found_locations) } // Given a referenced node index, find all references to it and return their locations, optionally together - // with the reference node's location if `include_reference` is true. + // with the reference node's location if `include_referenced` is true. + // If `include_self_type_name` is true, references where "Self" is written are returned, + // otherwise they are not. fn find_all_references_for_index( &self, referenced_node_index: PetGraphIndex, - include_reference: bool, + include_referenced: bool, + include_self_type_name: bool, ) -> Vec { let id = self.reference_graph[referenced_node_index]; let mut edit_locations = Vec::new(); - if include_reference { + if include_referenced && (include_self_type_name || !id.is_self_type_name()) { edit_locations.push(self.reference_location(id)); } @@ -131,7 +153,9 @@ impl NodeInterner { .neighbors_directed(referenced_node_index, petgraph::Direction::Incoming) .for_each(|reference_node_index| { let id = self.reference_graph[reference_node_index]; - edit_locations.push(self.reference_location(id)); + if include_self_type_name || !id.is_self_type_name() { + edit_locations.push(self.reference_location(id)); + } }); edit_locations } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 1f057e4f544..fabfc74b901 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -72,7 +72,7 @@ struct Monomorphizer<'interner> { /// Queue of functions to monomorphize next each item in the queue is a tuple of: /// (old_id, new_monomorphized_id, any type bindings to apply, the trait method if old_id is from a trait impl) - queue: VecDeque<(node_interner::FuncId, FuncId, TypeBindings, Option)>, + queue: VecDeque<(node_interner::FuncId, FuncId, TypeBindings, Option, Location)>, /// When a function finishes being monomorphized, the monomorphized ast::Function is /// stored here along with its FuncId. @@ -124,11 +124,15 @@ pub fn monomorphize_debug( let function_sig = monomorphizer.compile_main(main)?; while !monomorphizer.queue.is_empty() { - let (next_fn_id, new_id, bindings, trait_method) = monomorphizer.queue.pop_front().unwrap(); + let (next_fn_id, new_id, bindings, trait_method, location) = + monomorphizer.queue.pop_front().unwrap(); monomorphizer.locals.clear(); perform_instantiation_bindings(&bindings); - let impl_bindings = perform_impl_bindings(monomorphizer.interner, trait_method, next_fn_id); + let interner = &monomorphizer.interner; + let impl_bindings = perform_impl_bindings(interner, trait_method, next_fn_id, location) + .map_err(MonomorphizationError::InterpreterError)?; + monomorphizer.function(next_fn_id, new_id)?; undo_instantiation_bindings(impl_bindings); undo_instantiation_bindings(bindings); @@ -1275,9 +1279,10 @@ impl<'interner> Monomorphizer<'interner> { let new_id = self.next_function_id(); self.define_function(id, function_type.clone(), turbofish_generics, new_id); + let location = self.interner.expr_location(&expr_id); let bindings = self.interner.get_instantiation_bindings(expr_id); let bindings = self.follow_bindings(bindings); - self.queue.push_back((id, new_id, bindings, trait_method)); + self.queue.push_back((id, new_id, bindings, trait_method, location)); new_id } @@ -1747,7 +1752,8 @@ pub fn perform_impl_bindings( interner: &NodeInterner, trait_method: Option, impl_method: node_interner::FuncId, -) -> TypeBindings { + location: Location, +) -> Result { let mut bindings = TypeBindings::new(); if let Some(trait_method) = trait_method { @@ -1767,14 +1773,18 @@ pub fn perform_impl_bindings( let type_bindings = generics.iter().map(replace_type_variable).collect(); let impl_method_type = impl_method_type.force_substitute(&type_bindings); - trait_method_type.try_unify(&impl_method_type, &mut bindings).unwrap_or_else(|_| { - unreachable!("Impl method type {} does not unify with trait method type {} during monomorphization", impl_method_type, trait_method_type) - }); + trait_method_type.try_unify(&impl_method_type, &mut bindings).map_err(|_| { + InterpreterError::ImplMethodTypeMismatch { + expected: trait_method_type.clone(), + actual: impl_method_type, + location, + } + })?; perform_instantiation_bindings(&bindings); } - bindings + Ok(bindings) } pub fn resolve_trait_method( diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 76a67e3977c..588b56afa1a 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -68,12 +68,6 @@ pub struct NodeInterner { // The location of each module module_locations: HashMap, - // The location of each struct name - struct_name_locations: HashMap, - - // The location of each trait name - trait_name_locations: HashMap, - /// This graph tracks dependencies between different global definitions. /// This is used to ensure the absence of dependency cycles for globals and types. dependency_graph: DiGraph, @@ -251,7 +245,14 @@ pub enum ReferenceId { Global(GlobalId), Function(FuncId), Alias(TypeAliasId), - Variable(Location), + Local(DefinitionId), + Reference(Location, bool /* is Self */), +} + +impl ReferenceId { + pub fn is_self_type_name(&self) -> bool { + matches!(self, Self::Reference(_, true)) + } } /// A trait implementation is either a normal implementation that is present in the source @@ -547,8 +548,6 @@ impl Default for NodeInterner { function_modifiers: HashMap::new(), function_modules: HashMap::new(), module_locations: HashMap::new(), - struct_name_locations: HashMap::new(), - trait_name_locations: HashMap::new(), func_id_to_trait: HashMap::new(), dependency_graph: petgraph::graph::DiGraph::new(), dependency_graph_indices: HashMap::new(), @@ -838,12 +837,19 @@ impl NodeInterner { location: Location, ) -> DefinitionId { let id = DefinitionId(self.definitions.len()); + let is_local = matches!(definition, DefinitionKind::Local(_)); + if let DefinitionKind::Function(func_id) = definition { self.function_definition_ids.insert(func_id, id); } let kind = definition; self.definitions.push(DefinitionInfo { name, mutable, comptime, kind, location }); + + if is_local { + self.add_definition_location(ReferenceId::Local(id)); + } + id } @@ -980,22 +986,6 @@ impl NodeInterner { &self.struct_attributes[struct_id] } - pub fn add_struct_location(&mut self, struct_id: StructId, location: Location) { - self.struct_name_locations.insert(struct_id, location); - } - - pub fn struct_location(&self, struct_id: &StructId) -> Location { - self.struct_name_locations[struct_id] - } - - pub fn add_trait_location(&mut self, trait_id: TraitId, location: Location) { - self.trait_name_locations.insert(trait_id, location); - } - - pub fn trait_location(&self, trait_id: &TraitId) -> Location { - self.trait_name_locations[trait_id] - } - pub fn add_module_location(&mut self, module_id: ModuleId, location: Location) { self.module_locations.insert(module_id, location); } diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index d7a282dbfc7..c4aa0654ecd 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/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::{expression, parse_program, top_level_item}; +pub use parser::{expression, parse_program, top_level_items}; #[derive(Debug, Clone)] pub enum TopLevelStatement { diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index afeee889ede..de9095aaff2 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -192,8 +192,8 @@ fn module() -> impl NoirParser { } /// This parser is used for parsing top level statements in macros -pub fn top_level_item() -> impl NoirParser { - top_level_statement(module()) +pub fn top_level_items() -> impl NoirParser> { + top_level_statement(module()).repeated() } /// top_level_statement: function_definition @@ -1117,16 +1117,11 @@ where } fn quote() -> impl NoirParser { - token_kind(TokenKind::Quote).validate(|token, span, emit| { - let tokens = match token { + token_kind(TokenKind::Quote).map(|token| { + ExpressionKind::Quote(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) + }) }) } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 10183ae0ac9..70f8f785d68 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1455,6 +1455,79 @@ fn specify_method_types_with_turbofish() { assert_eq!(errors.len(), 0); } +#[test] +fn incorrect_turbofish_count_function_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + impl Default for u64 { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + fn generic_func() -> (T, U) where T: Default, U: Default { + (T::default(), U::default()) + } + + fn main() { + let _ = generic_func::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + +#[test] +fn incorrect_turbofish_count_method_call() { + let src = r#" + trait Default { + fn default() -> Self; + } + + impl Default for Field { + fn default() -> Self { 0 } + } + + // Need the above as we don't have access to the stdlib here. + // We also need to construct a concrete value of `U` without giving away its type + // as otherwise the unspecified type is ignored. + + struct Foo { + inner: T + } + + impl Foo { + fn generic_method(_self: Self) -> U where U: Default { + U::default() + } + } + + fn main() { + let foo: Foo = Foo { inner: 1 }; + let _ = foo.generic_method::(); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), + )); +} + #[test] fn struct_numeric_generic_in_function() { let src = r#" @@ -1983,3 +2056,56 @@ fn underflowing_i8() { panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); } } + +#[test] +fn turbofish_numeric_generic_nested_call() { + // Check for turbofish numeric generics used with function calls + let src = r#" + fn foo() -> [u8; N] { + [0; N] + } + + fn bar() -> [u8; N] { + foo::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); + + // Check for turbofish numeric generics used with method calls + let src = r#" + struct Foo { + a: T + } + + impl Foo { + fn static_method() -> [u8; N] { + [0; N] + } + + fn impl_method(self) -> [T; N] { + [self.a; N] + } + } + + fn bar() -> [u8; N] { + let _ = Foo::static_method::(); + let x: Foo = Foo { a: 0 }; + x.impl_method::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + let errors = get_program_errors(src); + assert!(errors.is_empty()); +} diff --git a/docs/docs/noir/concepts/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx index d619117b799..95da2030843 100644 --- a/docs/docs/noir/concepts/data_types/slices.mdx +++ b/docs/docs/noir/concepts/data_types/slices.mdx @@ -197,7 +197,7 @@ fn main() { Applies a function to each element of the slice, returning a new slice containing the mapped elements. ```rust -fn map(self, f: fn(T) -> U) -> [U] +fn map(self, f: fn[Env](T) -> U) -> [U] ``` example @@ -213,7 +213,7 @@ Applies a function to each element of the slice, returning the final accumulated parameter is the initial value. ```rust -fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +fn fold(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U ``` This is a left fold, so the given function will be applied to the accumulator and first element of @@ -247,7 +247,7 @@ fn main() { Same as fold, but uses the first element as the starting element. ```rust -fn reduce(self, f: fn(T, T) -> T) -> T +fn reduce(self, f: fn[Env](T, T) -> T) -> T ``` example: @@ -260,12 +260,72 @@ fn main() { } ``` +### filter + +Returns a new slice containing only elements for which the given predicate returns true. + +```rust +fn filter(self, f: fn[Env](T) -> bool) -> Self +``` + +example: + +```rust +fn main() { + let slice = &[1, 2, 3, 4, 5]; + let odds = slice.filter(|x| x % 2 == 1); + assert_eq(odds, &[1, 3, 5]); +} +``` + +### join + +Flatten each element in the slice into one value, separated by `separator`. + +Note that although slices implement `Append`, `join` cannot be used on slice +elements since nested slices are prohibited. + +```rust +fn join(self, separator: T) -> T where T: Append +``` + +example: + +```rust +struct Accumulator { + total: Field, +} + +// "Append" two accumulators by adding them +impl Append for Accumulator { + fn empty() -> Self { + Self { total: 0 } + } + + fn append(self, other: Self) -> Self { + Self { total: self.total + other.total } + } +} + +fn main() { + let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total }); + + let result = slice.join(Accumulator::empty()); + assert_eq(result, Accumulator { total: 15 }); + + // We can use a non-empty separator to insert additional elements to sum: + let separator = Accumulator { total: 10 }; + let result = slice.join(separator); + assert_eq(result, Accumulator { total: 55 }); +} +``` + ### all Returns true if all the elements satisfy the given predicate ```rust -fn all(self, predicate: fn(T) -> bool) -> bool +fn all(self, predicate: fn[Env](T) -> bool) -> bool ``` example: @@ -283,7 +343,7 @@ fn main() { Returns true if any of the elements satisfy the given predicate ```rust -fn any(self, predicate: fn(T) -> bool) -> bool +fn any(self, predicate: fn[Env](T) -> bool) -> bool ``` example: diff --git a/docs/docs/noir/standard_library/traits.md b/docs/docs/noir/standard_library/traits.md index 96a7b8e2f22..e6f6f80ff03 100644 --- a/docs/docs/noir/standard_library/traits.md +++ b/docs/docs/noir/standard_library/traits.md @@ -51,6 +51,7 @@ For primitive integer types, the return value of `default` is `0`. Container types such as arrays are filled with default values of their element type, except slices whose length is unknown and thus defaulted to zero. +--- ## `std::convert` @@ -85,6 +86,7 @@ For this reason, implementing `From` on a type will automatically generate a mat `Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. +--- ## `std::cmp` @@ -178,6 +180,8 @@ impl Ord for (A, B, C, D, E) where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } ``` +--- + ## `std::ops` ### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` @@ -301,3 +305,29 @@ impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } ``` + +--- + +## `std::append` + +### `std::append::Append` + +`Append` can abstract over types that can be appended to - usually container types: + +#include_code append-trait noir_stdlib/src/append.nr rust + +`Append` requires two methods: + +- `empty`: Constructs an empty value of `Self`. +- `append`: Append two values together, returning the result. + +Additionally, it is expected that for any implementation: + +- `T::empty().append(x) == x` +- `x.append(T::empty()) == x` + +Implementations: +```rust +impl Append for [T] +impl Append for Quoted +``` diff --git a/noir_stdlib/src/append.nr b/noir_stdlib/src/append.nr new file mode 100644 index 00000000000..4577ae199b8 --- /dev/null +++ b/noir_stdlib/src/append.nr @@ -0,0 +1,35 @@ +// Appends two values together, returning the result. +// +// An alternate name for this trait is `Monoid` if that is familiar. +// If not, it can be ignored. +// +// It is expected that for any implementation: +// - `T::empty().append(x) == x` +// - `x.append(T::empty()) == x` +// docs:start:append-trait +trait Append { + fn empty() -> Self; + fn append(self, other: Self) -> Self; +} +// docs:end:append-trait + +impl Append for [T] { + fn empty() -> Self { + &[] + } + + fn append(self, other: Self) -> Self { + // Slices have an existing append function which this will resolve to. + self.append(other) + } +} + +impl Append for Quoted { + fn empty() -> Self { + quote {} + } + + fn append(self, other: Self) -> Self { + quote { $self $other } + } +} diff --git a/noir_stdlib/src/embedded_curve_ops.nr b/noir_stdlib/src/embedded_curve_ops.nr index c5617094c0a..c791ac88404 100644 --- a/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir_stdlib/src/embedded_curve_ops.nr @@ -1,7 +1,6 @@ use crate::ops::arith::{Add, Sub, Neg}; use crate::cmp::Eq; -// TODO(https://github.com/noir-lang/noir/issues/4931) struct EmbeddedCurvePoint { x: Field, y: Field, @@ -9,6 +8,10 @@ struct EmbeddedCurvePoint { } impl EmbeddedCurvePoint { + fn new(x: Field, y: Field, is_infinite: bool) -> Self { + Self { x, y, is_infinite } + } + fn double(self) -> EmbeddedCurvePoint { embedded_curve_add(self, self) } @@ -53,6 +56,15 @@ struct EmbeddedCurveScalar { } impl EmbeddedCurveScalar { + pub fn new(lo: Field, hi: Field) -> Self { + EmbeddedCurveScalar { lo, hi } + } + + pub fn derive_public_key(self) -> EmbeddedCurvePoint { + let public_key = fixed_base_scalar_mul(self.lo, self.hi); + EmbeddedCurvePoint { x: public_key[0], y: public_key[1], is_infinite: false } + } + #[field(bn254)] fn from_field(scalar: Field) -> EmbeddedCurveScalar { let (a,b) = crate::field::bn254::decompose(scalar); @@ -60,6 +72,12 @@ impl EmbeddedCurveScalar { } } +impl Eq for EmbeddedCurveScalar { + fn eq(self, key: EmbeddedCurveScalar) -> bool { + (key.hi == self.hi) & (key.lo == self.lo) + } +} + // Computes a multi scalar multiplication over the embedded curve. // For bn254, We have Grumpkin and Baby JubJub. // For bls12-381, we have JubJub and Bandersnatch. diff --git a/noir_stdlib/src/hash/mod.nr b/noir_stdlib/src/hash/mod.nr index 493430c99a4..65f3b9419ff 100644 --- a/noir_stdlib/src/hash/mod.nr +++ b/noir_stdlib/src/hash/mod.nr @@ -25,6 +25,7 @@ pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} +#[no_predicates] // docs:start:pedersen_commitment pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint { // docs:end:pedersen_commitment @@ -46,6 +47,7 @@ fn pedersen_commitment_with_separator_noir(input: [Field; N], separa EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: values[2] as bool } } +#[no_predicates] 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 } diff --git a/noir_stdlib/src/hash/poseidon/bn254.nr b/noir_stdlib/src/hash/poseidon/bn254.nr index 0e47ca11e20..103ab3d166a 100644 --- a/noir_stdlib/src/hash/poseidon/bn254.nr +++ b/noir_stdlib/src/hash/poseidon/bn254.nr @@ -12,6 +12,7 @@ pub fn sponge(msg: [Field; N]) -> Field { // Various instances of the Poseidon hash function // Consistent with Circom's implementation +#[no_predicates] pub fn hash_1(input: [Field; 1]) -> Field { let mut state = [0; 2]; for i in 0..input.len() { @@ -21,6 +22,7 @@ pub fn hash_1(input: [Field; 1]) -> Field { perm::x5_2(state)[0] } +#[no_predicates] pub fn hash_2(input: [Field; 2]) -> Field { let mut state = [0; 3]; for i in 0..input.len() { @@ -30,6 +32,7 @@ pub fn hash_2(input: [Field; 2]) -> Field { perm::x5_3(state)[0] } +#[no_predicates] pub fn hash_3(input: [Field; 3]) -> Field { let mut state = [0; 4]; for i in 0..input.len() { @@ -39,6 +42,7 @@ pub fn hash_3(input: [Field; 3]) -> Field { perm::x5_4(state)[0] } +#[no_predicates] pub fn hash_4(input: [Field; 4]) -> Field { let mut state = [0; 5]; for i in 0..input.len() { @@ -48,6 +52,7 @@ pub fn hash_4(input: [Field; 4]) -> Field { perm::x5_5(state)[0] } +#[no_predicates] pub fn hash_5(input: [Field; 5]) -> Field { let mut state = [0; 6]; for i in 0..input.len() { @@ -57,6 +62,7 @@ pub fn hash_5(input: [Field; 5]) -> Field { perm::x5_6(state)[0] } +#[no_predicates] pub fn hash_6(input: [Field; 6]) -> Field { let mut state = [0; 7]; for i in 0..input.len() { @@ -66,6 +72,7 @@ pub fn hash_6(input: [Field; 6]) -> Field { perm::x5_7(state)[0] } +#[no_predicates] pub fn hash_7(input: [Field; 7]) -> Field { let mut state = [0; 8]; for i in 0..input.len() { @@ -75,6 +82,7 @@ pub fn hash_7(input: [Field; 7]) -> Field { perm::x5_8(state)[0] } +#[no_predicates] pub fn hash_8(input: [Field; 8]) -> Field { let mut state = [0; 9]; for i in 0..input.len() { @@ -84,6 +92,7 @@ pub fn hash_8(input: [Field; 8]) -> Field { perm::x5_9(state)[0] } +#[no_predicates] pub fn hash_9(input: [Field; 9]) -> Field { let mut state = [0; 10]; for i in 0..input.len() { @@ -93,6 +102,7 @@ pub fn hash_9(input: [Field; 9]) -> Field { perm::x5_10(state)[0] } +#[no_predicates] pub fn hash_10(input: [Field; 10]) -> Field { let mut state = [0; 11]; for i in 0..input.len() { @@ -102,6 +112,7 @@ pub fn hash_10(input: [Field; 10]) -> Field { perm::x5_11(state)[0] } +#[no_predicates] pub fn hash_11(input: [Field; 11]) -> Field { let mut state = [0; 12]; for i in 0..input.len() { @@ -111,6 +122,7 @@ pub fn hash_11(input: [Field; 11]) -> Field { perm::x5_12(state)[0] } +#[no_predicates] pub fn hash_12(input: [Field; 12]) -> Field { let mut state = [0; 13]; for i in 0..input.len() { @@ -120,6 +132,7 @@ pub fn hash_12(input: [Field; 12]) -> Field { perm::x5_13(state)[0] } +#[no_predicates] pub fn hash_13(input: [Field; 13]) -> Field { let mut state = [0; 14]; for i in 0..input.len() { @@ -129,6 +142,7 @@ pub fn hash_13(input: [Field; 13]) -> Field { perm::x5_14(state)[0] } +#[no_predicates] pub fn hash_14(input: [Field; 14]) -> Field { let mut state = [0; 15]; for i in 0..input.len() { @@ -138,6 +152,7 @@ pub fn hash_14(input: [Field; 14]) -> Field { perm::x5_15(state)[0] } +#[no_predicates] pub fn hash_15(input: [Field; 15]) -> Field { let mut state = [0; 16]; for i in 0..input.len() { @@ -147,6 +162,7 @@ pub fn hash_15(input: [Field; 15]) -> Field { perm::x5_16(state)[0] } +#[no_predicates] pub fn hash_16(input: [Field; 16]) -> Field { let mut state = [0; 17]; for i in 0..input.len() { diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index e34992364ab..08cf68d1f82 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -11,6 +11,7 @@ struct Poseidon2 { } impl Poseidon2 { + #[no_predicates] pub fn hash(input: [Field; N], message_size: u32) -> Field { if message_size == N { Poseidon2::hash_internal(input, N, false) diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 65da7e6e9ab..ac53941e752 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -27,6 +27,7 @@ mod uint128; mod bigint; mod runtime; mod meta; +mod append; // 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_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index 0161756c1d0..b5dc958e9d1 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -15,7 +15,9 @@ fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] { msg32 } + // SHA-256 hash function +#[no_predicates] pub fn digest(msg: [u8; N]) -> [u8; 32] { sha256_var(msg, N as u64) } diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index aed6c2878b3..be255a594af 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -49,7 +49,9 @@ fn sha_w(msg: [u64; 16]) -> [u64; 80] // Expanded message blocks } w } + // SHA-512 compression function +#[no_predicates] fn sha_c(msg: [u64; 16], hash: [u64; 8]) -> [u64; 8] { // noir-fmt:ignore let K: [u64; 80] = [4794697086780616226, 8158064640168781261, 13096744586834688815, 16840607885511220156, 4131703408338449720, 6480981068601479193, 10538285296894168987, 12329834152419229976, 15566598209576043074, 1334009975649890238, 2608012711638119052, 6128411473006802146, 8268148722764581231, 9286055187155687089, 11230858885718282805, 13951009754708518548, 16472876342353939154, 17275323862435702243, 1135362057144423861, 2597628984639134821, 3308224258029322869, 5365058923640841347, 6679025012923562964, 8573033837759648693, 10970295158949994411, 12119686244451234320, 12683024718118986047, 13788192230050041572, 14330467153632333762, 15395433587784984357, 489312712824947311, 1452737877330783856, 2861767655752347644, 3322285676063803686, 5560940570517711597, 5996557281743188959, 7280758554555802590, 8532644243296465576, 9350256976987008742, 10552545826968843579, 11727347734174303076, 12113106623233404929, 14000437183269869457, 14369950271660146224, 15101387698204529176, 15463397548674623760, 17586052441742319658, 1182934255886127544, 1847814050463011016, 2177327727835720531, 2830643537854262169, 3796741975233480872, 4115178125766777443, 5681478168544905931, 6601373596472566643, 7507060721942968483, 8399075790359081724, 8693463985226723168, 9568029438360202098, 10144078919501101548, 10430055236837252648, 11840083180663258601, 13761210420658862357, 14299343276471374635, 14566680578165727644, 15097957966210449927, 16922976911328602910, 17689382322260857208, 500013540394364858, 748580250866718886, 1242879168328830382, 1977374033974150939, 2944078676154940804, 3659926193048069267, 4368137639120453308, 4836135668995329356, 5532061633213252278, 6448918945643986474, 6902733635092675308, 7801388544844847127]; // first 64 bits of fractional parts of cube roots of first 80 primes diff --git a/noir_stdlib/src/slice.nr b/noir_stdlib/src/slice.nr index 1a40abcf704..8d3f395f080 100644 --- a/noir_stdlib/src/slice.nr +++ b/noir_stdlib/src/slice.nr @@ -1,3 +1,5 @@ +use crate::append::Append; + impl [T] { #[builtin(array_len)] pub fn len(self) -> u32 {} @@ -85,6 +87,33 @@ impl [T] { accumulator } + // Returns a new slice containing only elements for which the given predicate + // returns true. + pub fn filter(self, predicate: fn[Env](T) -> bool) -> Self { + let mut ret = &[]; + for elem in self { + if predicate(elem) { + ret = ret.push_back(elem); + } + } + ret + } + + // Flatten each element in the slice into one value, separated by `separator`. + pub fn join(self, separator: T) -> T where T: Append { + let mut ret = T::empty(); + + if self.len() != 0 { + ret = self[0]; + + for i in 1..self.len() { + ret = ret.append(separator).append(self[i]); + } + } + + ret + } + // Returns true if all elements in the slice satisfy the predicate pub fn all(self, predicate: fn[Env](T) -> bool) -> bool { let mut ret = true; diff --git a/test_programs/compile_success_empty/slice_join/Nargo.toml b/test_programs/compile_success_empty/slice_join/Nargo.toml new file mode 100644 index 00000000000..44be002efb4 --- /dev/null +++ b/test_programs/compile_success_empty/slice_join/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "slice_join" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_empty/slice_join/src/main.nr b/test_programs/compile_success_empty/slice_join/src/main.nr new file mode 100644 index 00000000000..217000694b0 --- /dev/null +++ b/test_programs/compile_success_empty/slice_join/src/main.nr @@ -0,0 +1,16 @@ +use std::append::Append; + +fn main() { + let slice = &[1, 2, 3, 4, 5]; + + let odds = slice.filter(|x| x % 2 == 1); + assert_eq(odds, &[1, 3, 5]); + + let odds_and_evens = append_three(odds, &[100], &[2, 4]); + assert_eq(odds_and_evens, &[1, 3, 5, 100, 2, 4]); +} + +fn append_three(one: T, two: T, three: T) -> T where T: Append { + // The `T::empty()`s here should do nothing + T::empty().append(one).append(two).append(three).append(T::empty()) +} diff --git a/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/Nargo.toml b/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/Nargo.toml new file mode 100644 index 00000000000..63f15f2b349 --- /dev/null +++ b/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unquote_multiple_items_from_annotation" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr b/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr new file mode 100644 index 00000000000..04f07f038e5 --- /dev/null +++ b/test_programs/compile_success_empty/unquote_multiple_items_from_annotation/src/main.nr @@ -0,0 +1,14 @@ +#[foo] +struct Foo {} + +fn main() { + assert_eq(ONE, 1); + assert_eq(TWO, 2); +} + +comptime fn foo(_: StructDefinition) -> Quoted { + quote { + global ONE = 1; + global TWO = 2; + } +} diff --git a/test_programs/execution_success/regression_5435/Nargo.toml b/test_programs/execution_success/regression_5435/Nargo.toml new file mode 100644 index 00000000000..6affc423b7a --- /dev/null +++ b/test_programs/execution_success/regression_5435/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_5435" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] diff --git a/test_programs/execution_success/regression_5435/Prover.toml b/test_programs/execution_success/regression_5435/Prover.toml new file mode 100644 index 00000000000..f3e2bbe32e7 --- /dev/null +++ b/test_programs/execution_success/regression_5435/Prover.toml @@ -0,0 +1,2 @@ +input = "0" +enable = false diff --git a/test_programs/execution_success/regression_5435/src/main.nr b/test_programs/execution_success/regression_5435/src/main.nr new file mode 100644 index 00000000000..65f13c5b201 --- /dev/null +++ b/test_programs/execution_success/regression_5435/src/main.nr @@ -0,0 +1,18 @@ +fn main(input: Field, enable: bool) { + if enable { + let hash = no_predicate_function(input); + // `EnableSideEffects` instruction from above instruction leaks out and removes the predicate from this call, + // resulting in execution failure. + fail(hash); + } +} + +#[no_predicates] +fn no_predicate_function(enable: Field) -> Field { + // if-statement ensures that an `EnableSideEffects` instruction is emitted. + if enable == 0 { 1 } else { 0 } +} + +unconstrained fn fail(_: Field) { + assert(false); +} diff --git a/tooling/fuzzer/src/dictionary/mod.rs b/tooling/fuzzer/src/dictionary/mod.rs index bf2ab87be29..a45b9c3abb2 100644 --- a/tooling/fuzzer/src/dictionary/mod.rs +++ b/tooling/fuzzer/src/dictionary/mod.rs @@ -10,7 +10,7 @@ use acvm::{ circuit::{ brillig::{BrilligBytecode, BrilligInputs}, directives::Directive, - opcodes::{BlackBoxFuncCall, FunctionInput}, + opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, Circuit, Opcode, Program, }, native_types::Expression, @@ -84,7 +84,15 @@ fn build_dictionary_from_circuit(circuit: &Circuit) -> HashSet< } Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { num_bits, .. }, + input: FunctionInput { input: ConstantOrWitnessEnum::Constant(c), num_bits }, + }) => { + let field = 1u128.wrapping_shl(*num_bits); + constants.insert(F::from(field)); + constants.insert(F::from(field - 1)); + constants.insert(*c); + } + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { input: ConstantOrWitnessEnum::Witness(_), num_bits }, }) => { let field = 1u128.wrapping_shl(*num_bits); constants.insert(F::from(field)); diff --git a/tooling/lsp/src/requests/goto_definition.rs b/tooling/lsp/src/requests/goto_definition.rs index fe591a433cf..3713e8b646a 100644 --- a/tooling/lsp/src/requests/goto_definition.rs +++ b/tooling/lsp/src/requests/goto_definition.rs @@ -196,4 +196,9 @@ mod goto_definition_tests { ) .await; } + + #[test] + async fn goto_for_local_variable() { + expect_goto_for_all_references("local_variable", "some_var", 0).await; + } } diff --git a/tooling/lsp/src/requests/references.rs b/tooling/lsp/src/requests/references.rs index d35ec4a86d8..f8c23632936 100644 --- a/tooling/lsp/src/requests/references.rs +++ b/tooling/lsp/src/requests/references.rs @@ -13,7 +13,7 @@ pub(crate) fn on_references_request( ) -> impl Future>, ResponseError>> { let result = process_request(state, params.text_document_position, |location, interner, files| { - interner.find_all_references(location, params.context.include_declaration).map( + interner.find_all_references(location, params.context.include_declaration, true).map( |locations| { locations .iter() diff --git a/tooling/lsp/src/requests/rename.rs b/tooling/lsp/src/requests/rename.rs index 67853c12b81..ac6c6792e15 100644 --- a/tooling/lsp/src/requests/rename.rs +++ b/tooling/lsp/src/requests/rename.rs @@ -7,6 +7,7 @@ use async_lsp::ResponseError; use lsp_types::{ PrepareRenameResponse, RenameParams, TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit, }; +use noirc_frontend::node_interner::ReferenceId; use crate::LspState; @@ -17,7 +18,13 @@ pub(crate) fn on_prepare_rename_request( params: TextDocumentPositionParams, ) -> impl Future, ResponseError>> { let result = process_request(state, params, |location, interner, _| { - let rename_possible = interner.is_location_known(location); + let reference_id = interner.reference_at_location(location); + let rename_possible = match reference_id { + // Rename shouldn't be possible when triggered on top of "Self" + Some(ReferenceId::Reference(_, true /* is self type name */)) => false, + Some(_) => true, + None => false, + }; Some(PrepareRenameResponse::DefaultBehavior { default_behavior: rename_possible }) }); future::ready(result) @@ -29,29 +36,30 @@ pub(crate) fn on_rename_request( ) -> impl Future, ResponseError>> { let result = process_request(state, params.text_document_position, |location, interner, files| { - let rename_changes = interner.find_all_references(location, true).map(|locations| { - let rs = locations.iter().fold( - HashMap::new(), - |mut acc: HashMap>, location| { - let file_id = location.file; - let span = location.span; - - let Some(lsp_location) = to_lsp_location(files, file_id, span) else { - return acc; - }; - - let edit = TextEdit { - range: lsp_location.range, - new_text: params.new_name.clone(), - }; - - acc.entry(lsp_location.uri).or_default().push(edit); - - acc - }, - ); - rs - }); + let rename_changes = + interner.find_all_references(location, true, false).map(|locations| { + let rs = locations.iter().fold( + HashMap::new(), + |mut acc: HashMap>, location| { + let file_id = location.file; + let span = location.span; + + let Some(lsp_location) = to_lsp_location(files, file_id, span) else { + return acc; + }; + + let edit = TextEdit { + range: lsp_location.range, + new_text: params.new_name.clone(), + }; + + acc.entry(lsp_location.uri).or_default().push(edit); + + acc + }, + ); + rs + }); let response = WorkspaceEdit { changes: rename_changes, @@ -103,12 +111,19 @@ mod rename_tests { let mut changes: Vec = changes.values().flatten().map(|edit| edit.range).collect(); changes.sort_by_key(|range| (range.start.line, range.start.character)); + if changes != ranges { + let extra_in_changes: Vec<_> = + changes.iter().filter(|range| !ranges.contains(range)).collect(); + let extra_in_ranges: Vec<_> = + ranges.iter().filter(|range| !changes.contains(range)).collect(); + panic!("Rename locations did not match.\nThese renames were not found: {:?}\nThese renames should not have been found: {:?}", extra_in_ranges, extra_in_changes); + } assert_eq!(changes, ranges); } } #[test] - async fn test_on_prepare_rename_request_cannot_be_applied() { + async fn test_on_prepare_rename_request_cannot_be_applied_if_there_are_no_matches() { let (mut state, noir_text_document) = test_utils::init_lsp_server("rename_function").await; let params = TextDocumentPositionParams { @@ -126,6 +141,25 @@ mod rename_tests { ); } + #[test] + async fn test_on_prepare_rename_request_cannot_be_applied_on_self_type_name() { + let (mut state, noir_text_document) = test_utils::init_lsp_server("rename_struct").await; + + let params = TextDocumentPositionParams { + text_document: lsp_types::TextDocumentIdentifier { uri: noir_text_document }, + position: lsp_types::Position { line: 11, character: 24 }, // At "Self" + }; + + let response = on_prepare_rename_request(&mut state, params) + .await + .expect("Could not execute on_prepare_rename_request"); + + assert_eq!( + response, + Some(PrepareRenameResponse::DefaultBehavior { default_behavior: false }) + ); + } + #[test] async fn test_rename_function() { check_rename_succeeds("rename_function", "another_function").await; @@ -150,4 +184,19 @@ mod rename_tests { async fn test_rename_trait() { check_rename_succeeds("rename_trait", "Foo").await; } + + #[test] + async fn test_rename_type_alias() { + check_rename_succeeds("rename_type_alias", "Bar").await; + } + + #[test] + async fn test_rename_global() { + check_rename_succeeds("rename_global", "FOO").await; + } + + #[test] + async fn test_rename_local_variable() { + check_rename_succeeds("local_variable", "some_var").await; + } } diff --git a/tooling/lsp/test_programs/local_variable/Nargo.toml b/tooling/lsp/test_programs/local_variable/Nargo.toml new file mode 100644 index 00000000000..df881fb5f4d --- /dev/null +++ b/tooling/lsp/test_programs/local_variable/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "local_variable" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/lsp/test_programs/local_variable/src/main.nr b/tooling/lsp/test_programs/local_variable/src/main.nr new file mode 100644 index 00000000000..e41cbed085f --- /dev/null +++ b/tooling/lsp/test_programs/local_variable/src/main.nr @@ -0,0 +1,5 @@ +fn main() { + let mut some_var = 1; + some_var = 2; + let _ = some_var; +} diff --git a/tooling/lsp/test_programs/rename_function/src/main.nr b/tooling/lsp/test_programs/rename_function/src/main.nr index ad526f10966..7a70084276e 100644 --- a/tooling/lsp/test_programs/rename_function/src/main.nr +++ b/tooling/lsp/test_programs/rename_function/src/main.nr @@ -19,3 +19,9 @@ mod foo { crate::another_function() } } + +use foo::some_other_function as bar; + +fn x() { + bar(); +} diff --git a/tooling/lsp/test_programs/rename_global/Nargo.toml b/tooling/lsp/test_programs/rename_global/Nargo.toml new file mode 100644 index 00000000000..350c6fe5506 --- /dev/null +++ b/tooling/lsp/test_programs/rename_global/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rename_global" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/lsp/test_programs/rename_global/src/main.nr b/tooling/lsp/test_programs/rename_global/src/main.nr new file mode 100644 index 00000000000..3ae8cf4bfc3 --- /dev/null +++ b/tooling/lsp/test_programs/rename_global/src/main.nr @@ -0,0 +1,22 @@ +mod foo { + global FOO = 1; +} + +use foo::FOO; + +fn main() { + let _ = foo::FOO; + let _ = FOO; + let _: [Field; FOO] = [1]; +} + +trait WithNumber { + +} + +struct Some { +} + +impl WithNumber for Some { + +} diff --git a/tooling/lsp/test_programs/rename_struct/src/main.nr b/tooling/lsp/test_programs/rename_struct/src/main.nr index 93a0779cf3b..d9da3e75763 100644 --- a/tooling/lsp/test_programs/rename_struct/src/main.nr +++ b/tooling/lsp/test_programs/rename_struct/src/main.nr @@ -6,6 +6,12 @@ mod foo { impl Foo { fn foo() {} + + fn bar(self) {} + + fn baz() -> Self { + Self { field: 1 } + } } } } diff --git a/tooling/lsp/test_programs/rename_type_alias/Nargo.toml b/tooling/lsp/test_programs/rename_type_alias/Nargo.toml new file mode 100644 index 00000000000..1b95727ed8d --- /dev/null +++ b/tooling/lsp/test_programs/rename_type_alias/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rename_type_alias" +type = "bin" +authors = [""] + +[dependencies] diff --git a/tooling/lsp/test_programs/rename_type_alias/src/main.nr b/tooling/lsp/test_programs/rename_type_alias/src/main.nr new file mode 100644 index 00000000000..2072d9cae87 --- /dev/null +++ b/tooling/lsp/test_programs/rename_type_alias/src/main.nr @@ -0,0 +1,24 @@ +mod foo { + struct Foo { + } + + type Bar = Foo; + + mod bar { + struct Baz { + + } + } +} + +use foo::Foo; +use foo::Bar; +use foo::bar; + +fn main() { + let x: Bar = Foo {}; +} + +fn x(b: Bar) -> Bar { + b +} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index faac228cb63..3789595aa26 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -61,7 +61,7 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ /// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_NEW_FEATURE_TESTS: [&str; 7] = [ +const IGNORED_NEW_FEATURE_TESTS: [&str; 8] = [ "macros", "wildcard_type", "type_definition_annotation", @@ -69,6 +69,7 @@ const IGNORED_NEW_FEATURE_TESTS: [&str; 7] = [ "derive_impl", "comptime_traits", "comptime_slice_methods", + "unquote_multiple_items_from_annotation", ]; fn read_test_cases( diff --git a/tooling/noir_js_backend_barretenberg/src/backend.ts b/tooling/noir_js_backend_barretenberg/src/backend.ts index 96c4d13aa61..8ede6a07b50 100644 --- a/tooling/noir_js_backend_barretenberg/src/backend.ts +++ b/tooling/noir_js_backend_barretenberg/src/backend.ts @@ -10,7 +10,7 @@ import { type Barretenberg } from '@aztec/bb.js'; // minus the public inputs. const numBytesInProofWithoutPublicInputs: number = 2144; -export class BarretenbergVerifierBackend implements VerifierBackend { +export class BarretenbergBackend implements Backend, VerifierBackend { // These type assertions are used so that we don't // have to initialize `api` and `acirComposer` in the constructor. // These are initialized asynchronously in the `init` function, @@ -60,29 +60,6 @@ export class BarretenbergVerifierBackend implements VerifierBackend { } } - /** @description Verifies a proof */ - async verifyProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - await this.instantiate(); - await this.api.acirInitVerificationKey(this.acirComposer); - return await this.api.acirVerifyProof(this.acirComposer, proof); - } - - async getVerificationKey(): Promise { - await this.instantiate(); - await this.api.acirInitVerificationKey(this.acirComposer); - return await this.api.acirGetVerificationKey(this.acirComposer); - } - - async destroy(): Promise { - if (!this.api) { - return; - } - await this.api.destroy(); - } -} - -export class BarretenbergBackend extends BarretenbergVerifierBackend implements Backend { /** @description Generates a proof */ async generateProof(compressedWitness: Uint8Array): Promise { await this.instantiate(); @@ -144,4 +121,25 @@ export class BarretenbergBackend extends BarretenbergVerifierBackend implements vkHash: vk[1].toString(), }; } + + /** @description Verifies a proof */ + async verifyProof(proofData: ProofData): Promise { + const proof = reconstructProofWithPublicInputs(proofData); + await this.instantiate(); + await this.api.acirInitVerificationKey(this.acirComposer); + return await this.api.acirVerifyProof(this.acirComposer, proof); + } + + async getVerificationKey(): Promise { + await this.instantiate(); + await this.api.acirInitVerificationKey(this.acirComposer); + return await this.api.acirGetVerificationKey(this.acirComposer); + } + + async destroy(): Promise { + if (!this.api) { + return; + } + await this.api.destroy(); + } } diff --git a/tooling/profiler/src/opcode_formatter.rs b/tooling/profiler/src/opcode_formatter.rs index aba92c95d85..a33de42a0ff 100644 --- a/tooling/profiler/src/opcode_formatter.rs +++ b/tooling/profiler/src/opcode_formatter.rs @@ -1,6 +1,6 @@ use acir::circuit::{directives::Directive, opcodes::BlackBoxFuncCall, Opcode}; -fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { +fn format_blackbox_function(call: &BlackBoxFuncCall) -> String { match call { BlackBoxFuncCall::AES128Encrypt { .. } => "aes128_encrypt".to_string(), BlackBoxFuncCall::AND { .. } => "and".to_string(), From 293fdbd2c4951356542861ddfa1745ea45ea1839 Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 10 Jul 2024 12:38:40 +0100 Subject: [PATCH 2/7] chore: bump bb --- scripts/install_bb.sh | 2 +- tooling/noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/install_bb.sh b/scripts/install_bb.sh index b0d55b6ff1d..95dcfdda880 100755 --- a/scripts/install_bb.sh +++ b/scripts/install_bb.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION="0.43.0" +VERSION="0.46.1" BBUP_PATH=~/.bb/bbup diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index b0c9e85315a..12793d70545 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/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": "0.43.0", + "@aztec/bb.js": "0.46.1", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index 73dfbf6e82e..dc033a7344f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.43.0": - version: 0.43.0 - resolution: "@aztec/bb.js@npm:0.43.0" +"@aztec/bb.js@npm:0.46.1": + version: 0.46.1 + resolution: "@aztec/bb.js@npm:0.46.1" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 63d2617529e00a05e1ac9364639dc10761e50cb6a16e010ac6354011440de037112a82d7cdd29a65b139af528c7d865b047e157b25d15ac36ff701863d550a5b + checksum: 9475388f994e430ab3282a2c9769cd116f334358049955ed520f467d1abec8237bfeae7fa2fed9cd292f24c09c466e32e2af2d0a5cec2d10cc0c727728d96b0d languageName: node linkType: hard @@ -4396,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": 0.43.0 + "@aztec/bb.js": 0.46.1 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From f9d35105899e15c0d4dd6110313c4a1e66af6342 Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 10 Jul 2024 12:42:00 +0100 Subject: [PATCH 3/7] . --- acvm-repo/acir/src/circuit/mod.rs | 2 +- acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs | 2 +- acvm-repo/acir/tests/test_program_serialization.rs | 2 +- acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index 115ecbf7992..5d749e709b3 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -360,7 +360,7 @@ mod tests { use std::collections::BTreeSet; use super::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, }; use crate::{ diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 7c560a0a346..6478f0c7a19 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -520,7 +520,7 @@ mod tests { use crate::{circuit::Opcode, native_types::Witness}; use acir_field::{AcirField, FieldElement}; - use super::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}; + use super::{BlackBoxFuncCall, FunctionInput}; fn keccakf1600_opcode() -> Opcode { let inputs: Box<[FunctionInput; 25]> = diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 3a42cc41d47..3047ac002e8 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -14,7 +14,7 @@ use std::collections::BTreeSet; use acir::{ circuit::{ brillig::{BrilligBytecode, BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockId, ConstantOrWitnessEnum, FunctionInput, MemOp}, + opcodes::{BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, diff --git a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 87a026148d7..b03b6715abe 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -150,7 +150,7 @@ mod tests { use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ circuit::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, ExpressionWidth, Opcode, PublicInputs, }, native_types::{Expression, Witness}, From b292281a8222a72b527bb6afd1430bddff4111af Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 10 Jul 2024 12:44:10 +0100 Subject: [PATCH 4/7] chore: remove unwanted stdlib method --- noir_stdlib/src/embedded_curve_ops.nr | 5 ----- 1 file changed, 5 deletions(-) diff --git a/noir_stdlib/src/embedded_curve_ops.nr b/noir_stdlib/src/embedded_curve_ops.nr index c791ac88404..0efe351bf4f 100644 --- a/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir_stdlib/src/embedded_curve_ops.nr @@ -60,11 +60,6 @@ impl EmbeddedCurveScalar { EmbeddedCurveScalar { lo, hi } } - pub fn derive_public_key(self) -> EmbeddedCurvePoint { - let public_key = fixed_base_scalar_mul(self.lo, self.hi); - EmbeddedCurvePoint { x: public_key[0], y: public_key[1], is_infinite: false } - } - #[field(bn254)] fn from_field(scalar: Field) -> EmbeddedCurveScalar { let (a,b) = crate::field::bn254::decompose(scalar); From 9f9d8a4ed740c7912242ccaea91f3367968afbc2 Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 10 Jul 2024 12:45:59 +0100 Subject: [PATCH 5/7] . --- noir_stdlib/src/embedded_curve_ops.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir_stdlib/src/embedded_curve_ops.nr b/noir_stdlib/src/embedded_curve_ops.nr index 0efe351bf4f..883ff479504 100644 --- a/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir_stdlib/src/embedded_curve_ops.nr @@ -68,8 +68,8 @@ impl EmbeddedCurveScalar { } impl Eq for EmbeddedCurveScalar { - fn eq(self, key: EmbeddedCurveScalar) -> bool { - (key.hi == self.hi) & (key.lo == self.lo) + fn eq(self, other: Self) -> bool { + (other.hi == self.hi) & (other.lo == self.lo) } } From 184da9c1e7d1c7ba08035980137ed1bd0d4a6c7d Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 10 Jul 2024 12:54:19 +0100 Subject: [PATCH 6/7] . --- noir_stdlib/src/embedded_curve_ops.nr | 4 ---- 1 file changed, 4 deletions(-) diff --git a/noir_stdlib/src/embedded_curve_ops.nr b/noir_stdlib/src/embedded_curve_ops.nr index 883ff479504..e114522f0a3 100644 --- a/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir_stdlib/src/embedded_curve_ops.nr @@ -8,10 +8,6 @@ struct EmbeddedCurvePoint { } impl EmbeddedCurvePoint { - fn new(x: Field, y: Field, is_infinite: bool) -> Self { - Self { x, y, is_infinite } - } - fn double(self) -> EmbeddedCurvePoint { embedded_curve_add(self, self) } From 8b127cddb7da14de0f3b3ebfd92cfd608c865bad Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 10 Jul 2024 18:54:12 +0100 Subject: [PATCH 7/7] . --- tooling/noir_js_backend_barretenberg/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 3628fb93bdd..12793d70545 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/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": "0.45.1", + "@aztec/bb.js": "0.46.1", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" },