diff --git a/.noir-sync-commit b/.noir-sync-commit index 301925b6bb1..c303f36d632 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -86c151aa43ba990a0e900995064a88fc2ae6637d +e29d4b3646f0527fc01bc4584ee33616db922c72 diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index aff8007f83f..7e8f33c0396 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -11,9 +11,9 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, /** * Authenticaion witness helper library * - * Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties + * Authentication Witness is a scheme for authenticating actions on Aztec, so users can allow third-parties * (e.g. protocols or other users) to execute an action on their behalf. - * + * * This library provides helper functions to manage such witnesses. * The authentication witness, is some "witness" (data) that authenticates a `message_hash`. * The simplest example of an authentication witness, is a signature. The signature is the "evidence", @@ -28,23 +28,23 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, * - version: the version of the chain that the message is being consumed on, * - inner_hash: the hash of the "inner" message that is being consumed, this is the "actual" message or action. * - * While the `inner_hash` could be anything, such as showing you signed a specific message, it will often be - * a hash of the "action" to approve, along with who made the call. As part of this library, we provide a few + * While the `inner_hash` could be anything, such as showing you signed a specific message, it will often be + * a hash of the "action" to approve, along with who made the call. As part of this library, we provide a few * helper functions to deal with such messages. * * For example, we provide helper function that is used for checking that the message is an encoding of the current call. - * This can be used to let some contract "allow" another contract to act on its behalf, as long as it can + * This can be used to let some contract "allow" another contract to act on its behalf, as long as it can * show that it is acting on behalf of the contract. * - * If we take a case of allowing a contract to transfer tokens on behalf of an account, the `inner_hash` can be + * If we take a case of allowing a contract to transfer tokens on behalf of an account, the `inner_hash` can be * derived as: * inner_hash = hash(caller, "transfer", hash(to, amount)) - * + * * Where the `caller` would be the address of the contract that is trying to transfer the tokens, and `to` and `amount` - * the arguments for the transfer. - * + * the arguments for the transfer. + * * Note that we have both a `caller` and a `consumer`, the `consumer` will be the contract that is consuming the message, - * in the case of the transfer, it would be the `Token` contract itself, while the caller, will be the actor that is + * in the case of the transfer, it would be the `Token` contract itself, while the caller, will be the actor that is * allowed to transfer the tokens. * * @@ -52,30 +52,30 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, * is executed on the user's device, so we can use `oracles` to "ask" the user (not contract) for information. In public * we cannot do this, since it is executed by the sequencer (someone else). Therefore we can instead use a "registry" * to store the messages that we have approved. - * - * A simple example would be a "token" that is being "pulled" from one account into another. We will first outline - * how this would look in private, and then in public later. - * - * Say that a user `Alice` wants to deposit some tokens into a DeFi protocol (say a DEX). - * `Alice` would make a `deposit` transaction, that she is executing using her account contract. - * The account would call the `DeFi` contract to execute `deposit`, which would try to pull funds from the `Token` - * contract. Since the `DeFi` contract is trying to pull funds from an account that is not its own, it needs to - * convince the `Token` contract that it is allowed to do so. - * - * This is where the authentication witness comes in The `Token` contract computes a `message_hash` from the - * `transfer` call, and then asks `Alice Account` contract to verify that the `DeFi` contract is allowed to + * + * A simple example would be a "token" that is being "pulled" from one account into another. We will first outline + * how this would look in private, and then in public later. + * + * Say that a user `Alice` wants to deposit some tokens into a DeFi protocol (say a DEX). + * `Alice` would make a `deposit` transaction, that she is executing using her account contract. + * The account would call the `DeFi` contract to execute `deposit`, which would try to pull funds from the `Token` + * contract. Since the `DeFi` contract is trying to pull funds from an account that is not its own, it needs to + * convince the `Token` contract that it is allowed to do so. + * + * This is where the authentication witness comes in The `Token` contract computes a `message_hash` from the + * `transfer` call, and then asks `Alice Account` contract to verify that the `DeFi` contract is allowed to * execute that call. - * - * `Alice Account` contract can then ask `Alice` if she wants to allow the `DeFi` contract to pull funds from her + * + * `Alice Account` contract can then ask `Alice` if she wants to allow the `DeFi` contract to pull funds from her * account. If she does, she will sign the `message_hash` and return the signature to the `Alice Account` which * will validate it and return success to the `Token` contract which will then allow the `DeFi` contract to pull * funds from `Alice`. - * + * * To ensure that the same "approval" cannot be used multiple times, we also compute a `nullifier` for the * authentication witness, and emit it from the `Token` contract (consumer). - * - * Note that we can do this flow as we are in private were we can do oracle calls out from contracts. - * + * + * Note that we can do this flow as we are in private were we can do oracle calls out from contracts. + * * * Person Contract Contract Contract * Alice Alice Account Token DeFi @@ -116,10 +116,10 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, * | | | | * | | | | * - * - * If we instead were in public, we cannot do the same flow. Instead we would use an authentication registry to store - * the messages that we have approved. - * + * + * If we instead were in public, we cannot do the same flow. Instead we would use an authentication registry to store + * the messages that we have approved. + * * To approve a message, `Alice Account` can make a `set_authorized` call to the registry, to set a `message_hash` * as authorized. This is essentially a mapping from `message_hash` to `true` for `Alice Contract`. Every account * has its own map in the registry, so `Alice` cannot approve a message for `Bob`. @@ -172,31 +172,31 @@ use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, * --- FAQ --- * Q: Why are we using a success flag of `keccak256("IS_VALID()")` instead of just returning a boolean? * A: We want to make sure that we don't accidentally return `true` if there is a collision in the function selector. - * By returning a hash of `IS_VALID()`, it becomes very unlikely that there is both a colission and we return + * By returning a hash of `IS_VALID()`, it becomes very unlikely that there is both a collision and we return * a success flag. - * + * * Q: Why are we using static calls? * A: We are using static calls to ensure that the account contract cannot re-enter. If it was a normal call, it * could make a new call and do a re-entry attack. Using a static ensures that it cannot update any state. * * Q: Would it not be cheaper to use a nullifier instead of updating state in public? - * A: At a quick glance, a public state update + nullifier is 96 bytes, but two state updates are 128, so it would be - * cheaper to use a nullifier, if this is the way it would always be done. However, if both the approval and the + * A: At a quick glance, a public state update + nullifier is 96 bytes, but two state updates are 128, so it would be + * cheaper to use a nullifier, if this is the way it would always be done. However, if both the approval and the * consumption is done in the same transaction, then we will be able to squash the updates (only final tx state diff is posted to DA), and now it is cheaper. - * + * * Q: Why is the chain id and the version part of the message hash? * A: The chain id and the version is part of the message hash to ensure that the message is only valid on a specific - * chain to avoid a case where the same message could be used across multiple chains. + * chain to avoid a case where the same message could be used across multiple chains. */ global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID()") -/** +/** * Assert that `on_behalf_of` have authorized the current call with a valid authentication witness - * - * Computing the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then making a call out to the + * + * Computing the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then making a call out to the * `on_behalf_of` contract to verify that the `inner_hash` is valid. - * + * * @param on_behalf_of The address that have authorized the current call */ // docs:start:assert_current_call_valid_authwit @@ -206,14 +206,14 @@ pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf } // docs:end:assert_current_call_valid_authwit -/** +/** * Assert that a specific `inner_hash` is valid for the `on_behalf_of` address - * + * * Used as an internal function for `assert_current_call_valid_authwit` and can be used as a standalone function when * the `inner_hash` is from a different source, e.g., say a block of text etc. - * + * * @param on_behalf_of The address that have authorized the current call - * @param inner_hash The hash of the message to authorize + * @param inner_hash The hash of the message to authorize */ pub fn assert_inner_hash_valid_authwit(context: &mut PrivateContext, on_behalf_of: AztecAddress, inner_hash: Field) { // We perform a static call here and not a standard one to ensure that the account contract cannot re-enter. @@ -229,15 +229,15 @@ pub fn assert_inner_hash_valid_authwit(context: &mut PrivateContext, on_behalf_o context.push_nullifier(nullifier); } -/** +/** * Assert that `on_behalf_of` have authorized the current call in the authentication registry - * - * Computing the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then making a call out to the + * + * Computing the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then making a call out to the * `on_behalf_of` contract to verify that the `inner_hash` is valid. - * + * * Note that the authentication registry will take the `msg_sender` into account as the consumer, so this will only * work if the `msg_sender` is the same as the `consumer` when the `message_hash` was inserted into the registry. - * + * * @param on_behalf_of The address that have authorized the current call */ // docs:start:assert_current_call_valid_authwit_public @@ -249,15 +249,15 @@ pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_ } // docs:end:assert_current_call_valid_authwit_public -/** +/** * Assert that `on_behalf_of` have authorized a speicifc `inner_hash` in the authentication registry - * - * Computing the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then making a call out to the + * + * Computing the `inner_hash` using the `msg_sender`, `selector` and `args_hash` and then making a call out to the * `on_behalf_of` contract to verify that the `inner_hash` is valid. * * Note that the authentication registry will take the `msg_sender` into account as the consumer, so this will only * work if the `msg_sender` is the same as the `consumer` when the `message_hash` was inserted into the registry. - * + * * @param on_behalf_of The address that have authorized the `inner_hash` */ pub fn assert_inner_hash_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress, inner_hash: Field) { @@ -271,12 +271,12 @@ pub fn assert_inner_hash_valid_authwit_public(context: &mut PublicContext, on_be } /** - * Compute the `message_hash` from a function call to be used by an authentication witness - * + * Compute the `message_hash` from a function call to be used by an authentication witness + * * Useful for when you need a non-account contract to approve during execution. For example if you need a contract * to make a call to nested contract, e.g., contract A wants to exit token T to L1 using bridge B, so it needs to allow * B to transfer T on its behalf. - * + * * @param caller The address of the contract that is calling the function, in the example above, this would be B * @param consumer The address of the contract that is consuming the message, in the example above, this would be T * @param chain_id The chain id of the chain that the message is being consumed on @@ -301,21 +301,21 @@ pub fn compute_authwit_message_hash_from_call( /** * Computes the `inner_hash` of the authentication witness - * + * * This is used internally, but also useful in cases where you want to compute the `inner_hash` for a specific message * that is not necessarily a call, but just some "bytes" or text. - * + * * @param args The arguments to hash */ pub fn compute_inner_authwit_hash(args: [Field; N]) -> Field { poseidon2_hash_with_separator(args, GENERATOR_INDEX__AUTHWIT_INNER) } -/** +/** * Computs the `authwit_nullifier` for a specific `on_behalf_of` and `inner_hash` - * + * * Using the `on_behalf_of` and the `inner_hash` to ensure that the nullifier is siloed for a specific `on_behalf_of`. - * + * * @param on_behalf_of The address that have authorized the `inner_hash` * @param inner_hash The hash of the message to authorize */ @@ -328,7 +328,7 @@ pub fn compute_authwit_nullifier(on_behalf_of: AztecAddress, inner_hash: Field) /** * Computes the `message_hash` for the authentication witness - * + * * @param consumer The address of the contract that is consuming the message * @param chain_id The chain id of the chain that the message is being consumed on * @param version The version of the chain that the message is being consumed on @@ -348,9 +348,9 @@ pub fn compute_authwit_message_hash(consumer: AztecAddress, chain_id: Field, ver /** * Helper function to set the authorization status of a message hash - * + * * Wraps a public call to the authentication registry to set the authorization status of a `message_hash` - * + * * @param message_hash The hash of the message to authorize * @param authorize True if the message should be authorized, false if it should be revoked */ @@ -365,10 +365,10 @@ pub fn set_authorized(context: &mut PublicContext, message_hash: Field, authoriz /** * Helper function to reject all authwits - * + * * Wraps a public call to the authentication registry to set the `reject_all` flag * - * @param reject True if all authwits should be rejected, false otherwise + * @param reject True if all authwits should be rejected, false otherwise */ pub fn set_reject_all(context: &mut PublicContext, reject: bool) { context.call_public_function( diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr index 93f6e076b5b..e220c014f8f 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable.nr @@ -123,6 +123,7 @@ impl SharedMutable SharedMutable where T: ToField + FromField + Eq { + pub fn schedule_value_change(self, new_value: T) { let mut value_change = self.read_value_change(); let delay_change = self.read_delay_change(); diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index b9c578f00fd..13b41096b62 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -132,7 +132,7 @@ impl TestEnvironment { PackedReturns::new(public_inputs.returns_hash).unpack_into() } - fn call_private_void( + fn call_private_void( _self: Self, call_interface: C ) where C: CallInterface { @@ -154,7 +154,7 @@ impl TestEnvironment { PackedReturns::new(public_inputs.returns_hash).assert_empty(); } - fn call_public( + fn call_public( _self: Self, call_interface: C ) -> T where C: CallInterface { @@ -179,7 +179,7 @@ impl TestEnvironment { result } - fn assert_public_call_fails( + fn assert_public_call_fails( _self: Self, call_interface: C ) where C: CallInterface { @@ -190,7 +190,7 @@ impl TestEnvironment { ); } - fn assert_private_call_fails( + fn assert_private_call_fails( _self: Self, call_interface: C ) where C: CallInterface { diff --git a/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-reset/src/main.nr b/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-reset/src/main.nr index 7c81aeb8914..6e977352ce1 100644 --- a/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-reset/src/main.nr +++ b/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-reset/src/main.nr @@ -1,7 +1,4 @@ -use dep::mock_types::{ - PrivateKernelPublicInputs, PrivateKernelPublicInputsBuilder, MAX_COMMITMENT_READ_REQUESTS_PER_TX, - MAX_COMMITMENTS_PER_TX -}; +use dep::mock_types::{PrivateKernelPublicInputs, MAX_COMMITMENT_READ_REQUESTS_PER_TX, MAX_COMMITMENTS_PER_TX}; // Mock reset kernel that reset read requests. // It needs hints to locate the commitment that matches the read requests. diff --git a/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-tail/src/main.nr b/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-tail/src/main.nr index df0e5a85401..06cb0fdc965 100644 --- a/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-tail/src/main.nr +++ b/noir-projects/mock-protocol-circuits/crates/mock-private-kernel-tail/src/main.nr @@ -1,7 +1,4 @@ -use dep::mock_types::{ - PrivateKernelPublicInputs, PrivateKernelPublicInputsBuilder, KernelPublicInputs, - MAX_COMMITMENT_READ_REQUESTS_PER_TX -}; +use dep::mock_types::{PrivateKernelPublicInputs, KernelPublicInputs, MAX_COMMITMENT_READ_REQUESTS_PER_TX}; // The tail kernel finishes the client IVC chain exposing the final public inputs with no remaining calls or unfulfilled read requests. fn main(prev_kernel_public_inputs: PrivateKernelPublicInputs) -> pub KernelPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr index 0beb838941c..7897c85c1e8 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/reset_output_validator.nr @@ -15,7 +15,7 @@ struct ResetOutputValidator< let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, let NLL_RR_SETTLED: u32, - let KEY_VALIDATION_REQUESTS: u32, + let KEY_VALIDATION_REQUESTS: u32, let NUM_TRANSIENT_DATA_INDEX_HINTS: u32 > { output: PrivateKernelCircuitPublicInputs, @@ -33,7 +33,7 @@ impl< let NH_RR_SETTLED: u32, let NLL_RR_PENDING: u32, let NLL_RR_SETTLED: u32, - let KEY_VALIDATION_REQUESTS: u32, + let KEY_VALIDATION_REQUESTS: u32, let NUM_TRANSIENT_DATA_INDEX_HINTS: u32 > ResetOutputValidator { pub fn new( @@ -182,9 +182,9 @@ impl< fn validate_sorted_note_logs(self) { // This will be called together with validate_sorted_siloed_note_hashes(). - // Same as validate_sorted_siloed_note_hashes, it will only be run once. + // Same as validate_sorted_siloed_note_hashes, it will only be run once. // This is fine because we don't allow emitting logs for notes emitted in another function at the moment. - // All the note logs emitted in a function call must link to note hashes emitted in the same call. + // All the note logs emitted in a function call must link to note hashes emitted in the same call. // This is checked in PrivateCallDataValidator > validate_note_logs. // note_hash_counter was used when squashing the note log along with its corresponding note hash. diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr index 1b1ae044ca0..f94339c133f 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -149,12 +149,12 @@ mod tests { encrypted_log_siloing_amount: u32, } - impl PrivateKernelResetInputsBuilder { - pub fn new() -> PrivateKernelResetInputsBuilder<6> { + impl PrivateKernelResetInputsBuilder<6> { + pub fn new() -> Self { let mut previous_kernel = FixtureBuilder::new().in_vk_tree(PRIVATE_KERNEL_INNER_INDEX); previous_kernel.set_first_nullifier(); - PrivateKernelResetInputsBuilder { + Self { previous_kernel, transient_data_index_hints: [TransientDataIndexHint::nada(MAX_NULLIFIERS_PER_TX, MAX_NOTE_HASHES_PER_TX); 6], note_hash_read_request_hints_builder: NoteHashReadRequestHintsBuilder::new(), @@ -165,7 +165,9 @@ mod tests { encrypted_log_siloing_amount: 0 } } + } + impl PrivateKernelResetInputsBuilder { pub fn with_siloing(&mut self) -> Self { self.note_hash_siloing_amount = 6; self.nullifier_siloing_amount = 6; diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index 3c95ad18bad..71542abe6a6 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -98,7 +98,7 @@ mod tests { utils::arrays::{array_length, array_merge}, merkle_tree::MembershipWitness }; - fn build_nullifier_tree() -> NonEmptyMerkleTree { + fn build_nullifier_tree() -> NonEmptyMerkleTree { let mut pre_existing_nullifiers = [NullifierLeafPreimage::empty(); MAX_NULLIFIERS_PER_TX]; pre_existing_nullifiers[0] = NullifierLeafPreimage { nullifier: 0, next_nullifier: 100, next_index: 1 }; pre_existing_nullifiers[1] = NullifierLeafPreimage { nullifier: 100, next_nullifier: 0, next_index: 0 }; @@ -118,7 +118,7 @@ mod tests { settled_public_data_leaves } - fn build_public_data_tree() -> NonEmptyMerkleTree { + fn build_public_data_tree() -> NonEmptyMerkleTree { let settled_public_data_leaves = get_settled_public_data_leaves(); NonEmptyMerkleTree::new( settled_public_data_leaves.map(|preimage: PublicDataTreeLeafPreimage| preimage.hash()), diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr index dc33a0a443e..9b556268a3f 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr @@ -67,8 +67,8 @@ mod tests { leaf_preimages: [NoteHashLeafPreimage; NUM_SETTLED_READS], } - impl TestBuilder { - pub fn new() -> TestBuilder<5, 4, 3, 2> { + impl TestBuilder<5, 4, 3, 2> { + pub fn new() -> Self { let contract_address = AztecAddress::from_field(123); // Create 4 note hashes. 10 and 11 are settled. 12 and 13 are pending. @@ -119,8 +119,11 @@ mod tests { leaf_preimages } } + } + + impl TestBuilder { - fn build_tree(self) -> NonEmptyMerkleTree<2, NOTE_HASH_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT - 1, 1> { + fn build_tree(self) -> NonEmptyMerkleTree<2, NOTE_HASH_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT - 1, 1> { NonEmptyMerkleTree::new( [self.leaf_preimages[0].as_leaf(), self.leaf_preimages[1].as_leaf()], [0; NOTE_HASH_TREE_HEIGHT], diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_read_request_reset.nr index ba11d655b1c..135099aa541 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_read_request_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/nullifier_read_request_reset.nr @@ -67,8 +67,8 @@ mod tests { leaf_preimages: [NullifierLeafPreimage; NUM_SETTLED_READS], } - impl TestBuilder { - pub fn new() -> TestBuilder<5, 4, 3, 2> { + impl TestBuilder<5, 4, 3, 2> { + pub fn new() -> Self { let contract_address = AztecAddress::from_field(123); // Create 4 nullifiers. 10 and 11 are settled. 12 and 13 are pending. @@ -112,8 +112,11 @@ mod tests { TestBuilder { read_requests, read_request_statuses, pending_values, pending_read_hints, leaf_preimages } } + } + + impl TestBuilder { - fn build_tree(self) -> NonEmptyMerkleTree<2, NULLIFIER_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT - 1, 1> { + fn build_tree(self) -> NonEmptyMerkleTree<2, NULLIFIER_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT - 1, 1> { NonEmptyMerkleTree::new( [self.leaf_preimages[0].hash(), self.leaf_preimages[1].hash()], [0; NULLIFIER_TREE_HEIGHT], diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr index 0d708dcaa79..de705e7bef5 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/mutable_data_read_request.nr @@ -138,7 +138,7 @@ mod tests { fn value(self) -> Field { self.value } - + fn override_counter(_self: Self) -> u32 { 0 } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/non_existent_read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/non_existent_read_request.nr index e91a6e267ed..c7f35b84da8 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/non_existent_read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/non_existent_read_request.nr @@ -167,7 +167,7 @@ mod tests { TestLeafPreimage { value: 10, next_value: 20 }, ]; - fn build_tree() -> NonEmptyMerkleTree<4, 3, 1, 2> { + fn build_tree() -> NonEmptyMerkleTree<4, 3, 1, 2> { NonEmptyMerkleTree::new( leaf_preimages.map(|leaf_preimage: TestLeafPreimage| leaf_preimage.as_leaf()), [0; 3], diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr index 200dcd37219..6a63c52afbe 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/read_request.nr @@ -80,8 +80,8 @@ fn validate_settled_read_requests + ReadValueHint, +) where + H: SettledReadHint + ReadValueHint, LEAF_PREIMAGE: LeafPreimage + Readable { for i in 0..NUM_SETTLED_READS { let hint = hints[i]; @@ -143,8 +143,8 @@ pub fn verify_reset_read_requests< settled_read_hints: [H; NUM_SETTLED_READS], tree_root: Field, propagated_read_requests: [ScopedReadRequest; READ_REQUEST_LEN] -) where - P: Readable, +) where + P: Readable, H: SettledReadHint + ReadValueHint, LEAF_PREIMAGE: LeafPreimage + Readable { validate_pending_read_requests(read_requests, pending_values, pending_read_hints); @@ -271,8 +271,8 @@ mod tests { leaf_preimages: [TestLeafPreimage; NUM_SETTLED_READS], } - impl TestBuilder { - pub fn new() -> TestBuilder<4, 2, 2, 2> { + impl TestBuilder<4, 2, 2, 2> { + pub fn new() -> Self { let contract_address = AztecAddress::from_field(123); // Create 4 values. 10 and 11 are settled. 12 and 13 are pending. @@ -310,8 +310,10 @@ mod tests { TestBuilder { read_requests, read_request_statuses, pending_values, pending_read_hints, leaf_preimages } } + } - fn build_tree(self) -> NonEmptyMerkleTree<2, 3, 2, 1> { + impl TestBuilder { + fn build_tree(self) -> NonEmptyMerkleTree<2, 3, 2, 1> { NonEmptyMerkleTree::new( [self.leaf_preimages[0].as_leaf(), self.leaf_preimages[1].as_leaf()], [0; 3], diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr index 70e62ab3d1c..62e81931aaa 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/reset/transient_data.nr @@ -99,7 +99,7 @@ pub fn verify_squashed_transient_data_with_hint_indexes< // - For non-empty logs, their note_hash_counter can't be 0. It must match the counter of a note hash, which is verified in private_call_data_validator. // Therefor, is_propagated will be false if index_hint incorrectly points to an empty expected_note_hash and fails the check inside the else statement. // - For empty logs, we expect is_propagated to be true, making them part of the note_logs_kept. - // index_hint might point them to the same empty note. This doesn't matter because we increment the value of + // index_hint might point them to the same empty note. This doesn't matter because we increment the value of // note_logs_kept and ensure that the correct number of empty logs are present in the expected_note_logs. let is_propagated = expected_note_hashes[hint_index].counter() == log.note_hash_counter; let expected_index = if is_propagated { @@ -203,8 +203,8 @@ mod tests { split_counter: u32, } - impl TestDataBuilder { - pub fn new() -> TestDataBuilder<5, 4, 3, 2> { + impl TestDataBuilder<5, 4, 3, 2> { + pub fn new() -> Self { let note_hashes = [ NoteHash { value: 11, counter: 100 }.scope(contract_address), NoteHash { value: 22, counter: 200 }.scope(contract_address), @@ -251,8 +251,10 @@ mod tests { split_counter: 0 } } + } - pub fn new_clear_all() -> TestDataBuilder<3, 3, 4, 3> { + impl TestDataBuilder<3, 3, 4, 3> { + pub fn new_clear_all() -> Self { let note_hashes = [ NoteHash { value: 11, counter: 100 }.scope(contract_address), NoteHash { value: 22, counter: 200 }.scope(contract_address), @@ -296,8 +298,10 @@ mod tests { split_counter: 0 } } + } - pub fn new_identical_note_hashes() -> TestDataBuilder<3, 3, 4, 3> { + impl TestDataBuilder<3, 3, 4, 3> { + pub fn new_identical_note_hashes() -> Self { let note_hashes = [ NoteHash { value: 11, counter: 100 }.scope(contract_address), NoteHash { value: 11, counter: 200 }.scope(contract_address), @@ -340,6 +344,9 @@ mod tests { split_counter: 0 } } + } + + impl TestDataBuilder { pub fn get_nada_index_hint(_self: Self) -> TransientDataIndexHint { TransientDataIndexHint { nullifier_index: NUM_NULLIFIERS, note_hash_index: NUM_NOTE_HASHES } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr index b167fa32ebc..51665f85ba4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr @@ -34,7 +34,7 @@ pub fn assert_check_non_membership( low_leaf_preimage: LEAF_PREIMAGE, low_leaf_membership_witness: MembershipWitness, tree_root: Field -) where +) where LEAF_PREIMAGE: IndexedTreeLeafPreimage { assert_check_valid_low_leaf(key, low_leaf_preimage); @@ -55,7 +55,7 @@ pub fn conditionally_assert_check_membership, tree_root: Field -) where +) where LEAF_PREIMAGE: IndexedTreeLeafPreimage { if exists { assert(key == leaf_preimage.get_key(), "Key does not match the key of the leaf preimage"); @@ -120,7 +120,7 @@ mod tests { TestLeafPreimage { value: 30, next_value: 40 }, ]; - fn build_tree() -> NonEmptyMerkleTree<4, 3, 1, 2> { + fn build_tree() -> NonEmptyMerkleTree<4, 3, 1, 2> { NonEmptyMerkleTree::new( leaf_preimages.map(|leaf_preimage: TestLeafPreimage| leaf_preimage.as_leaf()), [0; 3], diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/merkle_tree.nr b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/merkle_tree.nr index 57a13a96e33..f4b6bc3e8a0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/merkle_tree.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/merkle_tree.nr @@ -50,9 +50,9 @@ impl MerkleTree { let mut current_index = leaf_index; let mut subtree_width = N; - let mut sibling_index = MerkleTree::sibling_index(current_index); + let mut current_sibling_index = sibling_index(current_index); - path[0] = self.leaves[sibling_index]; + path[0] = self.leaves[current_sibling_index]; let mut subtree_offset: u32 = 0; @@ -60,9 +60,9 @@ impl MerkleTree { current_index = current_index / 2; subtree_width = subtree_width / 2; - sibling_index = MerkleTree::sibling_index(current_index); + current_sibling_index = sibling_index(current_index); - path[i] = self.nodes[subtree_offset + sibling_index]; + path[i] = self.nodes[subtree_offset + current_sibling_index]; subtree_offset += subtree_width; } @@ -71,3 +71,7 @@ impl MerkleTree { } } +pub fn sibling_index(index: u32) -> u32 { + if index % 2 == 0 { index + 1 } else { index - 1 } +} + diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr index 373ba7342ec..0a4cda20b8c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/merkle_tree_utils.nr @@ -1,4 +1,4 @@ -use crate::{merkle_tree::{MerkleTree, calculate_empty_tree_root}, traits::Empty, hash::merkle_hash}; +use crate::{merkle_tree::{merkle_tree, MerkleTree, calculate_empty_tree_root}, traits::Empty, hash::merkle_hash}; pub fn compute_zero_hashes(mut hashes: [Field; N]) -> [Field; N] { hashes[0] = merkle_hash(0, 0); @@ -14,7 +14,7 @@ impl MerkleTree { fn update_leaf(&mut self, index: u32, value: Field, _tree_height: [Field; K]) { self.leaves[index] = value; - let mut sibling_index = MerkleTree::sibling_index(index); + let mut sibling_index = merkle_tree::sibling_index(index); let (mut left_node, mut right_node) = if index % 2 == 0 { (value, self.leaves[sibling_index]) } else { @@ -26,7 +26,7 @@ impl MerkleTree { let mut node_index: u32 = index / 2 + layer_offset; for _ in 0..K { self.nodes[node_index] = merkle_hash(left_node, right_node); - sibling_index = MerkleTree::sibling_index(node_index); + sibling_index = merkle_tree::sibling_index(node_index); let nodes = if node_index % 2 == 0 { (self.nodes[node_index], self.nodes[sibling_index]) } else { @@ -115,7 +115,7 @@ impl TestBuilder { - pub fn new() -> TestBuilder<7> { + impl TestBuilder<7> { + pub fn new() -> Self { let original_array_lt = [6, 3, 8, 1, 0, 0, 0]; let original_array_gte = [4, 9, 5, 0, 0, 0, 0]; let permuted_array = [5, 8, 9, 3, 1, 6, 4]; let combined_indexes = [5, 3, 1, 4, 6, 2, 0]; TestBuilder { original_array_lt, original_array_gte, permuted_array, combined_indexes } } + } - pub fn new_with_padded_zeros() -> TestBuilder<10> { + impl TestBuilder<10> { + pub fn new_with_padded_zeros() -> Self { let original_array_lt = [6, 3, 8, 1, 0, 0, 0, 0, 0, 0]; let original_array_gte = [4, 9, 5, 0, 0, 0, 0, 0, 0, 0]; let permuted_array = [5, 8, 9, 3, 1, 6, 4, 0, 0, 0]; let combined_indexes = [5, 3, 1, 4, 6, 2, 0, 7, 8, 9]; TestBuilder { original_array_lt, original_array_gte, permuted_array, combined_indexes } } + } + impl TestBuilder { pub fn verify(self) { assert_combined_permuted_array( self.original_array_lt, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr index 5839a4c53d5..6eb7869fb59 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/utils/arrays/assert_combined_sorted_transformed_value_array.nr @@ -151,8 +151,8 @@ mod tests { hints: [CombinedOrderHint; N], } - impl TestDataBuilder { - pub fn new() -> TestDataBuilder { + impl TestDataBuilder { + pub fn new() -> Self { let original_array_lt = pad_end( [ TestItem { name: 7, price: 40, tax: 7, counter: 0 }, @@ -206,8 +206,10 @@ mod tests { TestDataBuilder { original_array_lt, original_array_gte, sorted_transformed_value_array, hints } } + } - pub fn new_without_prepended() -> TestDataBuilder { + impl TestDataBuilder { + pub fn new_without_prepended() -> Self { let original_array_lt = pad_end( [ TestItem { name: 6, price: 80, tax: 1, counter: 22 }, @@ -249,7 +251,9 @@ mod tests { TestDataBuilder { original_array_lt, original_array_gte, sorted_transformed_value_array, hints } } + } + impl TestDataBuilder { pub fn swap_items(&mut self, from: u32, to: u32) { let tmp = self.sorted_transformed_value_array[from]; self.sorted_transformed_value_array[from] = self.sorted_transformed_value_array[to]; diff --git a/noir/bb-version b/noir/bb-version index a758a09aae5..1e0c609c978 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.48.0 +0.51.1 diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index f78fbfede27..cd936e4bca2 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -451,6 +451,7 @@ version = "0.33.0" dependencies = [ "acvm", "convert_case 0.6.0", + "im", "iter-extended", "noirc_errors", "noirc_frontend", @@ -3004,6 +3005,7 @@ version = "0.33.0" dependencies = [ "acvm", "bn254_blackbox_solver", + "cfg-if 1.0.0", "chrono", "fxhash", "im", @@ -3012,6 +3014,7 @@ dependencies = [ "noirc_frontend", "num-bigint", "proptest", + "rayon", "serde", "serde_json", "serde_with", diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 52cb1012b71..a903ef6fec9 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -130,7 +130,7 @@ criterion = "0.5.0" # https://github.com/tikv/pprof-rs/pull/172 pprof = { version = "0.13", features = ["flamegraph", "criterion"] } - +cfg-if = "1.0.0" dirs = "4" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0" @@ -154,6 +154,7 @@ color-eyre = "0.6.2" rand = "0.8.5" proptest = "1.2.0" proptest-derive = "0.4.0" +rayon = "1.8.0" im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" diff --git a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml index c1cffc1334e..acc34457bc9 100644 --- a/noir/noir-repo/acvm-repo/acir_field/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir_field/Cargo.toml @@ -24,7 +24,7 @@ ark-bn254.workspace = true ark-bls12-381 = { workspace = true, optional = true } ark-ff.workspace = true -cfg-if = "1.0.0" +cfg-if.workspace = true [dev-dependencies] proptest.workspace = true diff --git a/noir/noir-repo/aztec_macros/Cargo.toml b/noir/noir-repo/aztec_macros/Cargo.toml index c9d88e36e28..258379cd7b8 100644 --- a/noir/noir-repo/aztec_macros/Cargo.toml +++ b/noir/noir-repo/aztec_macros/Cargo.toml @@ -18,5 +18,6 @@ noirc_frontend.workspace = true noirc_errors.workspace = true iter-extended.workspace = true convert_case = "0.6.0" +im.workspace = true regex = "1.10" tiny-keccak = { version = "2.0.0", features = ["keccak"] } diff --git a/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs index 4c6cbb10d9f..f2998fbaafc 100644 --- a/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs @@ -218,7 +218,10 @@ fn empty_statement(statement: &mut Statement) { StatementKind::For(for_loop_statement) => empty_for_loop_statement(for_loop_statement), StatementKind::Comptime(statement) => empty_statement(statement), StatementKind::Semi(expression) => empty_expression(expression), - StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + StatementKind::Break + | StatementKind::Continue + | StatementKind::Interned(_) + | StatementKind::Error => (), } } @@ -271,12 +274,15 @@ fn empty_expression(expression: &mut Expression) { ExpressionKind::Unsafe(block_expression, _span) => { empty_block_expression(block_expression); } - ExpressionKind::Quote(..) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), ExpressionKind::AsTraitPath(path) => { empty_unresolved_type(&mut path.typ); empty_path(&mut path.trait_path); empty_ident(&mut path.impl_item); } + ExpressionKind::Quote(..) + | ExpressionKind::Resolved(_) + | ExpressionKind::Interned(_) + | ExpressionKind::Error => (), } } @@ -353,6 +359,7 @@ fn empty_unresolved_type(unresolved_type: &mut UnresolvedType) { | UnresolvedTypeData::Unit | UnresolvedTypeData::Quoted(_) | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Interned(_) | UnresolvedTypeData::Unspecified | UnresolvedTypeData::Error => (), } @@ -531,6 +538,7 @@ fn empty_lvalue(lvalue: &mut LValue) { empty_expression(index); } LValue::Dereference(lvalue, _) => empty_lvalue(lvalue), + LValue::Interned(..) => (), } } diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index b244018cc71..6b200e79b89 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -29,3 +29,7 @@ rust-embed.workspace = true tracing.workspace = true aztec_macros = { path = "../../aztec_macros" } + +[features] +bn254 = ["noirc_frontend/bn254", "noirc_evaluator/bn254"] +bls12_381 = ["noirc_frontend/bls12_381", "noirc_evaluator/bls12_381"] diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 9b6540ebd74..b7bb07ad64a 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -129,11 +129,18 @@ pub struct CompileOptions { /// This check should always be run on production code. #[arg(long)] pub skip_underconstrained_check: bool, +} - /// A workspace is compiled in parallel by default. This flag will compile it sequentially. - /// This flag is useful to reduce memory consumption or to avoid using rayon threads for compilation (which don't have dynamic stacks). - #[arg(long)] - pub sequential: bool, +#[derive(Clone, Debug, Default)] +pub struct CheckOptions { + pub compile_options: CompileOptions, + pub error_on_unused_imports: bool, +} + +impl CheckOptions { + pub fn new(compile_options: &CompileOptions, error_on_unused_imports: bool) -> Self { + Self { compile_options: compile_options.clone(), error_on_unused_imports } + } } pub fn parse_expression_width(input: &str) -> Result { @@ -283,8 +290,10 @@ pub fn add_dep( pub fn check_crate( context: &mut Context, crate_id: CrateId, - options: &CompileOptions, + check_options: &CheckOptions, ) -> CompilationResult<()> { + let options = &check_options.compile_options; + let macros: &[&dyn MacroProcessor] = if options.disable_macros { &[] } else { &[&aztec_macros::AztecMacro] }; @@ -294,6 +303,7 @@ pub fn check_crate( context, options.debug_comptime_in_file.as_deref(), options.arithmetic_generics, + check_options.error_on_unused_imports, macros, ); errors.extend(diagnostics.into_iter().map(|(error, file_id)| { @@ -327,7 +337,10 @@ pub fn compile_main( options: &CompileOptions, cached_program: Option, ) -> CompilationResult { - let (_, mut warnings) = check_crate(context, crate_id, options)?; + let error_on_unused_imports = true; + let check_options = CheckOptions::new(options, error_on_unused_imports); + + let (_, mut warnings) = check_crate(context, crate_id, &check_options)?; let main = context.get_main_function(&crate_id).ok_or_else(|| { // TODO(#2155): This error might be a better to exist in Nargo @@ -362,7 +375,9 @@ pub fn compile_contract( crate_id: CrateId, options: &CompileOptions, ) -> CompilationResult { - let (_, warnings) = check_crate(context, crate_id, options)?; + let error_on_unused_imports = true; + let check_options = CheckOptions::new(options, error_on_unused_imports); + let (_, warnings) = check_crate(context, crate_id, &check_options)?; // TODO: We probably want to error if contracts is empty let contracts = context.get_all_contracts(&crate_id); diff --git a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs index 3ce0f268715..b21dc759f14 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs @@ -46,7 +46,7 @@ impl CustomDiagnostic { ) -> CustomDiagnostic { CustomDiagnostic { message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], notes: Vec::new(), kind, } @@ -98,7 +98,7 @@ impl CustomDiagnostic { ) -> CustomDiagnostic { CustomDiagnostic { message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span)], + secondaries: vec![CustomLabel::new(secondary_message, secondary_span, None)], notes: Vec::new(), kind: DiagnosticKind::Bug, } @@ -113,7 +113,11 @@ impl CustomDiagnostic { } pub fn add_secondary(&mut self, message: String, span: Span) { - self.secondaries.push(CustomLabel::new(message, span)); + self.secondaries.push(CustomLabel::new(message, span, None)); + } + + pub fn add_secondary_with_file(&mut self, message: String, span: Span, file: fm::FileId) { + self.secondaries.push(CustomLabel::new(message, span, Some(file))); } pub fn is_error(&self) -> bool { @@ -153,11 +157,12 @@ impl std::fmt::Display for CustomDiagnostic { pub struct CustomLabel { pub message: String, pub span: Span, + pub file: Option, } impl CustomLabel { - fn new(message: String, span: Span) -> CustomLabel { - CustomLabel { message, span } + fn new(message: String, span: Span, file: Option) -> CustomLabel { + CustomLabel { message, span, file } } } @@ -234,7 +239,8 @@ fn convert_diagnostic( .map(|sl| { let start_span = sl.span.start() as usize; let end_span = sl.span.end() as usize; - Label::secondary(file_id, start_span..end_span).with_message(&sl.message) + let file = sl.file.unwrap_or(file_id); + Label::secondary(file, start_span..end_span).with_message(&sl.message) }) .collect() } else { diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index 81feb0b7154..1db6af2ae85 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -26,6 +26,12 @@ serde_json.workspace = true serde_with = "3.2.0" tracing.workspace = true chrono = "0.4.37" +rayon.workspace = true +cfg-if.workspace = true [dev-dependencies] -proptest.workspace = true \ No newline at end of file +proptest.workspace = true + +[features] +bn254 = ["noirc_frontend/bn254"] +bls12_381= [] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 54dbd4716a9..eeaa60b4323 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -24,7 +24,7 @@ use acvm::{acir::AcirField, FieldElement}; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use iter_extended::vecmap; use num_bigint::BigUint; -use std::rc::Rc; +use std::sync::Arc; use super::brillig_black_box::convert_black_box_call; use super::brillig_block_variables::BlockVariables; @@ -1643,7 +1643,7 @@ impl<'block> BrilligBlock<'block> { fn initialize_constant_array_runtime( &mut self, - item_types: Rc>, + item_types: Arc>, item_to_repeat: Vec, item_count: usize, pointer: MemoryAddress, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs index 88f7e35865e..69e7ff5383d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -276,20 +276,14 @@ impl BrilligContext< let index_at_end_of_array = self.allocate_register(); let end_value_register = self.allocate_register(); - self.codegen_loop(iteration_count, |ctx, iterator_register| { - // Load both values - ctx.codegen_array_get(pointer, iterator_register, start_value_register); + self.mov_instruction(index_at_end_of_array, size); + self.codegen_loop(iteration_count, |ctx, iterator_register| { // The index at the end of array is size - 1 - iterator - ctx.mov_instruction(index_at_end_of_array, size); ctx.codegen_usize_op_in_place(index_at_end_of_array, BrilligBinaryOp::Sub, 1); - ctx.memory_op_instruction( - index_at_end_of_array, - iterator_register.address, - index_at_end_of_array, - BrilligBinaryOp::Sub, - ); + // Load both values + ctx.codegen_array_get(pointer, iterator_register, start_value_register); ctx.codegen_array_get( pointer, SingleAddrVariable::new_usize(index_at_end_of_array), diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index a27354d7cb6..d3d936385b0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -10,7 +10,6 @@ use crate::ssa::ir::{instruction::Endian, types::NumericType}; use acvm::acir::circuit::brillig::{BrilligFunctionId, BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{AcirFunctionId, BlockId, BlockType, MemOp}; use acvm::acir::circuit::{AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode}; -use acvm::blackbox_solver; use acvm::brillig_vm::{MemoryValue, VMStatus, VM}; use acvm::{ acir::AcirField, @@ -1459,7 +1458,6 @@ impl AcirContext { name, BlackBoxFunc::MultiScalarMul | BlackBoxFunc::Keccakf1600 - | BlackBoxFunc::Sha256Compression | BlackBoxFunc::Blake2s | BlackBoxFunc::Blake3 | BlackBoxFunc::AND @@ -2152,7 +2150,11 @@ fn execute_brillig( } // Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode. - let mut vm = VM::new(calldata, code, Vec::new(), &blackbox_solver::StubbedBlackBoxSolver); + // + // We pass a stubbed solver here as a concrete solver implies a field choice which conflicts with this function + // being generic. + let solver = acvm::blackbox_solver::StubbedBlackBoxSolver; + let mut vm = VM::new(calldata, code, Vec::new(), &solver); // Run the Brillig VM on these inputs, bytecode, etc! let vm_status = vm.process_opcodes(); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs index 79db4e645ee..26eab290d4b 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/checks/check_for_underconstrained_values.rs @@ -1,8 +1,6 @@ //! This module defines an SSA pass that detects if the final function has any subgraphs independent from inputs and outputs. //! If this is the case, then part of the final circuit can be completely replaced by any other passing circuit, since there are no constraints ensuring connections. //! So the compiler informs the developer of this as a bug -use im::HashMap; - use crate::errors::{InternalBug, SsaReport}; use crate::ssa::ir::basic_block::BasicBlockId; use crate::ssa::ir::function::RuntimeType; @@ -10,25 +8,29 @@ use crate::ssa::ir::function::{Function, FunctionId}; use crate::ssa::ir::instruction::{Instruction, InstructionId, Intrinsic}; use crate::ssa::ir::value::{Value, ValueId}; use crate::ssa::ssa_gen::Ssa; +use im::HashMap; +use rayon::prelude::*; use std::collections::{BTreeMap, HashSet}; impl Ssa { /// Go through each top-level non-brillig function and detect if it has independent subgraphs #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn check_for_underconstrained_values(&mut self) -> Vec { - let mut warnings: Vec = Vec::new(); - for function in self.functions.values() { - match function.runtime() { - RuntimeType::Acir { .. } => { - warnings.extend(check_for_underconstrained_values_within_function( - function, + let functions_id = self.functions.values().map(|f| f.id().to_usize()).collect::>(); + functions_id + .iter() + .par_bridge() + .flat_map(|fid| { + let function_to_process = &self.functions[&FunctionId::new(*fid)]; + match function_to_process.runtime() { + RuntimeType::Acir { .. } => check_for_underconstrained_values_within_function( + function_to_process, &self.functions, - )); + ), + RuntimeType::Brillig => Vec::new(), } - RuntimeType::Brillig => (), - } - } - warnings + }) + .collect() } } @@ -88,9 +90,8 @@ impl Context { self.visited_blocks.insert(block); self.connect_value_ids_in_block(function, block, all_functions); } - // Merge ValueIds into sets, where each original small set of ValueIds is merged with another set if they intersect - self.merge_sets(); + self.value_sets = Self::merge_sets_par(&self.value_sets); } /// Find sets that contain input or output value of the function @@ -267,14 +268,13 @@ impl Context { /// Merge all small sets into larger ones based on whether the sets intersect or not /// /// If two small sets have a common ValueId, we merge them into one - fn merge_sets(&mut self) { + fn merge_sets(current: &[HashSet]) -> Vec> { let mut new_set_id: usize = 0; let mut updated_sets: HashMap> = HashMap::new(); let mut value_dictionary: HashMap = HashMap::new(); let mut parsed_value_set: HashSet = HashSet::new(); - // Go through each set - for set in self.value_sets.iter() { + for set in current.iter() { // Check if the set has any of the ValueIds we've encountered at previous iterations let intersection: HashSet = set.intersection(&parsed_value_set).copied().collect(); @@ -327,7 +327,26 @@ impl Context { } updated_sets.insert(largest_set_index, largest_set); } - self.value_sets = updated_sets.values().cloned().collect(); + updated_sets.values().cloned().collect() + } + + /// Parallel version of merge_sets + /// The sets are merged by chunks, and then the chunks are merged together + fn merge_sets_par(sets: &[HashSet]) -> Vec> { + let mut sets = sets.to_owned(); + let mut len = sets.len(); + let mut prev_len = len + 1; + + while len > 1000 && len < prev_len { + sets = sets.par_chunks(1000).flat_map(Self::merge_sets).collect(); + + prev_len = len; + len = sets.len(); + } + // TODO: if prev_len >= len, this means we cannot effectively merge the sets anymore + // We should instead partition the sets into disjoint chunks and work on those chunks, + // but for now we fallback to the non-parallel implementation + Self::merge_sets(&sets) } } #[cfg(test)] diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index 9f964cf048d..38895bb977e 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -1,5 +1,4 @@ -use std::collections::BTreeMap; -use std::rc::Rc; +use std::{collections::BTreeMap, sync::Arc}; use crate::ssa::ir::{types::Type, value::ValueId}; use acvm::FieldElement; @@ -155,8 +154,8 @@ impl FunctionBuilder { let len = databus.values.len(); let array = if len > 0 { - let array = - self.array_constant(databus.values, Type::Array(Rc::new(vec![Type::field()]), len)); + let array = self + .array_constant(databus.values, Type::Array(Arc::new(vec![Type::field()]), len)); Some(array) } else { None diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 8cc42241d92..bf6430c36d7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -1,6 +1,6 @@ pub(crate) mod data_bus; -use std::{borrow::Cow, collections::BTreeMap, rc::Rc}; +use std::{borrow::Cow, collections::BTreeMap, sync::Arc}; use acvm::{acir::circuit::ErrorSelector, FieldElement}; use noirc_errors::Location; @@ -189,7 +189,7 @@ impl FunctionBuilder { /// given amount of field elements. Returns the result of the allocate instruction, /// which is always a Reference to the allocated data. pub(crate) fn insert_allocate(&mut self, element_type: Type) -> ValueId { - let reference_type = Type::Reference(Rc::new(element_type)); + let reference_type = Type::Reference(Arc::new(element_type)); self.insert_instruction(Instruction::Allocate, Some(vec![reference_type])).first() } @@ -516,7 +516,7 @@ impl std::ops::Index for FunctionBuilder { #[cfg(test)] mod tests { - use std::rc::Rc; + use std::sync::Arc; use acvm::{acir::AcirField, FieldElement}; @@ -542,7 +542,7 @@ mod tests { let to_bits_id = builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); let input = builder.numeric_constant(FieldElement::from(7_u128), Type::field()); let length = builder.numeric_constant(FieldElement::from(8_u128), Type::field()); - let result_types = vec![Type::Array(Rc::new(vec![Type::bool()]), 8)]; + let result_types = vec![Type::Array(Arc::new(vec![Type::bool()]), 8)]; let call_results = builder.insert_call(to_bits_id, vec![input, length], result_types).into_owned(); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index c3cd27bf179..36069f17933 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -733,11 +733,15 @@ impl Instruction { } } + let then_value = dfg.resolve(*then_value); + let else_value = dfg.resolve(*else_value); + if then_value == else_value { + return SimplifiedTo(then_value); + } + if matches!(&typ, Type::Numeric(_)) { let then_condition = *then_condition; - let then_value = *then_value; let else_condition = *else_condition; - let else_value = *else_value; let result = ValueMerger::merge_numeric_values( dfg, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index ea2523e873e..2c6aedeca35 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -1,7 +1,10 @@ use fxhash::FxHashMap as HashMap; -use std::{collections::VecDeque, rc::Rc}; +use std::{collections::VecDeque, sync::Arc}; -use acvm::{acir::AcirField, acir::BlackBoxFunc, BlackBoxResolutionError, FieldElement}; +use acvm::{ + acir::{AcirField, BlackBoxFunc}, + BlackBoxResolutionError, FieldElement, +}; use bn254_blackbox_solver::derive_generators; use iter_extended::vecmap; use num_bigint::BigUint; @@ -20,6 +23,8 @@ use crate::ssa::{ use super::{Binary, BinaryOp, Endian, Instruction, SimplifyResult}; +mod blackbox; + /// Try to simplify this call instruction. If the instruction can be simplified to a known value, /// that value is returned. Otherwise None is returned. /// @@ -468,11 +473,17 @@ fn simplify_black_box_func( arguments: &[ValueId], dfg: &mut DataFlowGraph, ) -> SimplifyResult { + cfg_if::cfg_if! { + if #[cfg(feature = "bn254")] { + let solver = bn254_blackbox_solver::Bn254BlackBoxSolver; + } else { + let solver = acvm::blackbox_solver::StubbedBlackBoxSolver; + } + }; match bb_func { BlackBoxFunc::SHA256 => simplify_hash(dfg, arguments, acvm::blackbox_solver::sha256), BlackBoxFunc::Blake2s => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake2s), BlackBoxFunc::Blake3 => simplify_hash(dfg, arguments, acvm::blackbox_solver::blake3), - BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => SimplifyResult::None, BlackBoxFunc::Keccakf1600 => { if let Some((array_input, _)) = dfg.get_array_constant(arguments[0]) { if array_is_constant(dfg, &array_input) { @@ -503,20 +514,26 @@ fn simplify_black_box_func( BlackBoxFunc::Keccak256 => { unreachable!("Keccak256 should have been replaced by calls to Keccakf1600") } - BlackBoxFunc::Poseidon2Permutation => SimplifyResult::None, //TODO(Guillaume) - BlackBoxFunc::EcdsaSecp256k1 => { - simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256k1_verify) - } - BlackBoxFunc::EcdsaSecp256r1 => { - simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256r1_verify) + BlackBoxFunc::Poseidon2Permutation => { + blackbox::simplify_poseidon2_permutation(dfg, solver, arguments) } + BlackBoxFunc::EcdsaSecp256k1 => blackbox::simplify_signature( + dfg, + arguments, + acvm::blackbox_solver::ecdsa_secp256k1_verify, + ), + BlackBoxFunc::EcdsaSecp256r1 => blackbox::simplify_signature( + dfg, + arguments, + acvm::blackbox_solver::ecdsa_secp256r1_verify, + ), + + BlackBoxFunc::PedersenCommitment + | BlackBoxFunc::PedersenHash + | BlackBoxFunc::MultiScalarMul => SimplifyResult::None, + BlackBoxFunc::EmbeddedCurveAdd => blackbox::simplify_ec_add(dfg, solver, arguments), + BlackBoxFunc::SchnorrVerify => blackbox::simplify_schnorr_verify(dfg, solver, arguments), - BlackBoxFunc::MultiScalarMul - | BlackBoxFunc::SchnorrVerify - | BlackBoxFunc::EmbeddedCurveAdd => { - // Currently unsolvable here as we rely on an implementation in the backend. - SimplifyResult::None - } BlackBoxFunc::BigIntAdd | BlackBoxFunc::BigIntSub | BlackBoxFunc::BigIntMul @@ -544,7 +561,7 @@ fn simplify_black_box_func( fn make_constant_array(dfg: &mut DataFlowGraph, results: Vec, typ: Type) -> ValueId { let result_constants = vecmap(results, |element| dfg.make_constant(element, typ.clone())); - let typ = Type::Array(Rc::new(vec![typ]), result_constants.len()); + let typ = Type::Array(Arc::new(vec![typ]), result_constants.len()); dfg.make_array(result_constants.into(), typ) } @@ -555,7 +572,7 @@ fn make_constant_slice( ) -> (ValueId, ValueId) { let result_constants = vecmap(results, |element| dfg.make_constant(element, typ.clone())); - let typ = Type::Slice(Rc::new(vec![typ])); + let typ = Type::Slice(Arc::new(vec![typ])); let length = FieldElement::from(result_constants.len() as u128); (dfg.make_constant(length, Type::length_type()), dfg.make_array(result_constants.into(), typ)) } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs new file mode 100644 index 00000000000..7789b212e58 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -0,0 +1,190 @@ +use std::sync::Arc; + +use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; +use iter_extended::vecmap; + +use crate::ssa::ir::{ + dfg::DataFlowGraph, instruction::SimplifyResult, types::Type, value::ValueId, +}; + +use super::{array_is_constant, make_constant_array, to_u8_vec}; + +pub(super) fn simplify_ec_add( + dfg: &mut DataFlowGraph, + solver: impl BlackBoxFunctionSolver, + arguments: &[ValueId], +) -> SimplifyResult { + match ( + dfg.get_numeric_constant(arguments[0]), + dfg.get_numeric_constant(arguments[1]), + dfg.get_numeric_constant(arguments[2]), + dfg.get_numeric_constant(arguments[3]), + dfg.get_numeric_constant(arguments[4]), + dfg.get_numeric_constant(arguments[5]), + ) { + ( + Some(point1_x), + Some(point1_y), + Some(point1_is_infinity), + Some(point2_x), + Some(point2_y), + Some(point2_is_infinity), + ) => { + let Ok((result_x, result_y, result_is_infinity)) = solver.ec_add( + &point1_x, + &point1_y, + &point1_is_infinity, + &point2_x, + &point2_y, + &point2_is_infinity, + ) else { + return SimplifyResult::None; + }; + + let result_x = dfg.make_constant(result_x, Type::field()); + let result_y = dfg.make_constant(result_y, Type::field()); + let result_is_infinity = dfg.make_constant(result_is_infinity, Type::bool()); + + let typ = Type::Array(Arc::new(vec![Type::field()]), 3); + let result_array = + dfg.make_array(im::vector![result_x, result_y, result_is_infinity], typ); + + SimplifyResult::SimplifiedTo(result_array) + } + _ => SimplifyResult::None, + } +} + +pub(super) fn simplify_poseidon2_permutation( + dfg: &mut DataFlowGraph, + solver: impl BlackBoxFunctionSolver, + arguments: &[ValueId], +) -> SimplifyResult { + match (dfg.get_array_constant(arguments[0]), dfg.get_numeric_constant(arguments[1])) { + (Some((state, _)), Some(state_length)) if array_is_constant(dfg, &state) => { + let state: Vec = state + .iter() + .map(|id| { + dfg.get_numeric_constant(*id) + .expect("value id from array should point at constant") + }) + .collect(); + + let Some(state_length) = state_length.try_to_u32() else { + return SimplifyResult::None; + }; + + let Ok(new_state) = solver.poseidon2_permutation(&state, state_length) else { + return SimplifyResult::None; + }; + + let result_array = make_constant_array(dfg, new_state, Type::field()); + + SimplifyResult::SimplifiedTo(result_array) + } + _ => SimplifyResult::None, + } +} + +pub(super) fn simplify_schnorr_verify( + dfg: &mut DataFlowGraph, + solver: impl BlackBoxFunctionSolver, + arguments: &[ValueId], +) -> SimplifyResult { + match ( + dfg.get_numeric_constant(arguments[0]), + dfg.get_numeric_constant(arguments[1]), + dfg.get_array_constant(arguments[2]), + dfg.get_array_constant(arguments[3]), + ) { + (Some(public_key_x), Some(public_key_y), Some((signature, _)), Some((message, _))) + if array_is_constant(dfg, &signature) && array_is_constant(dfg, &message) => + { + let signature = to_u8_vec(dfg, signature); + let signature: [u8; 64] = + signature.try_into().expect("Compiler should produce correctly sized signature"); + + let message = to_u8_vec(dfg, message); + + let Ok(valid_signature) = + solver.schnorr_verify(&public_key_x, &public_key_y, &signature, &message) + else { + return SimplifyResult::None; + }; + + let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool()); + SimplifyResult::SimplifiedTo(valid_signature) + } + _ => SimplifyResult::None, + } +} + +pub(super) fn simplify_hash( + dfg: &mut DataFlowGraph, + arguments: &[ValueId], + hash_function: fn(&[u8]) -> Result<[u8; 32], BlackBoxResolutionError>, +) -> SimplifyResult { + match dfg.get_array_constant(arguments[0]) { + Some((input, _)) if array_is_constant(dfg, &input) => { + let input_bytes: Vec = to_u8_vec(dfg, input); + + let hash = hash_function(&input_bytes) + .expect("Rust solvable black box function should not fail"); + + let hash_values = vecmap(hash, |byte| FieldElement::from_be_bytes_reduce(&[byte])); + + let result_array = make_constant_array(dfg, hash_values, Type::unsigned(8)); + SimplifyResult::SimplifiedTo(result_array) + } + _ => SimplifyResult::None, + } +} + +type ECDSASignatureVerifier = fn( + hashed_msg: &[u8], + public_key_x: &[u8; 32], + public_key_y: &[u8; 32], + signature: &[u8; 64], +) -> Result; + +pub(super) fn simplify_signature( + dfg: &mut DataFlowGraph, + arguments: &[ValueId], + signature_verifier: ECDSASignatureVerifier, +) -> SimplifyResult { + match ( + dfg.get_array_constant(arguments[0]), + dfg.get_array_constant(arguments[1]), + dfg.get_array_constant(arguments[2]), + dfg.get_array_constant(arguments[3]), + ) { + ( + Some((public_key_x, _)), + Some((public_key_y, _)), + Some((signature, _)), + Some((hashed_message, _)), + ) if array_is_constant(dfg, &public_key_x) + && array_is_constant(dfg, &public_key_y) + && array_is_constant(dfg, &signature) + && array_is_constant(dfg, &hashed_message) => + { + let public_key_x: [u8; 32] = to_u8_vec(dfg, public_key_x) + .try_into() + .expect("ECDSA public key fields are 32 bytes"); + let public_key_y: [u8; 32] = to_u8_vec(dfg, public_key_y) + .try_into() + .expect("ECDSA public key fields are 32 bytes"); + let signature: [u8; 64] = + to_u8_vec(dfg, signature).try_into().expect("ECDSA signatures are 64 bytes"); + let hashed_message: Vec = to_u8_vec(dfg, hashed_message); + + let valid_signature = + signature_verifier(&hashed_message, &public_key_x, &public_key_y, &signature) + .expect("Rust solvable black box function should not fail"); + + let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool()); + SimplifyResult::SimplifiedTo(valid_signature) + } + _ => SimplifyResult::None, + } +} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs index f1265553b83..769d52e6e65 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/map.rs @@ -27,7 +27,7 @@ impl Id { /// Constructs a new Id for the given index. /// This constructor is deliberately private to prevent /// constructing invalid IDs. - fn new(index: usize) -> Self { + pub(crate) fn new(index: usize) -> Self { Self { index, _marker: std::marker::PhantomData } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs index e467fa5400d..b7ee37ba17a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use std::rc::Rc; +use std::sync::Arc; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -72,13 +72,13 @@ pub(crate) enum Type { Numeric(NumericType), /// A reference to some value, such as an array - Reference(Rc), + Reference(Arc), /// An immutable array value with the given element type and length - Array(Rc, usize), + Array(Arc, usize), /// An immutable slice value with a given element type - Slice(Rc), + Slice(Arc), /// A function that may be called directly Function, @@ -112,7 +112,7 @@ impl Type { /// Creates the str type, of the given length N pub(crate) fn str(length: usize) -> Type { - Type::Array(Rc::new(vec![Type::char()]), length) + Type::Array(Arc::new(vec![Type::char()]), length) } /// Creates the native field type. @@ -190,7 +190,7 @@ impl Type { } } - pub(crate) fn element_types(self) -> Rc> { + pub(crate) fn element_types(self) -> Arc> { match self { Type::Array(element_types, _) | Type::Slice(element_types) => element_types, other => panic!("element_types: Expected array or slice, found {other}"), diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index c8f6d201d86..ff9a63c8d79 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -311,7 +311,7 @@ impl Context { #[cfg(test)] mod test { - use std::rc::Rc; + use std::sync::Arc; use crate::ssa::{ function_builder::FunctionBuilder, @@ -509,7 +509,7 @@ mod test { let one = builder.field_constant(1u128); let v1 = builder.insert_binary(v0, BinaryOp::Add, one); - let array_type = Type::Array(Rc::new(vec![Type::field()]), 1); + let array_type = Type::Array(Arc::new(vec![Type::field()]), 1); let arr = builder.current_function.dfg.make_array(vec![v1].into(), array_type); builder.terminate_with_return(vec![arr]); @@ -601,7 +601,7 @@ mod test { // 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 v0 = builder.add_parameter(Type::Array(Arc::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)); @@ -737,7 +737,7 @@ mod test { let zero = builder.field_constant(0u128); let one = builder.field_constant(1u128); - let typ = Type::Array(Rc::new(vec![Type::field()]), 2); + let typ = Type::Array(Arc::new(vec![Type::field()]), 2); let array = builder.array_constant(vec![zero, one].into(), typ); let _v2 = builder.insert_array_get(array, v1, Type::field()); @@ -787,7 +787,7 @@ mod test { let v0 = builder.add_parameter(Type::bool()); let v1 = builder.add_parameter(Type::bool()); - let v2 = builder.add_parameter(Type::Array(Rc::new(vec![Type::field()]), 2)); + let v2 = builder.add_parameter(Type::Array(Arc::new(vec![Type::field()]), 2)); let zero = builder.numeric_constant(0u128, Type::length_type()); let one = builder.numeric_constant(1u128, Type::length_type()); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 72ed02b00a8..d5fb98c7adc 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -878,7 +878,7 @@ impl<'f> Context<'f> { #[cfg(test)] mod test { - use std::rc::Rc; + use std::sync::Arc; use acvm::acir::AcirField; @@ -1016,7 +1016,7 @@ mod test { let b2 = builder.insert_block(); let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::Reference(Rc::new(Type::field()))); + let v1 = builder.add_parameter(Type::Reference(Arc::new(Type::field()))); builder.terminate_with_jmpif(v0, b1, b2); @@ -1078,7 +1078,7 @@ mod test { let b3 = builder.insert_block(); let v0 = builder.add_parameter(Type::bool()); - let v1 = builder.add_parameter(Type::Reference(Rc::new(Type::field()))); + let v1 = builder.add_parameter(Type::Reference(Arc::new(Type::field()))); builder.terminate_with_jmpif(v0, b1, b2); @@ -1477,7 +1477,7 @@ mod test { let b2 = builder.insert_block(); let b3 = builder.insert_block(); - let element_type = Rc::new(vec![Type::unsigned(8)]); + let element_type = Arc::new(vec![Type::unsigned(8)]); let array_type = Type::Array(element_type.clone(), 2); let array = builder.add_parameter(array_type); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index 7c2db62b0ea..75ee57dd4fa 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -58,6 +58,13 @@ impl<'a> ValueMerger<'a> { then_value: ValueId, else_value: ValueId, ) -> ValueId { + let then_value = self.dfg.resolve(then_value); + let else_value = self.dfg.resolve(else_value); + + if then_value == else_value { + return then_value; + } + match self.dfg.type_of_value(then_value) { Type::Numeric(_) => Self::merge_numeric_values( self.dfg, diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index e5a25dcfef1..9d6582c0db7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -425,7 +425,7 @@ impl<'f> PerFunctionContext<'f> { #[cfg(test)] mod tests { - use std::rc::Rc; + use std::sync::Arc; use acvm::{acir::AcirField, FieldElement}; use im::vector; @@ -454,11 +454,11 @@ mod tests { let func_id = Id::test_new(0); let mut builder = FunctionBuilder::new("func".into(), func_id); - let v0 = builder.insert_allocate(Type::Array(Rc::new(vec![Type::field()]), 2)); + let v0 = builder.insert_allocate(Type::Array(Arc::new(vec![Type::field()]), 2)); let one = builder.field_constant(FieldElement::one()); let two = builder.field_constant(FieldElement::one()); - let element_type = Rc::new(vec![Type::field()]); + let element_type = Arc::new(vec![Type::field()]); let array_type = Type::Array(element_type, 2); let array = builder.array_constant(vector![one, two], array_type.clone()); @@ -672,7 +672,7 @@ mod tests { let zero = builder.field_constant(0u128); builder.insert_store(v0, zero); - let v2 = builder.insert_allocate(Type::Reference(Rc::new(Type::field()))); + let v2 = builder.insert_allocate(Type::Reference(Arc::new(Type::field()))); builder.insert_store(v2, v0); let v3 = builder.insert_load(v2, Type::field()); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/rc.rs index 1561547e32e..4f109a27874 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -161,7 +161,7 @@ fn remove_instructions(to_remove: HashSet, function: &mut Functio #[cfg(test)] mod test { - use std::rc::Rc; + use std::sync::Arc; use crate::ssa::{ function_builder::FunctionBuilder, @@ -209,14 +209,14 @@ mod test { let mut builder = FunctionBuilder::new("foo".into(), main_id); builder.set_runtime(RuntimeType::Brillig); - let inner_array_type = Type::Array(Rc::new(vec![Type::field()]), 2); + let inner_array_type = Type::Array(Arc::new(vec![Type::field()]), 2); let v0 = builder.add_parameter(inner_array_type.clone()); builder.insert_inc_rc(v0); builder.insert_inc_rc(v0); builder.insert_dec_rc(v0); - let outer_array_type = Type::Array(Rc::new(vec![inner_array_type]), 1); + let outer_array_type = Type::Array(Arc::new(vec![inner_array_type]), 1); let array = builder.array_constant(vec![v0].into(), outer_array_type); builder.terminate_with_return(vec![array]); @@ -248,7 +248,7 @@ mod test { let main_id = Id::test_new(0); let mut builder = FunctionBuilder::new("mutator".into(), main_id); - let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); + let array_type = Type::Array(Arc::new(vec![Type::field()]), 2); let v0 = builder.add_parameter(array_type.clone()); let v1 = builder.insert_allocate(array_type.clone()); @@ -297,8 +297,8 @@ mod test { let main_id = Id::test_new(0); let mut builder = FunctionBuilder::new("mutator2".into(), main_id); - let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); - let reference_type = Type::Reference(Rc::new(array_type.clone())); + let array_type = Type::Array(Arc::new(vec![Type::field()]), 2); + let reference_type = Type::Reference(Arc::new(array_type.clone())); let v0 = builder.add_parameter(reference_type); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index 628e1bd7410..6ca7a76d740 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, rc::Rc}; +use std::{borrow::Cow, sync::Arc}; use acvm::{acir::AcirField, FieldElement}; @@ -174,7 +174,7 @@ impl Context<'_> { let to_bits = self.function.dfg.import_intrinsic(Intrinsic::ToBits(Endian::Little)); let length = self.field_constant(FieldElement::from(bit_size as i128)); let result_types = - vec![Type::field(), Type::Array(Rc::new(vec![Type::bool()]), bit_size as usize)]; + vec![Type::field(), Type::Array(Arc::new(vec![Type::bool()]), bit_size as usize)]; let rhs_bits = self.insert_call(to_bits, vec![rhs, length], result_types); let rhs_bits = rhs_bits[1]; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 13e5c2445ad..fb7091a8854 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -1,5 +1,4 @@ -use std::rc::Rc; -use std::sync::{Mutex, RwLock}; +use std::sync::{Arc, Mutex, RwLock}; use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; @@ -198,7 +197,7 @@ impl<'a> FunctionContext<'a> { // A mutable reference wraps each element into a reference. // This can be multiple values if the element type is a tuple. ast::Type::MutableReference(element) => { - Self::map_type_helper(element, &mut |typ| f(Type::Reference(Rc::new(typ)))) + Self::map_type_helper(element, &mut |typ| f(Type::Reference(Arc::new(typ)))) } ast::Type::FmtString(len, fields) => { // A format string is represented by multiple values @@ -213,7 +212,7 @@ impl<'a> FunctionContext<'a> { let element_types = Self::convert_type(elements).flatten(); Tree::Branch(vec![ Tree::Leaf(f(Type::length_type())), - Tree::Leaf(f(Type::Slice(Rc::new(element_types)))), + Tree::Leaf(f(Type::Slice(Arc::new(element_types)))), ]) } other => Tree::Leaf(f(Self::convert_non_tuple_type(other))), @@ -237,7 +236,7 @@ impl<'a> FunctionContext<'a> { ast::Type::Field => Type::field(), ast::Type::Array(len, element) => { let element_types = Self::convert_type(element).flatten(); - Type::Array(Rc::new(element_types), *len as usize) + Type::Array(Arc::new(element_types), *len as usize) } ast::Type::Integer(Signedness::Signed, bits) => Type::signed((*bits).into()), ast::Type::Integer(Signedness::Unsigned, bits) => Type::unsigned((*bits).into()), @@ -253,7 +252,7 @@ impl<'a> FunctionContext<'a> { ast::Type::MutableReference(element) => { // Recursive call to panic if element is a tuple let element = Self::convert_non_tuple_type(element); - Type::Reference(Rc::new(element)) + Type::Reference(Arc::new(element)) } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index 7ef8870eaa8..c0f6c8965fb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -27,7 +27,7 @@ num-traits.workspace = true rustc-hash = "1.1.0" small-ord-set = "0.1.3" regex = "1.9.1" -cfg-if = "1.0.0" +cfg-if.workspace = true tracing.workspace = true petgraph = "0.6" rangemap = "1.4.0" diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs index dc07f55ee33..f242180134d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -7,7 +7,7 @@ use crate::ast::{ }; use crate::hir::def_collector::errors::DefCollectorErrorKind; use crate::macros_api::StructId; -use crate::node_interner::{ExprId, QuotedTypeId}; +use crate::node_interner::{ExprId, InternedExpressionKind, QuotedTypeId}; use crate::token::{Attributes, FunctionAttribute, Token, Tokens}; use crate::{Kind, Type}; use acvm::{acir::AcirField, FieldElement}; @@ -43,6 +43,11 @@ pub enum ExpressionKind { // code. It is used to translate function values back into the AST while // guaranteeing they have the same instantiated type and definition id without resolving again. Resolved(ExprId), + + // This is an interned ExpressionKind during comptime code. + // The actual ExpressionKind can be retrieved with a NodeInterner. + Interned(InternedExpressionKind), + Error, } @@ -603,6 +608,7 @@ impl Display for ExpressionKind { Unsafe(block, _) => write!(f, "unsafe {block}"), Error => write!(f, "Error"), Resolved(_) => write!(f, "?Resolved"), + Interned(_) => write!(f, "?Interned"), Unquote(expr) => write!(f, "$({expr})"), Quote(tokens) => { let tokens = vecmap(&tokens.0, ToString::to_string); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs index 6f6d5cbccdf..e63222bfc87 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -10,6 +10,9 @@ mod statement; mod structure; mod traits; mod type_alias; +mod visitor; + +pub use visitor::Visitor; pub use expression::*; pub use function::*; @@ -22,7 +25,7 @@ pub use traits::*; pub use type_alias::*; use crate::{ - node_interner::QuotedTypeId, + node_interner::{InternedUnresolvedTypeData, QuotedTypeId}, parser::{ParserError, ParserErrorReason}, token::IntType, BinaryTypeOperator, @@ -141,6 +144,10 @@ pub enum UnresolvedTypeData { /// as a result of being spliced into a macro's token stream input. Resolved(QuotedTypeId), + // This is an interned UnresolvedTypeData during comptime code. + // The actual UnresolvedTypeData can be retrieved with a NodeInterner. + Interned(InternedUnresolvedTypeData), + Unspecified, // This is for when the user declares a variable without specifying it's type Error, } @@ -297,6 +304,7 @@ impl std::fmt::Display for UnresolvedTypeData { Unspecified => write!(f, "unspecified"), Parenthesized(typ) => write!(f, "({typ})"), Resolved(_) => write!(f, "(resolved type)"), + Interned(_) => write!(f, "?Interned"), AsTraitPath(path) => write!(f, "{path}"), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs index edccf545a02..c88fcba749b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -13,6 +13,7 @@ use super::{ use crate::elaborator::types::SELF_TYPE_NAME; use crate::lexer::token::SpannedToken; use crate::macros_api::{SecondaryAttribute, UnresolvedTypeData}; +use crate::node_interner::{InternedExpressionKind, InternedStatementKind}; use crate::parser::{ParserError, ParserErrorReason}; use crate::token::Token; @@ -45,6 +46,9 @@ pub enum StatementKind { Comptime(Box), // This is an expression with a trailing semi-colon Semi(Expression), + // This is an interned StatementKind during comptime code. + // The actual StatementKind can be retrieved with a NodeInterner. + Interned(InternedStatementKind), // This statement is the result of a recovered parse error. // To avoid issuing multiple errors in later steps, it should // be skipped in any future analysis if possible. @@ -97,6 +101,9 @@ impl StatementKind { // A semicolon on a for loop is optional and does nothing StatementKind::For(_) => self, + // No semicolon needed for a resolved statement + StatementKind::Interned(_) => self, + StatementKind::Expression(expr) => { match (&expr.kind, semi, last_statement_in_block) { // Semicolons are optional for these expressions @@ -534,6 +541,7 @@ pub enum LValue { MemberAccess { object: Box, field_name: Ident, span: Span }, Index { array: Box, index: Expression, span: Span }, Dereference(Box, Span), + Interned(InternedExpressionKind, Span), } #[derive(Debug, PartialEq, Eq, Clone)] @@ -591,7 +599,7 @@ impl Recoverable for Pattern { } impl LValue { - fn as_expression(&self) -> Expression { + pub fn as_expression(&self) -> Expression { let kind = match self { LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), LValue::MemberAccess { object, field_name, span: _ } => { @@ -612,17 +620,53 @@ impl LValue { rhs: lvalue.as_expression(), })) } + LValue::Interned(id, _) => ExpressionKind::Interned(*id), }; let span = self.span(); Expression::new(kind, span) } + pub fn from_expression(expr: Expression) -> LValue { + LValue::from_expression_kind(expr.kind, expr.span) + } + + pub fn from_expression_kind(expr: ExpressionKind, span: Span) -> LValue { + match expr { + ExpressionKind::Variable(path) => LValue::Ident(path.as_ident().unwrap().clone()), + ExpressionKind::MemberAccess(member_access) => LValue::MemberAccess { + object: Box::new(LValue::from_expression(member_access.lhs)), + field_name: member_access.rhs, + span, + }, + ExpressionKind::Index(index) => LValue::Index { + array: Box::new(LValue::from_expression(index.collection)), + index: index.index, + span, + }, + ExpressionKind::Prefix(prefix) => { + if matches!( + prefix.operator, + crate::ast::UnaryOp::Dereference { implicitly_added: false } + ) { + LValue::Dereference(Box::new(LValue::from_expression(prefix.rhs)), span) + } else { + panic!("Called LValue::from_expression with an invalid prefix operator") + } + } + ExpressionKind::Interned(id) => LValue::Interned(id, span), + _ => { + panic!("Called LValue::from_expression with an invalid expression") + } + } + } + pub fn span(&self) -> Span { match self { LValue::Ident(ident) => ident.span(), LValue::MemberAccess { span, .. } | LValue::Index { span, .. } | LValue::Dereference(_, span) => *span, + LValue::Interned(_, span) => *span, } } } @@ -777,6 +821,7 @@ impl Display for StatementKind { StatementKind::Continue => write!(f, "continue"), StatementKind::Comptime(statement) => write!(f, "comptime {}", statement.kind), StatementKind::Semi(semi) => write!(f, "{semi};"), + StatementKind::Interned(_) => write!(f, "(resolved);"), StatementKind::Error => write!(f, "Error"), } } @@ -809,6 +854,7 @@ impl Display for LValue { } LValue::Index { array, index, span: _ } => write!(f, "{array}[{index}]"), LValue::Dereference(lvalue, _span) => write!(f, "*{lvalue}"), + LValue::Interned(_, _) => write!(f, "?Interned"), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs new file mode 100644 index 00000000000..96183d3322f --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -0,0 +1,1301 @@ +use acvm::FieldElement; +use noirc_errors::Span; + +use crate::{ + ast::{ + ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, + CastExpression, ConstrainStatement, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, ForRange, Ident, IfExpression, IndexExpression, InfixExpression, LValue, + Lambda, LetStatement, Literal, MemberAccessExpression, MethodCallExpression, + ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, + PrefixExpression, Statement, StatementKind, TraitImplItem, TraitItem, TypeImpl, UseTree, + UseTreeKind, + }, + node_interner::{ + ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, + QuotedTypeId, + }, + parser::{Item, ItemKind, ParsedSubModule}, + token::Tokens, + ParsedModule, QuotedType, +}; + +use super::{ + FunctionReturnType, GenericTypeArgs, IntegerBitSize, Pattern, Signedness, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, +}; + +/// Implements the [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for Noir's AST. +/// +/// In this implementation, methods must return a bool: +/// - true means children must be visited +/// - false means children must not be visited, either because the visitor implementation +/// will visit children of interest manually, or because no children are of interest +pub trait Visitor { + fn visit_parsed_module(&mut self, _: &ParsedModule) -> bool { + true + } + + fn visit_item(&mut self, _: &Item) -> bool { + true + } + + fn visit_parsed_submodule(&mut self, _: &ParsedSubModule, _: Span) -> bool { + true + } + + fn visit_noir_function(&mut self, _: &NoirFunction, _: Span) -> bool { + true + } + + fn visit_noir_trait_impl(&mut self, _: &NoirTraitImpl, _: Span) -> bool { + true + } + + fn visit_type_impl(&mut self, _: &TypeImpl, _: Span) -> bool { + true + } + + fn visit_trait_impl_item(&mut self, _: &TraitImplItem) -> bool { + true + } + + fn visit_trait_impl_item_function(&mut self, _: &NoirFunction, _span: Span) -> bool { + true + } + + fn visit_trait_impl_item_constant( + &mut self, + _name: &Ident, + _typ: &UnresolvedType, + _expression: &Expression, + ) -> bool { + true + } + + fn visit_trait_impl_item_type(&mut self, _name: &Ident, _alias: &UnresolvedType) -> bool { + true + } + + fn visit_noir_trait(&mut self, _: &NoirTrait, _: Span) -> bool { + true + } + + fn visit_trait_item(&mut self, _: &TraitItem) -> bool { + true + } + + fn visit_trait_item_function( + &mut self, + _name: &Ident, + _generics: &UnresolvedGenerics, + _parameters: &[(Ident, UnresolvedType)], + _return_type: &FunctionReturnType, + _where_clause: &[UnresolvedTraitConstraint], + _body: &Option, + ) -> bool { + true + } + + fn visit_trait_item_constant( + &mut self, + _name: &Ident, + _typ: &UnresolvedType, + _default_value: &Option, + ) -> bool { + true + } + + fn visit_trait_item_type(&mut self, _: &Ident) {} + + fn visit_use_tree(&mut self, _: &UseTree) -> bool { + true + } + + fn visit_use_tree_path(&mut self, _: &UseTree, _ident: &Ident, _alias: &Option) {} + + fn visit_use_tree_list(&mut self, _: &UseTree, _: &[UseTree]) -> bool { + true + } + + fn visit_noir_struct(&mut self, _: &NoirStruct, _: Span) -> bool { + true + } + + fn visit_noir_type_alias(&mut self, _: &NoirTypeAlias, _: Span) -> bool { + true + } + + fn visit_module_declaration(&mut self, _: &ModuleDeclaration, _: Span) {} + + fn visit_expression(&mut self, _: &Expression) -> bool { + true + } + + fn visit_literal(&mut self, _: &Literal, _: Span) -> bool { + true + } + + fn visit_literal_array(&mut self, _: &ArrayLiteral) -> bool { + true + } + + fn visit_literal_slice(&mut self, _: &ArrayLiteral) -> bool { + true + } + + fn visit_literal_bool(&mut self, _: bool) {} + + fn visit_literal_integer(&mut self, _value: FieldElement, _negative: bool) {} + + fn visit_literal_str(&mut self, _: &str) {} + + fn visit_literal_raw_str(&mut self, _: &str, _: u8) {} + + fn visit_literal_fmt_str(&mut self, _: &str) {} + + fn visit_literal_unit(&mut self) {} + + fn visit_block_expression(&mut self, _: &BlockExpression, _: Option) -> bool { + true + } + + fn visit_prefix_expression(&mut self, _: &PrefixExpression, _: Span) -> bool { + true + } + + fn visit_index_expression(&mut self, _: &IndexExpression, _: Span) -> bool { + true + } + + fn visit_call_expression(&mut self, _: &CallExpression, _: Span) -> bool { + true + } + + fn visit_method_call_expression(&mut self, _: &MethodCallExpression, _: Span) -> bool { + true + } + + fn visit_constructor_expression(&mut self, _: &ConstructorExpression, _: Span) -> bool { + true + } + + fn visit_member_access_expression(&mut self, _: &MemberAccessExpression, _: Span) -> bool { + true + } + + fn visit_cast_expression(&mut self, _: &CastExpression, _: Span) -> bool { + true + } + + fn visit_infix_expression(&mut self, _: &InfixExpression, _: Span) -> bool { + true + } + + fn visit_if_expression(&mut self, _: &IfExpression, _: Span) -> bool { + true + } + + fn visit_tuple(&mut self, _: &[Expression], _: Span) -> bool { + true + } + + fn visit_parenthesized(&mut self, _: &Expression, _: Span) -> bool { + true + } + + fn visit_unquote(&mut self, _: &Expression, _: Span) -> bool { + true + } + + fn visit_comptime_expression(&mut self, _: &BlockExpression, _: Span) -> bool { + true + } + + fn visit_unsafe(&mut self, _: &BlockExpression, _: Span) -> bool { + true + } + + fn visit_variable(&mut self, _: &Path, _: Span) -> bool { + true + } + + fn visit_quote(&mut self, _: &Tokens) {} + + fn visit_resolved_expression(&mut self, _expr_id: ExprId) {} + + fn visit_interned_expression(&mut self, _id: InternedExpressionKind) {} + + fn visit_error_expression(&mut self) {} + + fn visit_lambda(&mut self, _: &Lambda, _: Span) -> bool { + true + } + + fn visit_array_literal(&mut self, _: &ArrayLiteral) -> bool { + true + } + + fn visit_array_literal_standard(&mut self, _: &[Expression]) -> bool { + true + } + + fn visit_array_literal_repeated( + &mut self, + _repeated_element: &Expression, + _length: &Expression, + ) -> bool { + true + } + + fn visit_statement(&mut self, _: &Statement) -> bool { + true + } + + fn visit_import(&mut self, _: &UseTree) -> bool { + true + } + + fn visit_global(&mut self, _: &LetStatement, _: Span) -> bool { + true + } + + fn visit_let_statement(&mut self, _: &LetStatement) -> bool { + true + } + + fn visit_constrain_statement(&mut self, _: &ConstrainStatement) -> bool { + true + } + + fn visit_assign_statement(&mut self, _: &AssignStatement) -> bool { + true + } + + fn visit_for_loop_statement(&mut self, _: &ForLoopStatement) -> bool { + true + } + + fn visit_comptime_statement(&mut self, _: &Statement) -> bool { + true + } + + fn visit_break(&mut self) {} + + fn visit_continue(&mut self) {} + + fn visit_interned_statement(&mut self, _: InternedStatementKind) {} + + fn visit_error_statement(&mut self) {} + + fn visit_lvalue(&mut self, _: &LValue) -> bool { + true + } + + fn visit_lvalue_ident(&mut self, _: &Ident) {} + + fn visit_lvalue_member_access( + &mut self, + _object: &LValue, + _field_name: &Ident, + _span: Span, + ) -> bool { + true + } + + fn visit_lvalue_index(&mut self, _array: &LValue, _index: &Expression, _span: Span) -> bool { + true + } + + fn visit_lvalue_dereference(&mut self, _lvalue: &LValue, _span: Span) -> bool { + true + } + + fn visit_lvalue_interned(&mut self, _id: InternedExpressionKind, _span: Span) {} + + fn visit_for_range(&mut self, _: &ForRange) -> bool { + true + } + + fn visit_as_trait_path(&mut self, _: &AsTraitPath, _: Span) -> bool { + true + } + + fn visit_unresolved_type(&mut self, _: &UnresolvedType) -> bool { + true + } + + fn visit_array_type( + &mut self, + _: &UnresolvedTypeExpression, + _: &UnresolvedType, + _: Span, + ) -> bool { + true + } + + fn visit_slice_type(&mut self, _: &UnresolvedType, _: Span) -> bool { + true + } + + fn visit_parenthesized_type(&mut self, _: &UnresolvedType, _: Span) -> bool { + true + } + + fn visit_named_type(&mut self, _: &Path, _: &GenericTypeArgs, _: Span) -> bool { + true + } + + fn visit_trait_as_type(&mut self, _: &Path, _: &GenericTypeArgs, _: Span) -> bool { + true + } + + fn visit_mutable_reference_type(&mut self, _: &UnresolvedType, _: Span) -> bool { + true + } + + fn visit_tuple_type(&mut self, _: &[UnresolvedType], _: Span) -> bool { + true + } + + fn visit_function_type( + &mut self, + _args: &[UnresolvedType], + _ret: &UnresolvedType, + _env: &UnresolvedType, + _unconstrained: bool, + _span: Span, + ) -> bool { + true + } + + fn visit_as_trait_path_type(&mut self, _: &AsTraitPath, _: Span) -> bool { + true + } + + fn visit_expression_type(&mut self, _: &UnresolvedTypeExpression, _: Span) {} + + fn visit_format_string_type( + &mut self, + _: &UnresolvedTypeExpression, + _: &UnresolvedType, + _: Span, + ) -> bool { + true + } + + fn visit_string_type(&mut self, _: &UnresolvedTypeExpression, _: Span) {} + + fn visit_unspecified_type(&mut self, _: Span) {} + + fn visit_quoted_type(&mut self, _: &QuotedType, _: Span) {} + + fn visit_field_element_type(&mut self, _: Span) {} + + fn visit_integer_type(&mut self, _: Signedness, _: IntegerBitSize, _: Span) {} + + fn visit_bool_type(&mut self, _: Span) {} + + fn visit_unit_type(&mut self, _: Span) {} + + fn visit_resolved_type(&mut self, _: QuotedTypeId, _: Span) {} + + fn visit_interned_type(&mut self, _: InternedUnresolvedTypeData, _: Span) {} + + fn visit_error_type(&mut self, _: Span) {} + + fn visit_path(&mut self, _: &Path) {} + + fn visit_generic_type_args(&mut self, _: &GenericTypeArgs) -> bool { + true + } + + fn visit_function_return_type(&mut self, _: &FunctionReturnType) -> bool { + true + } + + fn visit_pattern(&mut self, _: &Pattern) -> bool { + true + } + + fn visit_identifier_pattern(&mut self, _: &Ident) {} + + fn visit_mutable_pattern(&mut self, _: &Pattern, _: Span, _is_synthesized: bool) -> bool { + true + } + + fn visit_tuple_pattern(&mut self, _: &[Pattern], _: Span) -> bool { + true + } + + fn visit_struct_pattern(&mut self, _: &Path, _: &[(Ident, Pattern)], _: Span) -> bool { + true + } +} + +impl ParsedModule { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_parsed_module(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for item in &self.items { + item.accept(visitor); + } + } +} + +impl Item { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_item(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match &self.kind { + ItemKind::Submodules(parsed_sub_module) => { + parsed_sub_module.accept(self.span, visitor); + } + ItemKind::Function(noir_function) => noir_function.accept(self.span, visitor), + ItemKind::TraitImpl(noir_trait_impl) => { + noir_trait_impl.accept(self.span, visitor); + } + ItemKind::Impl(type_impl) => type_impl.accept(self.span, visitor), + ItemKind::Global(let_statement) => { + if visitor.visit_global(let_statement, self.span) { + let_statement.accept(visitor); + } + } + ItemKind::Trait(noir_trait) => noir_trait.accept(self.span, visitor), + ItemKind::Import(use_tree) => { + if visitor.visit_import(use_tree) { + use_tree.accept(visitor); + } + } + ItemKind::TypeAlias(noir_type_alias) => noir_type_alias.accept(self.span, visitor), + ItemKind::Struct(noir_struct) => noir_struct.accept(self.span, visitor), + ItemKind::ModuleDecl(module_declaration) => { + module_declaration.accept(self.span, visitor); + } + } + } +} + +impl ParsedSubModule { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_parsed_submodule(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.contents.accept(visitor); + } +} + +impl NoirFunction { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_noir_function(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for param in &self.def.parameters { + param.typ.accept(visitor); + } + + self.def.body.accept(None, visitor); + } +} + +impl NoirTraitImpl { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_noir_trait_impl(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.trait_name.accept(visitor); + self.object_type.accept(visitor); + + for item in &self.items { + item.accept(visitor); + } + } +} + +impl TraitImplItem { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_trait_impl_item(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + TraitImplItem::Function(noir_function) => { + let span = Span::from( + noir_function.name_ident().span().start()..noir_function.span().end(), + ); + + if visitor.visit_trait_impl_item_function(noir_function, span) { + noir_function.accept(span, visitor); + } + } + TraitImplItem::Constant(name, unresolved_type, expression) => { + if visitor.visit_trait_impl_item_constant(name, unresolved_type, expression) { + unresolved_type.accept(visitor); + expression.accept(visitor); + } + } + TraitImplItem::Type { name, alias } => { + if visitor.visit_trait_impl_item_type(name, alias) { + alias.accept(visitor); + } + } + } + } +} + +impl TypeImpl { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_type_impl(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.object_type.accept(visitor); + + for (method, span) in &self.methods { + method.accept(*span, visitor); + } + } +} + +impl NoirTrait { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_noir_trait(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for item in &self.items { + item.accept(visitor); + } + } +} + +impl TraitItem { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_trait_item(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body } => { + if visitor.visit_trait_item_function( + name, + generics, + parameters, + return_type, + where_clause, + body, + ) { + for (_name, unresolved_type) in parameters { + unresolved_type.accept(visitor); + } + + return_type.accept(visitor); + + for unresolved_trait_constraint in where_clause { + unresolved_trait_constraint.typ.accept(visitor); + } + + if let Some(body) = body { + body.accept(None, visitor); + } + } + } + TraitItem::Constant { name, typ, default_value } => { + if visitor.visit_trait_item_constant(name, typ, default_value) { + typ.accept(visitor); + + if let Some(default_value) = default_value { + default_value.accept(visitor); + } + } + } + TraitItem::Type { name } => visitor.visit_trait_item_type(name), + } + } +} + +impl UseTree { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_use_tree(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match &self.kind { + UseTreeKind::Path(ident, alias) => visitor.visit_use_tree_path(self, ident, alias), + UseTreeKind::List(use_trees) => { + if visitor.visit_use_tree_list(self, use_trees) { + for use_tree in use_trees { + use_tree.accept(visitor); + } + } + } + } + } +} + +impl NoirStruct { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_noir_struct(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for (_name, unresolved_type) in &self.fields { + unresolved_type.accept(visitor); + } + } +} + +impl NoirTypeAlias { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_noir_type_alias(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.typ.accept(visitor); + } +} + +impl ModuleDeclaration { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + visitor.visit_module_declaration(self, span); + } +} + +impl Expression { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_expression(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match &self.kind { + ExpressionKind::Literal(literal) => literal.accept(self.span, visitor), + ExpressionKind::Block(block_expression) => { + block_expression.accept(Some(self.span), visitor); + } + ExpressionKind::Prefix(prefix_expression) => { + prefix_expression.accept(self.span, visitor); + } + ExpressionKind::Index(index_expression) => { + index_expression.accept(self.span, visitor); + } + ExpressionKind::Call(call_expression) => { + call_expression.accept(self.span, visitor); + } + ExpressionKind::MethodCall(method_call_expression) => { + method_call_expression.accept(self.span, visitor); + } + ExpressionKind::Constructor(constructor_expression) => { + constructor_expression.accept(self.span, visitor); + } + ExpressionKind::MemberAccess(member_access_expression) => { + member_access_expression.accept(self.span, visitor); + } + ExpressionKind::Cast(cast_expression) => { + cast_expression.accept(self.span, visitor); + } + ExpressionKind::Infix(infix_expression) => { + infix_expression.accept(self.span, visitor); + } + ExpressionKind::If(if_expression) => { + if_expression.accept(self.span, visitor); + } + ExpressionKind::Tuple(expressions) => { + if visitor.visit_tuple(expressions, self.span) { + visit_expressions(expressions, visitor); + } + } + ExpressionKind::Lambda(lambda) => lambda.accept(self.span, visitor), + ExpressionKind::Parenthesized(expression) => { + if visitor.visit_parenthesized(expression, self.span) { + expression.accept(visitor); + } + } + ExpressionKind::Unquote(expression) => { + if visitor.visit_unquote(expression, self.span) { + expression.accept(visitor); + } + } + ExpressionKind::Comptime(block_expression, _) => { + if visitor.visit_comptime_expression(block_expression, self.span) { + block_expression.accept(None, visitor); + } + } + ExpressionKind::Unsafe(block_expression, _) => { + if visitor.visit_unsafe(block_expression, self.span) { + block_expression.accept(None, visitor); + } + } + ExpressionKind::Variable(path) => { + if visitor.visit_variable(path, self.span) { + path.accept(visitor); + } + } + ExpressionKind::AsTraitPath(as_trait_path) => { + as_trait_path.accept(self.span, visitor); + } + ExpressionKind::Quote(tokens) => visitor.visit_quote(tokens), + ExpressionKind::Resolved(expr_id) => visitor.visit_resolved_expression(*expr_id), + ExpressionKind::Interned(id) => visitor.visit_interned_expression(*id), + ExpressionKind::Error => visitor.visit_error_expression(), + } + } +} + +impl Literal { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_literal(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + Literal::Array(array_literal) => { + if visitor.visit_literal_array(array_literal) { + array_literal.accept(visitor); + } + } + Literal::Slice(array_literal) => { + if visitor.visit_literal_slice(array_literal) { + array_literal.accept(visitor); + } + } + Literal::Bool(value) => visitor.visit_literal_bool(*value), + Literal::Integer(value, negative) => visitor.visit_literal_integer(*value, *negative), + Literal::Str(str) => visitor.visit_literal_str(str), + Literal::RawStr(str, length) => visitor.visit_literal_raw_str(str, *length), + Literal::FmtStr(str) => visitor.visit_literal_fmt_str(str), + Literal::Unit => visitor.visit_literal_unit(), + } + } +} + +impl BlockExpression { + pub fn accept(&self, span: Option, visitor: &mut impl Visitor) { + if visitor.visit_block_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for statement in &self.statements { + statement.accept(visitor); + } + } +} + +impl PrefixExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_prefix_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.rhs.accept(visitor); + } +} + +impl IndexExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_index_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.collection.accept(visitor); + self.index.accept(visitor); + } +} + +impl CallExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_call_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.func.accept(visitor); + visit_expressions(&self.arguments, visitor); + } +} + +impl MethodCallExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_method_call_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.object.accept(visitor); + visit_expressions(&self.arguments, visitor); + } +} + +impl ConstructorExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_constructor_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.type_name.accept(visitor); + + for (_field_name, expression) in &self.fields { + expression.accept(visitor); + } + } +} + +impl MemberAccessExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_member_access_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.lhs.accept(visitor); + } +} + +impl CastExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_cast_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.lhs.accept(visitor); + } +} + +impl InfixExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_infix_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.lhs.accept(visitor); + self.rhs.accept(visitor); + } +} + +impl IfExpression { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_if_expression(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.condition.accept(visitor); + self.consequence.accept(visitor); + if let Some(alternative) = &self.alternative { + alternative.accept(visitor); + } + } +} + +impl Lambda { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_lambda(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + for (_, unresolved_type) in &self.parameters { + unresolved_type.accept(visitor); + } + + self.body.accept(visitor); + } +} + +impl ArrayLiteral { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_array_literal(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + ArrayLiteral::Standard(expressions) => { + if visitor.visit_array_literal_standard(expressions) { + visit_expressions(expressions, visitor); + } + } + ArrayLiteral::Repeated { repeated_element, length } => { + if visitor.visit_array_literal_repeated(repeated_element, length) { + repeated_element.accept(visitor); + length.accept(visitor); + } + } + } + } +} + +impl Statement { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_statement(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match &self.kind { + StatementKind::Let(let_statement) => { + let_statement.accept(visitor); + } + StatementKind::Constrain(constrain_statement) => { + constrain_statement.accept(visitor); + } + StatementKind::Expression(expression) => { + expression.accept(visitor); + } + StatementKind::Assign(assign_statement) => { + assign_statement.accept(visitor); + } + StatementKind::For(for_loop_statement) => { + for_loop_statement.accept(visitor); + } + StatementKind::Comptime(statement) => { + if visitor.visit_comptime_statement(statement) { + statement.accept(visitor); + } + } + StatementKind::Semi(expression) => { + expression.accept(visitor); + } + StatementKind::Break => visitor.visit_break(), + StatementKind::Continue => visitor.visit_continue(), + StatementKind::Interned(id) => visitor.visit_interned_statement(*id), + StatementKind::Error => visitor.visit_error_statement(), + } + } +} + +impl LetStatement { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_let_statement(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.pattern.accept(visitor); + self.r#type.accept(visitor); + self.expression.accept(visitor); + } +} + +impl ConstrainStatement { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_constrain_statement(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.0.accept(visitor); + + if let Some(exp) = &self.1 { + exp.accept(visitor); + } + } +} + +impl AssignStatement { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_assign_statement(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.lvalue.accept(visitor); + self.expression.accept(visitor); + } +} + +impl ForLoopStatement { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_for_loop_statement(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.range.accept(visitor); + self.block.accept(visitor); + } +} + +impl LValue { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_lvalue(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + LValue::Ident(ident) => visitor.visit_lvalue_ident(ident), + LValue::MemberAccess { object, field_name, span } => { + if visitor.visit_lvalue_member_access(object, field_name, *span) { + object.accept(visitor); + } + } + LValue::Index { array, index, span } => { + if visitor.visit_lvalue_index(array, index, *span) { + array.accept(visitor); + index.accept(visitor); + } + } + LValue::Dereference(lvalue, span) => { + if visitor.visit_lvalue_dereference(lvalue, *span) { + lvalue.accept(visitor); + } + } + LValue::Interned(id, span) => visitor.visit_lvalue_interned(*id, *span), + } + } +} + +impl ForRange { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_for_range(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + ForRange::Range(start, end) => { + start.accept(visitor); + end.accept(visitor); + } + ForRange::Array(expression) => expression.accept(visitor), + } + } +} + +impl AsTraitPath { + pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_as_trait_path(self, span) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + self.trait_path.accept(visitor); + self.trait_generics.accept(visitor); + } +} + +impl UnresolvedType { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_unresolved_type(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match &self.typ { + UnresolvedTypeData::Array(unresolved_type_expression, unresolved_type) => { + if visitor.visit_array_type(unresolved_type_expression, unresolved_type, self.span) + { + unresolved_type.accept(visitor); + } + } + UnresolvedTypeData::Slice(unresolved_type) => { + if visitor.visit_slice_type(unresolved_type, self.span) { + unresolved_type.accept(visitor); + } + } + UnresolvedTypeData::Parenthesized(unresolved_type) => { + if visitor.visit_parenthesized_type(unresolved_type, self.span) { + unresolved_type.accept(visitor); + } + } + UnresolvedTypeData::Named(path, generic_type_args, _) => { + if visitor.visit_named_type(path, generic_type_args, self.span) { + path.accept(visitor); + generic_type_args.accept(visitor); + } + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + if visitor.visit_trait_as_type(path, generic_type_args, self.span) { + path.accept(visitor); + generic_type_args.accept(visitor); + } + } + UnresolvedTypeData::MutableReference(unresolved_type) => { + if visitor.visit_mutable_reference_type(unresolved_type, self.span) { + unresolved_type.accept(visitor); + } + } + UnresolvedTypeData::Tuple(unresolved_types) => { + if visitor.visit_tuple_type(unresolved_types, self.span) { + visit_unresolved_types(unresolved_types, visitor); + } + } + UnresolvedTypeData::Function(args, ret, env, unconstrained) => { + if visitor.visit_function_type(args, ret, env, *unconstrained, self.span) { + visit_unresolved_types(args, visitor); + ret.accept(visitor); + env.accept(visitor); + } + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + if visitor.visit_as_trait_path_type(as_trait_path, self.span) { + as_trait_path.accept(self.span, visitor); + } + } + UnresolvedTypeData::Expression(expr) => visitor.visit_expression_type(expr, self.span), + UnresolvedTypeData::FormatString(expr, typ) => { + if visitor.visit_format_string_type(expr, typ, self.span) { + typ.accept(visitor); + } + } + UnresolvedTypeData::String(expr) => visitor.visit_string_type(expr, self.span), + UnresolvedTypeData::Unspecified => visitor.visit_unspecified_type(self.span), + UnresolvedTypeData::Quoted(typ) => visitor.visit_quoted_type(typ, self.span), + UnresolvedTypeData::FieldElement => visitor.visit_field_element_type(self.span), + UnresolvedTypeData::Integer(signdness, size) => { + visitor.visit_integer_type(*signdness, *size, self.span); + } + UnresolvedTypeData::Bool => visitor.visit_bool_type(self.span), + UnresolvedTypeData::Unit => visitor.visit_unit_type(self.span), + UnresolvedTypeData::Resolved(id) => visitor.visit_resolved_type(*id, self.span), + UnresolvedTypeData::Interned(id) => visitor.visit_interned_type(*id, self.span), + UnresolvedTypeData::Error => visitor.visit_error_type(self.span), + } + } +} + +impl Path { + pub fn accept(&self, visitor: &mut impl Visitor) { + visitor.visit_path(self); + } +} + +impl GenericTypeArgs { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_generic_type_args(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + visit_unresolved_types(&self.ordered_args, visitor); + for (_name, typ) in &self.named_args { + typ.accept(visitor); + } + } +} + +impl FunctionReturnType { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_function_return_type(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + FunctionReturnType::Default(_) => (), + FunctionReturnType::Ty(unresolved_type) => { + unresolved_type.accept(visitor); + } + } + } +} + +impl Pattern { + pub fn accept(&self, visitor: &mut impl Visitor) { + if visitor.visit_pattern(self) { + self.accept_children(visitor); + } + } + + pub fn accept_children(&self, visitor: &mut impl Visitor) { + match self { + Pattern::Identifier(ident) => visitor.visit_identifier_pattern(ident), + Pattern::Mutable(pattern, span, is_synthesized) => { + if visitor.visit_mutable_pattern(pattern, *span, *is_synthesized) { + pattern.accept(visitor); + } + } + Pattern::Tuple(patterns, span) => { + if visitor.visit_tuple_pattern(patterns, *span) { + for pattern in patterns { + pattern.accept(visitor); + } + } + } + Pattern::Struct(path, fields, span) => { + if visitor.visit_struct_pattern(path, fields, *span) { + path.accept(visitor); + for (_, pattern) in fields { + pattern.accept(visitor); + } + } + } + } + } +} + +fn visit_expressions(expressions: &[Expression], visitor: &mut impl Visitor) { + for expression in expressions { + expression.accept(visitor); + } +} + +fn visit_unresolved_types(unresolved_type: &[UnresolvedType], visitor: &mut impl Visitor) { + for unresolved_type in unresolved_type { + unresolved_type.accept(visitor); + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs index 935acc4e6d0..fe027969473 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -322,6 +322,9 @@ impl DebugInstrumenter { ast::LValue::Dereference(_ref, _span) => { unimplemented![] } + ast::LValue::Interned(..) => { + unimplemented![] + } } } build_assign_member_stmt( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index 01b4585640f..3e71f167802 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -46,6 +46,7 @@ impl<'context> Elaborator<'context> { self.crate_id, self.debug_comptime_in_file, self.enable_arithmetic_generics, + self.interpreter_call_stack.clone(), ); elaborator.function_context.push(FunctionContext::default()); @@ -167,7 +168,7 @@ impl<'context> Elaborator<'context> { /// Parses an attribute in the form of a function call (e.g. `#[foo(a b, c d)]`) into /// the function and quoted arguments called (e.g. `("foo", vec![(a b, location), (c d, location)])`) #[allow(clippy::type_complexity)] - fn parse_attribute( + pub(crate) fn parse_attribute( annotation: &str, file: FileId, ) -> Result)>, (CompilationError, FileId)> { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index cf0b4f4071a..beede7a3a30 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -62,6 +62,11 @@ impl<'context> Elaborator<'context> { self.elaborate_unsafe_block(block_expression) } ExpressionKind::Resolved(id) => return (id, self.interner.id_type(id)), + ExpressionKind::Interned(id) => { + let expr_kind = self.interner.get_expression_kind(id); + let expr = Expression::new(expr_kind.clone(), expr.span); + return self.elaborate_expression(expr); + } ExpressionKind::Error => (HirExpression::Error, Type::Error), ExpressionKind::Unquote(_) => { self.push_err(ResolverError::UnquoteUsedOutsideQuote { span: expr.span }); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index 53b46536078..e84ed76050d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -11,7 +11,7 @@ use crate::{ UnresolvedTypeAlias, }, def_map::DefMaps, - resolution::{errors::ResolverError, path_resolver::PathResolver}, + resolution::errors::ResolverError, scope::ScopeForest as GenericScopeForest, type_check::{generics::TraitGenerics, TypeCheckError}, }, @@ -36,7 +36,7 @@ use crate::{ hir::{ def_collector::{dc_crate::CollectedItems, errors::DefCollectorErrorKind}, def_map::{LocalModuleId, ModuleDefId, ModuleId, MAIN_FUNCTION}, - resolution::{import::PathResolution, path_resolver::StandardPathResolver}, + resolution::import::PathResolution, Context, }, hir_def::function::{FuncMeta, HirFunction}, @@ -168,6 +168,8 @@ pub struct Elaborator<'context> { /// Temporary flag to enable the experimental arithmetic generics feature enable_arithmetic_generics: bool, + + pub(crate) interpreter_call_stack: im::Vector, } #[derive(Default)] @@ -191,6 +193,7 @@ impl<'context> Elaborator<'context> { crate_id: CrateId, debug_comptime_in_file: Option, enable_arithmetic_generics: bool, + interpreter_call_stack: im::Vector, ) -> Self { Self { scopes: ScopeForest::default(), @@ -214,6 +217,7 @@ impl<'context> Elaborator<'context> { unresolved_globals: BTreeMap::new(), enable_arithmetic_generics, current_trait: None, + interpreter_call_stack, } } @@ -229,6 +233,7 @@ impl<'context> Elaborator<'context> { crate_id, debug_comptime_in_file, enable_arithmetic_generics, + im::Vector::new(), ) } @@ -630,10 +635,8 @@ impl<'context> Elaborator<'context> { } } - pub fn resolve_module_by_path(&self, path: Path) -> Option { - let path_resolver = StandardPathResolver::new(self.module_id()); - - match path_resolver.resolve(self.def_maps, path.clone(), &mut None) { + pub fn resolve_module_by_path(&mut self, path: Path) -> Option { + match self.resolve_path(path.clone()) { Ok(PathResolution { module_def_id: ModuleDefId::ModuleId(module_id), error }) => { if error.is_some() { None @@ -646,9 +649,7 @@ impl<'context> Elaborator<'context> { } fn resolve_trait_by_path(&mut self, path: Path) -> Option { - let path_resolver = StandardPathResolver::new(self.module_id()); - - let error = match path_resolver.resolve(self.def_maps, path.clone(), &mut None) { + let error = match self.resolve_path(path.clone()) { Ok(PathResolution { module_def_id: ModuleDefId::TraitId(trait_id), error }) => { if let Some(error) = error { self.push_err(error); @@ -815,6 +816,11 @@ impl<'context> Elaborator<'context> { None }; + let attributes = func.secondary_attributes().iter(); + let attributes = + attributes.filter_map(|secondary_attribute| secondary_attribute.as_custom()); + let attributes = attributes.map(|str| str.to_string()).collect(); + let meta = FuncMeta { name: name_ident, kind: func.kind, @@ -838,6 +844,7 @@ impl<'context> Elaborator<'context> { function_body: FunctionBody::Unresolved(func.kind, body, func.def.span), self_type: self.self_type.clone(), source_file: self.file, + custom_attributes: attributes, }; self.interner.push_fn_meta(meta, func_id); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs index 3288d10b62e..a51fd737f74 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -2,6 +2,7 @@ use noirc_errors::{Location, Spanned}; use crate::ast::{PathKind, ERROR_IDENT}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::hir::resolution::import::{PathResolution, PathResolutionResult}; use crate::hir::resolution::path_resolver::{PathResolver, StandardPathResolver}; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; use crate::macros_api::Ident; @@ -29,7 +30,7 @@ type ScopeTree = GenericScopeTree; impl<'context> Elaborator<'context> { pub(super) fn lookup(&mut self, path: Path) -> Result { let span = path.span(); - let id = self.resolve_path(path)?; + let id = self.resolve_path_or_error(path)?; T::try_from(id).ok_or_else(|| ResolverError::Expected { expected: T::description(), got: id.as_str().to_owned(), @@ -42,15 +43,37 @@ impl<'context> Elaborator<'context> { ModuleId { krate: self.crate_id, local_id: self.local_module } } - pub(super) fn resolve_path(&mut self, path: Path) -> Result { + pub(super) fn resolve_path_or_error( + &mut self, + path: Path, + ) -> Result { + let path_resolution = self.resolve_path(path)?; + + if let Some(error) = path_resolution.error { + self.push_err(error); + } + + Ok(path_resolution.module_def_id) + } + + pub(super) fn resolve_path(&mut self, path: Path) -> PathResolutionResult { let mut module_id = self.module_id(); let mut path = path; + if path.kind == PathKind::Plain { + let def_map = self.def_maps.get_mut(&self.crate_id).unwrap(); + let module_data = &mut def_map.modules[module_id.local_id.0]; + module_data.use_import(&path.segments[0].ident); + } + if path.kind == PathKind::Plain && path.first_name() == SELF_TYPE_NAME { if let Some(Type::Struct(struct_type, _)) = &self.self_type { let struct_type = struct_type.borrow(); if path.segments.len() == 1 { - return Ok(ModuleDefId::TypeId(struct_type.id)); + return Ok(PathResolution { + module_def_id: ModuleDefId::TypeId(struct_type.id), + error: None, + }); } module_id = struct_type.id.module_id(); @@ -65,11 +88,7 @@ impl<'context> Elaborator<'context> { self.resolve_path_in_module(path, module_id) } - fn resolve_path_in_module( - &mut self, - path: Path, - module_id: ModuleId, - ) -> Result { + fn resolve_path_in_module(&mut self, path: Path, module_id: ModuleId) -> PathResolutionResult { let resolver = StandardPathResolver::new(module_id); let path_resolution; @@ -99,11 +118,7 @@ impl<'context> Elaborator<'context> { path_resolution = resolver.resolve(self.def_maps, path, &mut None)?; } - if let Some(error) = path_resolution.error { - self.push_err(error); - } - - Ok(path_resolution.module_def_id) + Ok(path_resolution) } pub(super) fn get_struct(&self, type_id: StructId) -> Shared { @@ -150,7 +165,7 @@ impl<'context> Elaborator<'context> { pub(super) fn lookup_global(&mut self, path: Path) -> Result { let span = path.span(); - let id = self.resolve_path(path)?; + let id = self.resolve_path_or_error(path)?; if let Some(function) = TryFromModuleDefId::try_from(id) { return Ok(self.interner.function_definition_id(function)); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 0bb8641b6b3..d7d330f891a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -39,11 +39,16 @@ impl<'context> Elaborator<'context> { let (expr, _typ) = self.elaborate_expression(expr); (HirStatement::Semi(expr), Type::Unit) } + StatementKind::Interned(id) => { + let kind = self.interner.get_statement_kind(id); + let statement = Statement { kind: kind.clone(), span: statement.span }; + self.elaborate_statement_value(statement) + } StatementKind::Error => (HirStatement::Error, Type::Error), } } - pub(super) fn elaborate_statement(&mut self, statement: Statement) -> (StmtId, Type) { + pub(crate) fn elaborate_statement(&mut self, statement: Statement) -> (StmtId, Type) { let span = statement.span; let (hir_statement, typ) = self.elaborate_statement_value(statement); let id = self.interner.push_stmt(hir_statement); @@ -357,6 +362,10 @@ impl<'context> Elaborator<'context> { let lvalue = HirLValue::Dereference { lvalue, element_type, location }; (lvalue, typ, true) } + LValue::Interned(id, span) => { + let lvalue = self.interner.get_lvalue(id, span).clone(); + self.elaborate_lvalue(lvalue, assign_span) + } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index 44bded6b92f..e41234a5be5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -158,6 +158,10 @@ impl<'context> Elaborator<'context> { Parenthesized(typ) => self.resolve_type_inner(*typ, kind), Resolved(id) => self.interner.get_quoted_type(id).clone(), AsTraitPath(path) => self.resolve_as_trait_path(*path), + Interned(id) => { + let typ = self.interner.get_unresolved_type_data(id).clone(); + return self.resolve_type_inner(UnresolvedType { typ, span }, kind); + } }; let location = Location::new(named_path_span.unwrap_or(typ.span), self.file); @@ -421,7 +425,7 @@ impl<'context> Elaborator<'context> { } // If we cannot find a local generic of the same name, try to look up a global - match self.resolve_path(path.clone()) { + match self.resolve_path_or_error(path.clone()) { Ok(ModuleDefId::GlobalId(id)) => { if let Some(current_item) = self.current_item { self.interner.add_global_dependency(current_item, id); @@ -445,14 +449,19 @@ impl<'context> Elaborator<'context> { }) } UnresolvedTypeExpression::Constant(int, _) => Type::Constant(int), - UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, _) => { + UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); let lhs = self.convert_expression_type(*lhs); let rhs = self.convert_expression_type(*rhs); match (lhs, rhs) { (Type::Constant(lhs), Type::Constant(rhs)) => { - Type::Constant(op.function(lhs, rhs)) + if let Some(result) = op.function(lhs, rhs) { + Type::Constant(result) + } else { + self.push_err(ResolverError::OverflowInType { lhs, op, rhs, span }); + Type::Error + } } (lhs, rhs) => { if !self.enable_arithmetic_generics { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs index fd916485eaf..cfee6bcedac 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -56,6 +56,7 @@ pub enum InterpreterError { FailingConstraint { message: Option, location: Location, + call_stack: im::Vector, }, NoMethodFound { name: String, @@ -353,12 +354,20 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let msg = format!("Expected a `bool` but found `{typ}`"); CustomDiagnostic::simple_error(msg, String::new(), location.span) } - InterpreterError::FailingConstraint { message, location } => { + InterpreterError::FailingConstraint { message, location, call_stack } => { let (primary, secondary) = match message { Some(msg) => (msg.clone(), "Assertion failed".into()), None => ("Assertion failed".into(), String::new()), }; - CustomDiagnostic::simple_error(primary, secondary, location.span) + let mut diagnostic = + CustomDiagnostic::simple_error(primary, secondary, location.span); + + // Only take at most 3 frames starting from the top of the stack to avoid producing too much output + for frame in call_stack.iter().rev().take(3) { + diagnostic.add_secondary_with_file("".to_string(), frame.span, frame.file); + } + + diagnostic } InterpreterError::NoMethodFound { name, typ, location } => { let msg = format!("No method named `{name}` found for type `{typ}`"); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 33f8c9d8332..4980045c68d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -70,7 +70,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { current_function: Option, ) -> Self { let bound_generics = Vec::new(); - Self { elaborator, crate_id, current_function, bound_generics, in_loop: false } + let in_loop = false; + Self { elaborator, crate_id, current_function, bound_generics, in_loop } } pub(crate) fn call_function( @@ -99,8 +100,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } self.remember_bindings(&instantiation_bindings, &impl_bindings); + self.elaborator.interpreter_call_stack.push_back(location); + let result = self.call_function_inner(function, arguments, location); + self.elaborator.interpreter_call_stack.pop_back(); undo_instantiation_bindings(impl_bindings); undo_instantiation_bindings(instantiation_bindings); self.rebind_generics_from_previous_function(); @@ -1462,7 +1466,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let message = constrain.2.and_then(|expr| self.evaluate(expr).ok()); let message = message.map(|value| value.display(self.elaborator.interner).to_string()); - Err(InterpreterError::FailingConstraint { location, message }) + let call_stack = self.elaborator.interpreter_call_stack.clone(); + Err(InterpreterError::FailingConstraint { location, message, call_stack }) } value => { let location = self.elaborator.interner.expr_location(&constrain.0); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 8aa8e92408f..d52d4ca8c71 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -6,32 +6,36 @@ use std::{ use acvm::{AcirField, FieldElement}; use builtin_helpers::{ block_expression_to_value, check_argument_count, check_function_not_yet_resolved, - check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_function_def, - get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, - get_trait_impl, get_tuple, get_type, get_u32, get_unresolved_type, hir_pattern_to_tokens, - mutate_func_meta_type, parse, parse_tokens, replace_func_meta_parameters, - replace_func_meta_return_type, + check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field, + get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, + get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, get_u32, + get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse, + replace_func_meta_parameters, replace_func_meta_return_type, }; +use chumsky::{prelude::choice, Parser}; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; +use num_bigint::BigUint; use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ - ArrayLiteral, Expression, ExpressionKind, FunctionKind, FunctionReturnType, IntegerBitSize, - Literal, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, + ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, FunctionKind, + FunctionReturnType, IntegerBitSize, LValue, Literal, Statement, StatementKind, UnaryOp, + UnresolvedType, UnresolvedTypeData, Visibility, }, + elaborator::Elaborator, hir::comptime::{ errors::IResult, - value::{add_token_spans, ExprValue}, + value::{ExprValue, TypedExpr}, InterpreterError, Value, }, hir_def::function::FunctionBody, - macros_api::{ModuleDefId, NodeInterner, Signedness}, + macros_api::{HirExpression, HirLiteral, ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, parser::{self}, - token::{SpannedToken, Token}, + token::Token, QuotedType, Shared, Type, }; @@ -49,37 +53,51 @@ impl<'local, 'context> Interpreter<'local, 'context> { location: Location, ) -> IResult { let interner = &mut self.elaborator.interner; + let call_stack = &self.elaborator.interpreter_call_stack; match name { "array_as_str_unchecked" => array_as_str_unchecked(interner, arguments, location), "array_len" => array_len(interner, arguments, location), + "assert_constant" => Ok(Value::Bool(true)), "as_slice" => as_slice(interner, arguments, location), - "expr_as_array" => expr_as_array(arguments, return_type, location), - "expr_as_assign" => expr_as_assign(arguments, return_type, location), - "expr_as_binary_op" => expr_as_binary_op(arguments, return_type, location), - "expr_as_block" => expr_as_block(arguments, return_type, location), - "expr_as_bool" => expr_as_bool(arguments, return_type, location), - "expr_as_cast" => expr_as_cast(arguments, return_type, location), - "expr_as_comptime" => expr_as_comptime(arguments, return_type, location), - "expr_as_function_call" => expr_as_function_call(arguments, return_type, location), - "expr_as_if" => expr_as_if(arguments, return_type, location), - "expr_as_index" => expr_as_index(arguments, return_type, location), - "expr_as_integer" => expr_as_integer(arguments, return_type, location), - "expr_as_member_access" => expr_as_member_access(arguments, return_type, location), - "expr_as_method_call" => expr_as_method_call(arguments, return_type, location), + "expr_as_array" => expr_as_array(interner, arguments, return_type, location), + "expr_as_assert" => expr_as_assert(interner, arguments, return_type, location), + "expr_as_assign" => expr_as_assign(interner, arguments, return_type, location), + "expr_as_binary_op" => expr_as_binary_op(interner, arguments, return_type, location), + "expr_as_block" => expr_as_block(interner, arguments, return_type, location), + "expr_as_bool" => expr_as_bool(interner, arguments, return_type, location), + "expr_as_cast" => expr_as_cast(interner, arguments, return_type, location), + "expr_as_comptime" => expr_as_comptime(interner, arguments, return_type, location), + "expr_as_function_call" => { + expr_as_function_call(interner, arguments, return_type, location) + } + "expr_as_if" => expr_as_if(interner, arguments, return_type, location), + "expr_as_index" => expr_as_index(interner, arguments, return_type, location), + "expr_as_integer" => expr_as_integer(interner, arguments, return_type, location), + "expr_as_member_access" => { + expr_as_member_access(interner, arguments, return_type, location) + } + "expr_as_method_call" => { + expr_as_method_call(interner, arguments, return_type, location) + } "expr_as_repeated_element_array" => { - expr_as_repeated_element_array(arguments, return_type, location) + expr_as_repeated_element_array(interner, arguments, return_type, location) } "expr_as_repeated_element_slice" => { - expr_as_repeated_element_slice(arguments, return_type, location) + expr_as_repeated_element_slice(interner, arguments, return_type, location) } - "expr_as_slice" => expr_as_slice(arguments, return_type, location), - "expr_as_tuple" => expr_as_tuple(arguments, return_type, location), - "expr_as_unary_op" => expr_as_unary_op(arguments, return_type, location), - "expr_as_unsafe" => expr_as_unsafe(arguments, return_type, location), - "expr_has_semicolon" => expr_has_semicolon(arguments, location), - "expr_is_break" => expr_is_break(arguments, location), - "expr_is_continue" => expr_is_continue(arguments, location), + "expr_as_slice" => expr_as_slice(interner, arguments, return_type, location), + "expr_as_tuple" => expr_as_tuple(interner, arguments, return_type, location), + "expr_as_unary_op" => expr_as_unary_op(interner, arguments, return_type, location), + "expr_as_unsafe" => expr_as_unsafe(interner, arguments, return_type, location), + "expr_has_semicolon" => expr_has_semicolon(interner, arguments, location), + "expr_is_break" => expr_is_break(interner, arguments, location), + "expr_is_continue" => expr_is_continue(interner, arguments, location), + "expr_resolve" => expr_resolve(self, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), + "function_def_body" => function_def_body(interner, arguments, location), + "function_def_has_named_attribute" => { + function_def_has_named_attribute(interner, arguments, location) + } "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), "function_def_return_type" => function_def_return_type(interner, arguments, location), @@ -102,14 +120,15 @@ impl<'local, 'context> Interpreter<'local, 'context> { "quoted_as_type" => quoted_as_type(self, arguments, location), "quoted_eq" => quoted_eq(arguments, location), "slice_insert" => slice_insert(interner, arguments, location), - "slice_pop_back" => slice_pop_back(interner, arguments, location), - "slice_pop_front" => slice_pop_front(interner, arguments, location), + "slice_pop_back" => slice_pop_back(interner, arguments, location, call_stack), + "slice_pop_front" => slice_pop_front(interner, arguments, location, call_stack), "slice_push_back" => slice_push_back(interner, arguments, location), "slice_push_front" => slice_push_front(interner, arguments, location), - "slice_remove" => slice_remove(interner, arguments, location), + "slice_remove" => slice_remove(interner, arguments, location, call_stack), "struct_def_as_type" => struct_def_as_type(interner, arguments, location), "struct_def_fields" => struct_def_fields(interner, arguments, location), "struct_def_generics" => struct_def_generics(interner, arguments, location), + "to_le_radix" => to_le_radix(arguments, location), "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), "trait_constraint_hash" => trait_constraint_hash(interner, arguments, location), "trait_def_as_trait_constraint" => { @@ -125,6 +144,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "type_as_constant" => type_as_constant(arguments, return_type, location), "type_as_integer" => type_as_integer(arguments, return_type, location), "type_as_slice" => type_as_slice(arguments, return_type, location), + "type_as_str" => type_as_str(arguments, return_type, location), "type_as_struct" => type_as_struct(arguments, return_type, location), "type_as_tuple" => type_as_tuple(arguments, return_type, location), "type_eq" => type_eq(arguments, location), @@ -135,7 +155,10 @@ impl<'local, 'context> Interpreter<'local, 'context> { "type_is_bool" => type_is_bool(arguments, location), "type_is_field" => type_is_field(arguments, location), "type_of" => type_of(arguments, location), - "unresolved_type_is_field" => unresolved_type_is_field(arguments, location), + "typed_expr_as_function_definition" => { + typed_expr_as_function_definition(interner, arguments, return_type, location) + } + "unresolved_type_is_field" => unresolved_type_is_field(interner, arguments, location), "zeroed" => zeroed(return_type), _ => { let item = format!("Comptime evaluation for builtin function {name}"); @@ -145,8 +168,16 @@ impl<'local, 'context> Interpreter<'local, 'context> { } } -fn failing_constraint(message: impl Into, location: Location) -> IResult { - Err(InterpreterError::FailingConstraint { message: Some(message.into()), location }) +fn failing_constraint( + message: impl Into, + location: Location, + call_stack: &im::Vector, +) -> IResult { + Err(InterpreterError::FailingConstraint { + message: Some(message.into()), + location, + call_stack: call_stack.clone(), + }) } fn array_len( @@ -278,6 +309,7 @@ fn slice_remove( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, + call_stack: &im::Vector, ) -> IResult { let (slice, index) = check_two_arguments(arguments, location)?; @@ -285,7 +317,7 @@ fn slice_remove( let index = get_u32(index)? as usize; if values.is_empty() { - return failing_constraint("slice_remove called on empty slice", location); + return failing_constraint("slice_remove called on empty slice", location, call_stack); } if index >= values.len() { @@ -293,7 +325,7 @@ fn slice_remove( "slice_remove: index {index} is out of bounds for a slice of length {}", values.len() ); - return failing_constraint(message, location); + return failing_constraint(message, location, call_stack); } let element = values.remove(index); @@ -316,13 +348,14 @@ fn slice_pop_front( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, + call_stack: &im::Vector, ) -> IResult { let argument = check_one_argument(arguments, location)?; let (mut values, typ) = get_slice(interner, argument)?; match values.pop_front() { Some(element) => Ok(Value::Tuple(vec![element, Value::Slice(values, typ)])), - None => failing_constraint("slice_pop_front called on empty slice", location), + None => failing_constraint("slice_pop_front called on empty slice", location, call_stack), } } @@ -330,13 +363,14 @@ fn slice_pop_back( interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, location: Location, + call_stack: &im::Vector, ) -> IResult { let argument = check_one_argument(arguments, location)?; let (mut values, typ) = get_slice(interner, argument)?; match values.pop_back() { Some(element) => Ok(Value::Tuple(vec![Value::Slice(values, typ), element])), - None => failing_constraint("slice_pop_back called on empty slice", location), + None => failing_constraint("slice_pop_back called on empty slice", location, call_stack), } } @@ -361,10 +395,14 @@ fn quoted_as_expr( ) -> IResult { let argument = check_one_argument(arguments, location)?; - let expr = parse(argument, parser::expression(), "an expression").ok(); - let value = expr.map(|expr| Value::expression(expr.kind)); + let expr_parser = parser::expression().map(|expr| Value::expression(expr.kind)); + let statement_parser = parser::fresh_statement().map(Value::statement); + let lvalue_parser = parser::lvalue(parser::expression()).map(Value::lvalue); + let parser = choice((expr_parser, statement_parser, lvalue_parser)); + + let expr = parse(argument, parser, "an expression").ok(); - option(return_type, value) + option(return_type, expr) } // fn as_module(quoted: Quoted) -> Option @@ -417,6 +455,35 @@ fn quoted_as_type( Ok(Value::Type(typ)) } +fn to_le_radix(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let (value, radix, limb_count) = check_three_arguments(arguments, location)?; + + let value = get_field(value)?; + let radix = get_u32(radix)?; + let limb_count = get_u32(limb_count)?; + + // Decompose the integer into its radix digits in little endian form. + let decomposed_integer = compute_to_radix(value, radix); + let decomposed_integer = vecmap(0..limb_count as usize, |i| match decomposed_integer.get(i) { + Some(digit) => Value::U8(*digit), + None => Value::U8(0), + }); + Ok(Value::Array( + decomposed_integer.into(), + Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + )) +} + +fn compute_to_radix(field: FieldElement, radix: u32) -> Vec { + let bit_size = u32::BITS - (radix - 1).leading_zeros(); + let radix_big = BigUint::from(radix); + assert_eq!(BigUint::from(2u128).pow(bit_size), radix_big, "ICE: Radix must be a power of 2"); + let big_integer = BigUint::from_bytes_be(&field.to_be_bytes()); + + // Decompose the integer into its radix digits in little endian form. + big_integer.to_radix_le(radix) +} + // fn as_array(self) -> Option<(Type, Type)> fn type_as_array( arguments: Vec<(Value, Location)>, @@ -477,6 +544,21 @@ fn type_as_slice( }) } +// fn as_str(self) -> Option +fn type_as_str( + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + type_as(arguments, return_type, location, |typ| { + if let Type::String(n) = typ { + Some(Value::Type(*n)) + } else { + None + } + }) +} + // fn as_struct(self) -> Option<(StructDefinition, [Type])> fn type_as_struct( arguments: Vec<(Value, Location)>, @@ -709,13 +791,31 @@ fn trait_impl_trait_generic_args( Ok(Value::Slice(trait_generics, slice_type)) } +fn typed_expr_as_function_definition( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let typed_expr = get_typed_expr(self_argument)?; + let option_value = if let TypedExpr::ExprId(expr_id) = typed_expr { + let func_id = interner.lookup_function_from_expr(&expr_id); + func_id.map(Value::FunctionDefinition) + } else { + None + }; + option(return_type, option_value) +} + // fn is_field(self) -> bool fn unresolved_type_is_field( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { let self_argument = check_one_argument(arguments, location)?; - let typ = get_unresolved_type(self_argument)?; + let typ = get_unresolved_type(interner, self_argument)?; Ok(Value::Bool(matches!(typ, UnresolvedTypeData::FieldElement))) } @@ -802,11 +902,12 @@ fn zeroed(return_type: Type) -> IResult { // fn as_array(self) -> Option<[Expr]> fn expr_as_array( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Literal(Literal::Array( ArrayLiteral::Standard(exprs), ))) = expr @@ -820,13 +921,46 @@ fn expr_as_array( }) } +// fn as_assert(self) -> Option<(Expr, Option)> +fn expr_as_assert( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(interner, arguments, return_type.clone(), location, |expr| { + if let ExprValue::Statement(StatementKind::Constrain(constrain)) = expr { + if constrain.2 == ConstrainKind::Assert { + let predicate = Value::expression(constrain.0.kind); + + let option_type = extract_option_generic_type(return_type); + let Type::Tuple(mut tuple_types) = option_type else { + panic!("Expected the return type option generic arg to be a tuple"); + }; + assert_eq!(tuple_types.len(), 2); + + let option_type = tuple_types.pop().unwrap(); + let message = constrain.1.map(|message| Value::expression(message.kind)); + let message = option(option_type, message).ok()?; + + Some(Value::Tuple(vec![predicate, message])) + } else { + None + } + } else { + None + } + }) +} + // fn as_assign(self) -> Option<(Expr, Expr)> fn expr_as_assign( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Statement(StatementKind::Assign(assign)) = expr { let lhs = Value::lvalue(assign.lvalue); let rhs = Value::expression(assign.expression.kind); @@ -839,11 +973,12 @@ fn expr_as_assign( // fn as_binary_op(self) -> Option<(Expr, BinaryOp, Expr)> fn expr_as_binary_op( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type.clone(), location, |expr| { + expr_as(interner, arguments, return_type.clone(), location, |expr| { if let ExprValue::Expression(ExpressionKind::Infix(infix_expr)) = expr { let option_type = extract_option_generic_type(return_type); let Type::Tuple(mut tuple_types) = option_type else { @@ -872,11 +1007,12 @@ fn expr_as_binary_op( // fn as_block(self) -> Option<[Expr]> fn expr_as_block( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Block(block_expr)) = expr { Some(block_expression_to_value(block_expr)) } else { @@ -887,11 +1023,12 @@ fn expr_as_block( // fn as_bool(self) -> Option fn expr_as_bool( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Literal(Literal::Bool(bool))) = expr { Some(Value::Bool(bool)) } else { @@ -902,11 +1039,12 @@ fn expr_as_bool( // fn as_cast(self) -> Option<(Expr, UnresolvedType)> fn expr_as_cast( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Cast(cast)) = expr { let lhs = Value::expression(cast.lhs.kind); let typ = Value::UnresolvedType(cast.r#type.typ); @@ -919,13 +1057,14 @@ fn expr_as_cast( // fn as_comptime(self) -> Option<[Expr]> fn expr_as_comptime( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { use ExpressionKind::Block; - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Comptime(block_expr, _)) = expr { Some(block_expression_to_value(block_expr)) } else if let ExprValue::Statement(StatementKind::Comptime(statement)) = expr { @@ -951,11 +1090,12 @@ fn expr_as_comptime( // fn as_function_call(self) -> Option<(Expr, [Expr])> fn expr_as_function_call( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Call(call_expression)) = expr { let function = Value::expression(call_expression.func.kind); let arguments = call_expression.arguments.into_iter(); @@ -971,11 +1111,12 @@ fn expr_as_function_call( // fn as_if(self) -> Option<(Expr, Expr, Option)> fn expr_as_if( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type.clone(), location, |expr| { + expr_as(interner, arguments, return_type.clone(), location, |expr| { if let ExprValue::Expression(ExpressionKind::If(if_expr)) = expr { // Get the type of `Option` let option_type = extract_option_generic_type(return_type.clone()); @@ -1003,11 +1144,12 @@ fn expr_as_if( // fn as_index(self) -> Option fn expr_as_index( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Index(index_expr)) = expr { Some(Value::Tuple(vec![ Value::expression(index_expr.collection.kind), @@ -1021,27 +1163,36 @@ fn expr_as_index( // fn as_integer(self) -> Option<(Field, bool)> fn expr_as_integer( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type.clone(), location, |expr| { - if let ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) = expr - { + expr_as(interner, arguments, return_type.clone(), location, |expr| match expr { + ExprValue::Expression(ExpressionKind::Literal(Literal::Integer(field, sign))) => { Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) - } else { - None } + ExprValue::Expression(ExpressionKind::Resolved(id)) => { + if let HirExpression::Literal(HirLiteral::Integer(field, sign)) = + interner.expression(&id) + { + Some(Value::Tuple(vec![Value::Field(field), Value::Bool(sign)])) + } else { + None + } + } + _ => None, }) } // fn as_member_access(self) -> Option<(Expr, Quoted)> fn expr_as_member_access( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| match expr { + expr_as(interner, arguments, return_type, location, |expr| match expr { ExprValue::Expression(ExpressionKind::MemberAccess(member_access)) => { let tokens = Rc::new(vec![Token::Ident(member_access.rhs.0.contents.clone())]); Some(Value::Tuple(vec![ @@ -1059,11 +1210,12 @@ fn expr_as_member_access( // fn as_method_call(self) -> Option<(Expr, Quoted, [UnresolvedType], [Expr])> fn expr_as_method_call( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::MethodCall(method_call)) = expr { let object = Value::expression(method_call.object.kind); @@ -1092,11 +1244,12 @@ fn expr_as_method_call( // fn as_repeated_element_array(self) -> Option<(Expr, Expr)> fn expr_as_repeated_element_array( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Literal(Literal::Array( ArrayLiteral::Repeated { repeated_element, length }, ))) = expr @@ -1113,11 +1266,12 @@ fn expr_as_repeated_element_array( // fn as_repeated_element_slice(self) -> Option<(Expr, Expr)> fn expr_as_repeated_element_slice( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Literal(Literal::Slice( ArrayLiteral::Repeated { repeated_element, length }, ))) = expr @@ -1134,11 +1288,12 @@ fn expr_as_repeated_element_slice( // fn as_slice(self) -> Option<[Expr]> fn expr_as_slice( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Literal(Literal::Slice( ArrayLiteral::Standard(exprs), ))) = expr @@ -1154,11 +1309,12 @@ fn expr_as_slice( // fn as_tuple(self) -> Option<[Expr]> fn expr_as_tuple( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Tuple(expressions)) = expr { let expressions = expressions.into_iter().map(|expr| Value::expression(expr.kind)).collect(); @@ -1172,11 +1328,12 @@ fn expr_as_tuple( // fn as_unary_op(self) -> Option<(UnaryOp, Expr)> fn expr_as_unary_op( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type.clone(), location, |expr| { + expr_as(interner, arguments, return_type.clone(), location, |expr| { if let ExprValue::Expression(ExpressionKind::Prefix(prefix_expr)) = expr { let option_type = extract_option_generic_type(return_type); let Type::Tuple(mut tuple_types) = option_type else { @@ -1209,11 +1366,12 @@ fn expr_as_unary_op( // fn as_unsafe(self) -> Option<[Expr]> fn expr_as_unsafe( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, ) -> IResult { - expr_as(arguments, return_type, location, |expr| { + expr_as(interner, arguments, return_type, location, |expr| { if let ExprValue::Expression(ExpressionKind::Unsafe(block_expr, _)) = expr { Some(block_expression_to_value(block_expr)) } else { @@ -1223,28 +1381,41 @@ fn expr_as_unsafe( } // fn as_has_semicolon(self) -> bool -fn expr_has_semicolon(arguments: Vec<(Value, Location)>, location: Location) -> IResult { +fn expr_has_semicolon( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { let self_argument = check_one_argument(arguments, location)?; - let expr_value = get_expr(self_argument)?; + let expr_value = get_expr(interner, self_argument)?; Ok(Value::Bool(matches!(expr_value, ExprValue::Statement(StatementKind::Semi(..))))) } // fn is_break(self) -> bool -fn expr_is_break(arguments: Vec<(Value, Location)>, location: Location) -> IResult { +fn expr_is_break( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { let self_argument = check_one_argument(arguments, location)?; - let expr_value = get_expr(self_argument)?; + let expr_value = get_expr(interner, self_argument)?; Ok(Value::Bool(matches!(expr_value, ExprValue::Statement(StatementKind::Break)))) } // fn is_continue(self) -> bool -fn expr_is_continue(arguments: Vec<(Value, Location)>, location: Location) -> IResult { +fn expr_is_continue( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { let self_argument = check_one_argument(arguments, location)?; - let expr_value = get_expr(self_argument)?; + let expr_value = get_expr(interner, self_argument)?; Ok(Value::Bool(matches!(expr_value, ExprValue::Statement(StatementKind::Continue)))) } // Helper function for implementing the `expr_as_...` functions. fn expr_as( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, @@ -1254,7 +1425,65 @@ where F: FnOnce(ExprValue) -> Option, { let self_argument = check_one_argument(arguments, location)?; - let mut expr_value = get_expr(self_argument)?; + let expr_value = get_expr(interner, self_argument)?; + let expr_value = unwrap_expr_value(interner, expr_value); + + let option_value = f(expr_value); + option(return_type, option_value) +} + +// fn resolve(self, in_function: Option) -> TypedExpr +fn expr_resolve( + interpreter: &mut Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, func) = check_two_arguments(arguments, location)?; + let self_argument_location = self_argument.1; + let expr_value = get_expr(interpreter.elaborator.interner, self_argument)?; + let expr_value = unwrap_expr_value(interpreter.elaborator.interner, expr_value); + + let Value::Struct(fields, _) = func.0 else { + panic!("Expected second argument to be a struct"); + }; + + let is_some = fields.get(&Rc::new("_is_some".to_string())).unwrap(); + let Value::Bool(is_some) = is_some else { + panic!("Expected is_some to be a boolean"); + }; + + let function_to_resolve_in = if *is_some { + let value = fields.get(&Rc::new("_value".to_string())).unwrap(); + let Value::FunctionDefinition(func_id) = value else { + panic!("Expected option value to be a FunctionDefinition"); + }; + Some(*func_id) + } else { + interpreter.current_function + }; + + let value = interpreter.elaborate_item(function_to_resolve_in, |elaborator| match expr_value { + ExprValue::Expression(expression_kind) => { + let expr = Expression { kind: expression_kind, span: self_argument_location.span }; + let (expr_id, _) = elaborator.elaborate_expression(expr); + Value::TypedExpr(TypedExpr::ExprId(expr_id)) + } + ExprValue::Statement(statement_kind) => { + let statement = Statement { kind: statement_kind, span: self_argument_location.span }; + let (stmt_id, _) = elaborator.elaborate_statement(statement); + Value::TypedExpr(TypedExpr::StmtId(stmt_id)) + } + ExprValue::LValue(lvalue) => { + let expr = lvalue.as_expression(); + let (expr_id, _) = elaborator.elaborate_expression(expr); + Value::TypedExpr(TypedExpr::ExprId(expr_id)) + } + }); + + Ok(value) +} + +fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> ExprValue { loop { match expr_value { ExprValue::Expression(ExpressionKind::Parenthesized(expression)) => { @@ -1264,12 +1493,70 @@ where | ExprValue::Statement(StatementKind::Semi(expression)) => { expr_value = ExprValue::Expression(expression.kind); } + ExprValue::Expression(ExpressionKind::Interned(id)) => { + expr_value = ExprValue::Expression(interner.get_expression_kind(id).clone()); + } + ExprValue::Statement(StatementKind::Interned(id)) => { + expr_value = ExprValue::Statement(interner.get_statement_kind(id).clone()); + } + ExprValue::LValue(LValue::Interned(id, span)) => { + expr_value = ExprValue::LValue(interner.get_lvalue(id, span).clone()); + } _ => break, } } + expr_value +} - let option_value = f(expr_value); - option(return_type, option_value) +// fn body(self) -> Expr +fn function_def_body( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let func_id = get_function_def(self_argument)?; + let func_meta = interner.function_meta(&func_id); + if let FunctionBody::Unresolved(_, block_expr, _) = &func_meta.function_body { + Ok(Value::expression(ExpressionKind::Block(block_expr.clone()))) + } else { + Err(InterpreterError::FunctionAlreadyResolved { location }) + } +} + +// fn has_named_attribute(self, name: Quoted) -> bool +fn function_def_has_named_attribute( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, name) = check_two_arguments(arguments, location)?; + let func_id = get_function_def(self_argument)?; + let name = get_quoted(name)?; + let func_meta = interner.function_meta(&func_id); + let attributes = &func_meta.custom_attributes; + if attributes.is_empty() { + return Ok(Value::Bool(false)); + }; + + let name = name.iter().map(|token| token.to_string()).collect::>().join(""); + + for attribute in attributes { + let parse_result = Elaborator::parse_attribute(attribute, location.file); + let Ok(Some((function, _arguments))) = parse_result else { + continue; + }; + + let ExpressionKind::Variable(path) = function.kind else { + continue; + }; + + if path.last_name() == name { + return Ok(Value::Bool(true)); + } + } + + Ok(Value::Bool(false)) } // fn name(self) -> Quoted @@ -1326,32 +1613,30 @@ fn function_def_return_type( Ok(Value::Type(func_meta.return_type().follow_bindings())) } -// fn set_body(self, body: Quoted) +// fn set_body(self, body: Expr) fn function_def_set_body( interpreter: &mut Interpreter, arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { let (self_argument, body_argument) = check_two_arguments(arguments, location)?; - let body_argument_location = body_argument.1; + let body_location = body_argument.1; let func_id = get_function_def(self_argument)?; check_function_not_yet_resolved(interpreter, func_id, location)?; - let body_tokens = get_quoted(body_argument)?; - let mut body_quoted = add_token_spans(body_tokens.clone(), body_argument_location.span); - - // Surround the body in `{ ... }` so we can parse it as a block - body_quoted.0.insert(0, SpannedToken::new(Token::LeftBrace, location.span)); - body_quoted.0.push(SpannedToken::new(Token::RightBrace, location.span)); + let body_argument = get_expr(interpreter.elaborator.interner, body_argument)?; + let statement_kind = match body_argument { + ExprValue::Expression(expression_kind) => StatementKind::Expression(Expression { + kind: expression_kind, + span: body_location.span, + }), + ExprValue::Statement(statement_kind) => statement_kind, + ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), + }; - let body = parse_tokens( - body_tokens, - body_quoted, - body_argument_location, - parser::block(parser::fresh_statement()), - "a block", - )?; + let statement = Statement { kind: statement_kind, span: body_location.span }; + let body = BlockExpression { statements: vec![statement] }; let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id); func_meta.has_body = true; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index a409731a5e4..dd9ea51961e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -4,11 +4,14 @@ use acvm::FieldElement; use noirc_errors::Location; use crate::{ - ast::{BlockExpression, IntegerBitSize, Signedness, UnresolvedTypeData}, + ast::{ + BlockExpression, ExpressionKind, IntegerBitSize, LValue, Signedness, StatementKind, + UnresolvedTypeData, + }, hir::{ comptime::{ errors::IResult, - value::{add_token_spans, ExprValue}, + value::{add_token_spans, ExprValue, TypedExpr}, Interpreter, InterpreterError, Value, }, def_map::ModuleId, @@ -142,9 +145,33 @@ pub(crate) fn get_u32((value, location): (Value, Location)) -> IResult { } } -pub(crate) fn get_expr((value, location): (Value, Location)) -> IResult { +pub(crate) fn get_u64((value, location): (Value, Location)) -> IResult { match value { - Value::Expr(expr) => Ok(expr), + Value::U64(value) => Ok(value), + value => { + let expected = Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour); + type_mismatch(value, expected, location) + } + } +} + +pub(crate) fn get_expr( + interner: &NodeInterner, + (value, location): (Value, Location), +) -> IResult { + match value { + Value::Expr(expr) => match expr { + ExprValue::Expression(ExpressionKind::Interned(id)) => { + Ok(ExprValue::Expression(interner.get_expression_kind(id).clone())) + } + ExprValue::Statement(StatementKind::Interned(id)) => { + Ok(ExprValue::Statement(interner.get_statement_kind(id).clone())) + } + ExprValue::LValue(LValue::Interned(id, _)) => { + Ok(ExprValue::LValue(interner.get_lvalue(id, location.span).clone())) + } + _ => Ok(expr), + }, value => type_mismatch(value, Type::Quoted(QuotedType::Expr), location), } } @@ -200,6 +227,13 @@ pub(crate) fn get_type((value, location): (Value, Location)) -> IResult { } } +pub(crate) fn get_typed_expr((value, location): (Value, Location)) -> IResult { + match value { + Value::TypedExpr(typed_expr) => Ok(typed_expr), + value => type_mismatch(value, Type::Quoted(QuotedType::TypedExpr), location), + } +} + pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult>> { match value { Value::Quoted(tokens) => Ok(tokens), @@ -208,10 +242,18 @@ pub(crate) fn get_quoted((value, location): (Value, Location)) -> IResult IResult { match value { - Value::UnresolvedType(typ) => Ok(typ), + Value::UnresolvedType(typ) => { + if let UnresolvedTypeData::Interned(id) = typ { + let typ = interner.get_unresolved_type_data(id).clone(); + Ok(typ) + } else { + Ok(typ) + } + } value => type_mismatch(value, Type::Quoted(QuotedType::UnresolvedType), location), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs index f7caf84ec42..5ae60bb4d00 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -1,5 +1,6 @@ -use acvm::BlackBoxFunctionSolver; +use acvm::blackbox_solver::BlackBoxFunctionSolver; use bn254_blackbox_solver::Bn254BlackBoxSolver; +use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; @@ -8,7 +9,9 @@ use crate::{ macros_api::NodeInterner, }; -use super::builtin::builtin_helpers::{check_two_arguments, get_array, get_field, get_u32}; +use super::builtin::builtin_helpers::{ + check_one_argument, check_two_arguments, get_array, get_field, get_u32, get_u64, +}; pub(super) fn call_foreign( interner: &mut NodeInterner, @@ -18,6 +21,7 @@ pub(super) fn call_foreign( ) -> IResult { match name { "poseidon2_permutation" => poseidon2_permutation(interner, arguments, location), + "keccakf1600" => keccakf1600(interner, arguments, location), _ => { let item = format!("Comptime evaluation for builtin function {name}"); Err(InterpreterError::Unimplemented { item, location }) @@ -47,3 +51,26 @@ fn poseidon2_permutation( let array = fields.into_iter().map(Value::Field).collect(); Ok(Value::Array(array, typ)) } + +fn keccakf1600( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let input = check_one_argument(arguments, location)?; + let input_location = input.1; + + let (input, typ) = get_array(interner, input)?; + + let input = try_vecmap(input, |integer| get_u64((integer, input_location)))?; + + let mut state = [0u64; 25]; + for (it, input_value) in state.iter_mut().zip(input.iter()) { + *it = *input_value; + } + let result_lanes = acvm::blackbox_solver::keccakf1600(state) + .map_err(|error| InterpreterError::BlackBoxError(error, location))?; + + let array: Vector = result_lanes.into_iter().map(Value::U64).collect(); + Ok(Value::Array(array, typ)) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs index 18f482585ea..b96c4852931 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -9,8 +9,11 @@ use strum_macros::Display; use crate::{ ast::{ - ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, LValue, - Signedness, Statement, StatementKind, UnresolvedTypeData, + ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, + ConstrainStatement, ConstructorExpression, ForLoopStatement, ForRange, Ident, IfExpression, + IndexExpression, InfixExpression, IntegerBitSize, LValue, Lambda, LetStatement, + MemberAccessExpression, MethodCallExpression, PrefixExpression, Signedness, Statement, + StatementKind, UnresolvedTypeData, }, hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, hir_def::{ @@ -21,7 +24,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, - node_interner::{ExprId, FuncId, TraitId, TraitImplId}, + node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, TypeBindings, @@ -66,6 +69,7 @@ pub enum Value { Type(Type), Zeroed(Type), Expr(ExprValue), + TypedExpr(TypedExpr), UnresolvedType(UnresolvedTypeData), } @@ -76,6 +80,12 @@ pub enum ExprValue { LValue(LValue), } +#[derive(Debug, Clone, PartialEq, Eq, Display)] +pub enum TypedExpr { + ExprId(ExprId), + StmtId(StmtId), +} + impl Value { pub(crate) fn expression(expr: ExpressionKind) -> Self { Value::Expr(ExprValue::Expression(expr)) @@ -134,6 +144,7 @@ impl Value { Value::Type(_) => Type::Quoted(QuotedType::Type), Value::Zeroed(typ) => return Cow::Borrowed(typ), Value::Expr(_) => Type::Quoted(QuotedType::Expr), + Value::TypedExpr(_) => Type::Quoted(QuotedType::TypedExpr), Value::UnresolvedType(_) => Type::Quoted(QuotedType::UnresolvedType), }) } @@ -261,7 +272,8 @@ impl Value { statements: vec![Statement { kind: statement, span: location.span }], }) } - Value::Expr(ExprValue::LValue(_)) + Value::Expr(ExprValue::LValue(lvalue)) => lvalue.as_expression().kind, + Value::TypedExpr(..) | Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -386,7 +398,9 @@ impl Value { HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } Value::Quoted(tokens) => HirExpression::Unquote(add_token_spans(tokens, location.span)), - Value::Expr(..) + Value::TypedExpr(TypedExpr::ExprId(expr_id)) => interner.expression(&expr_id), + Value::TypedExpr(TypedExpr::StmtId(..)) + | Value::Expr(..) | Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -417,6 +431,18 @@ impl Value { let token = match self { Value::Quoted(tokens) => return Ok(unwrap_rc(tokens)), Value::Type(typ) => Token::QuotedType(interner.push_quoted_type(typ)), + Value::Expr(ExprValue::Expression(expr)) => { + Token::InternedExpr(interner.push_expression_kind(expr)) + } + Value::Expr(ExprValue::Statement(statement)) => { + Token::InternedStatement(interner.push_statement_kind(statement)) + } + Value::Expr(ExprValue::LValue(lvalue)) => { + Token::InternedLValue(interner.push_lvalue(lvalue)) + } + Value::UnresolvedType(typ) => { + Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ)) + } other => Token::UnquoteMarker(other.into_hir_expression(interner, location)?), }; Ok(vec![token]) @@ -597,10 +623,24 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::ModuleDefinition(_) => write!(f, "(module)"), Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), Value::Type(typ) => write!(f, "{}", typ), - Value::Expr(ExprValue::Expression(expr)) => write!(f, "{}", expr), - Value::Expr(ExprValue::Statement(statement)) => write!(f, "{}", statement), - Value::Expr(ExprValue::LValue(lvalue)) => write!(f, "{}", lvalue), - Value::UnresolvedType(typ) => write!(f, "{}", typ), + Value::Expr(ExprValue::Expression(expr)) => { + write!(f, "{}", remove_interned_in_expression_kind(self.interner, expr.clone())) + } + Value::Expr(ExprValue::Statement(statement)) => { + write!(f, "{}", remove_interned_in_statement_kind(self.interner, statement.clone())) + } + Value::Expr(ExprValue::LValue(lvalue)) => { + write!(f, "{}", remove_interned_in_lvalue(self.interner, lvalue.clone())) + } + Value::TypedExpr(_) => write!(f, "(typed expr)"), + Value::UnresolvedType(typ) => { + if let UnresolvedTypeData::Interned(id) = typ { + let typ = self.interner.get_unresolved_type_data(*id); + write!(f, "{}", typ) + } else { + write!(f, "{}", typ) + } + } } } } @@ -609,3 +649,227 @@ fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitCon let trait_ = interner.get_trait(trait_constraint.trait_id); format!("{}: {}{}", trait_constraint.typ, trait_.name, trait_constraint.trait_generics) } + +// Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. +fn remove_interned_in_expression(interner: &NodeInterner, expr: Expression) -> Expression { + Expression { kind: remove_interned_in_expression_kind(interner, expr.kind), span: expr.span } +} + +// Returns a new ExpressionKind where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. +fn remove_interned_in_expression_kind( + interner: &NodeInterner, + expr: ExpressionKind, +) -> ExpressionKind { + match expr { + ExpressionKind::Literal(literal) => { + ExpressionKind::Literal(remove_interned_in_literal(interner, literal)) + } + ExpressionKind::Block(block) => { + let statements = + vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); + ExpressionKind::Block(BlockExpression { statements }) + } + ExpressionKind::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { + rhs: remove_interned_in_expression(interner, prefix.rhs), + ..*prefix + })), + ExpressionKind::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { + collection: remove_interned_in_expression(interner, index.collection), + index: remove_interned_in_expression(interner, index.index), + })), + ExpressionKind::Call(call) => ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(remove_interned_in_expression(interner, *call.func)), + arguments: vecmap(call.arguments, |arg| remove_interned_in_expression(interner, arg)), + ..*call + })), + ExpressionKind::MethodCall(call) => { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: remove_interned_in_expression(interner, call.object), + arguments: vecmap(call.arguments, |arg| { + remove_interned_in_expression(interner, arg) + }), + ..*call + })) + } + ExpressionKind::Constructor(constructor) => { + ExpressionKind::Constructor(Box::new(ConstructorExpression { + fields: vecmap(constructor.fields, |(name, expr)| { + (name, remove_interned_in_expression(interner, expr)) + }), + ..*constructor + })) + } + ExpressionKind::MemberAccess(member_access) => { + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: remove_interned_in_expression(interner, member_access.lhs), + ..*member_access + })) + } + ExpressionKind::Cast(cast) => ExpressionKind::Cast(Box::new(CastExpression { + lhs: remove_interned_in_expression(interner, cast.lhs), + ..*cast + })), + ExpressionKind::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { + lhs: remove_interned_in_expression(interner, infix.lhs), + rhs: remove_interned_in_expression(interner, infix.rhs), + ..*infix + })), + ExpressionKind::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { + condition: remove_interned_in_expression(interner, if_expr.condition), + consequence: remove_interned_in_expression(interner, if_expr.consequence), + alternative: if_expr + .alternative + .map(|alternative| remove_interned_in_expression(interner, alternative)), + })), + ExpressionKind::Variable(_) => expr, + ExpressionKind::Tuple(expressions) => ExpressionKind::Tuple(vecmap(expressions, |expr| { + remove_interned_in_expression(interner, expr) + })), + ExpressionKind::Lambda(lambda) => ExpressionKind::Lambda(Box::new(Lambda { + body: remove_interned_in_expression(interner, lambda.body), + ..*lambda + })), + ExpressionKind::Parenthesized(expr) => { + ExpressionKind::Parenthesized(Box::new(remove_interned_in_expression(interner, *expr))) + } + ExpressionKind::Quote(_) => expr, + ExpressionKind::Unquote(expr) => { + ExpressionKind::Unquote(Box::new(remove_interned_in_expression(interner, *expr))) + } + ExpressionKind::Comptime(block, span) => { + let statements = + vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); + ExpressionKind::Comptime(BlockExpression { statements }, span) + } + ExpressionKind::Unsafe(block, span) => { + let statements = + vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); + ExpressionKind::Unsafe(BlockExpression { statements }, span) + } + ExpressionKind::AsTraitPath(_) => expr, + ExpressionKind::Resolved(id) => { + let expr = interner.expression(&id); + expr.to_display_ast(interner, Span::default()).kind + } + ExpressionKind::Interned(id) => { + let expr = interner.get_expression_kind(id).clone(); + remove_interned_in_expression_kind(interner, expr) + } + ExpressionKind::Error => expr, + } +} + +fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Literal { + match literal { + Literal::Array(array_literal) => { + Literal::Array(remove_interned_in_array_literal(interner, array_literal)) + } + Literal::Slice(array_literal) => { + Literal::Array(remove_interned_in_array_literal(interner, array_literal)) + } + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => literal, + } +} + +fn remove_interned_in_array_literal( + interner: &NodeInterner, + literal: ArrayLiteral, +) -> ArrayLiteral { + match literal { + ArrayLiteral::Standard(expressions) => { + ArrayLiteral::Standard(vecmap(expressions, |expr| { + remove_interned_in_expression(interner, expr) + })) + } + ArrayLiteral::Repeated { repeated_element, length } => ArrayLiteral::Repeated { + repeated_element: Box::new(remove_interned_in_expression(interner, *repeated_element)), + length: Box::new(remove_interned_in_expression(interner, *length)), + }, + } +} + +// Returns a new Statement where all Interned statements have been turned into non-interned StatementKind. +fn remove_interned_in_statement(interner: &NodeInterner, statement: Statement) -> Statement { + Statement { + kind: remove_interned_in_statement_kind(interner, statement.kind), + span: statement.span, + } +} + +// Returns a new StatementKind where all Interned statements have been turned into non-interned StatementKind. +fn remove_interned_in_statement_kind( + interner: &NodeInterner, + statement: StatementKind, +) -> StatementKind { + match statement { + StatementKind::Let(let_statement) => StatementKind::Let(LetStatement { + expression: remove_interned_in_expression(interner, let_statement.expression), + ..let_statement + }), + StatementKind::Constrain(constrain) => StatementKind::Constrain(ConstrainStatement( + remove_interned_in_expression(interner, constrain.0), + constrain.1.map(|expr| remove_interned_in_expression(interner, expr)), + constrain.2, + )), + StatementKind::Expression(expr) => { + StatementKind::Expression(remove_interned_in_expression(interner, expr)) + } + StatementKind::Assign(assign) => StatementKind::Assign(AssignStatement { + lvalue: assign.lvalue, + expression: remove_interned_in_expression(interner, assign.expression), + }), + StatementKind::For(for_loop) => StatementKind::For(ForLoopStatement { + range: match for_loop.range { + ForRange::Range(from, to) => ForRange::Range( + remove_interned_in_expression(interner, from), + remove_interned_in_expression(interner, to), + ), + ForRange::Array(expr) => { + ForRange::Array(remove_interned_in_expression(interner, expr)) + } + }, + block: remove_interned_in_expression(interner, for_loop.block), + ..for_loop + }), + StatementKind::Comptime(statement) => { + StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) + } + StatementKind::Semi(expr) => { + StatementKind::Semi(remove_interned_in_expression(interner, expr)) + } + StatementKind::Interned(id) => { + let statement = interner.get_statement_kind(id).clone(); + remove_interned_in_statement_kind(interner, statement) + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => statement, + } +} + +// Returns a new LValue where all Interned LValues have been turned into LValue. +fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue { + match lvalue { + LValue::Ident(_) => lvalue, + LValue::MemberAccess { object, field_name, span } => LValue::MemberAccess { + object: Box::new(remove_interned_in_lvalue(interner, *object)), + field_name, + span, + }, + LValue::Index { array, index, span } => LValue::Index { + array: Box::new(remove_interned_in_lvalue(interner, *array)), + index: remove_interned_in_expression(interner, index), + span, + }, + LValue::Dereference(lvalue, span) => { + LValue::Dereference(Box::new(remove_interned_in_lvalue(interner, *lvalue)), span) + } + LValue::Interned(id, span) => { + let lvalue = interner.get_lvalue(id, span); + remove_interned_in_lvalue(interner, lvalue) + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index a961de628a8..30c91b42b2e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -245,6 +245,7 @@ impl DefCollector { /// Collect all of the definitions in a given crate into a CrateDefMap /// Modules which are not a part of the module hierarchy starting with /// the root module, will be ignored. + #[allow(clippy::too_many_arguments)] pub fn collect_crate_and_dependencies( mut def_map: CrateDefMap, context: &mut Context, @@ -252,6 +253,7 @@ impl DefCollector { root_file_id: FileId, debug_comptime_in_file: Option<&str>, enable_arithmetic_generics: bool, + error_on_unused_imports: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; @@ -265,11 +267,13 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { + let error_on_unused_imports = false; errors.extend(CrateDefMap::collect_defs( dep.crate_id, context, debug_comptime_in_file, enable_arithmetic_generics, + error_on_unused_imports, macro_processors, )); @@ -413,8 +417,26 @@ impl DefCollector { ); } + if error_on_unused_imports { + Self::check_unused_imports(context, crate_id, &mut errors); + } + errors } + + fn check_unused_imports( + context: &Context, + crate_id: CrateId, + errors: &mut Vec<(CompilationError, FileId)>, + ) { + errors.extend(context.def_maps[&crate_id].modules().iter().flat_map(|(_, module)| { + module.unused_imports().iter().map(|ident| { + let ident = ident.clone(); + let error = CompilationError::ResolverError(ResolverError::UnusedImport { ident }); + (error, module.location.file) + }) + })); + } } fn add_import_reference( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs index e607de52ff1..758b4cf6e5c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -77,6 +77,7 @@ impl CrateDefMap { context: &mut Context, debug_comptime_in_file: Option<&str>, enable_arithmetic_generics: bool, + error_on_unused_imports: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled @@ -127,12 +128,14 @@ impl CrateDefMap { root_file_id, debug_comptime_in_file, enable_arithmetic_generics, + error_on_unused_imports, macro_processors, )); errors.extend( parsing_errors.iter().map(|e| (e.clone().into(), root_file_id)).collect::>(), ); + errors } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs index 8a0125cfe95..7b14db8be77 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use noirc_errors::Location; @@ -24,6 +24,10 @@ pub struct ModuleData { /// True if this module is a `contract Foo { ... }` module containing contract functions pub is_contract: bool, + + /// List of all unused imports. Each time something is imported into this module it's added + /// to this set. When it's used, it's removed. At the end of the program only unused imports remain. + unused_imports: HashSet, } impl ModuleData { @@ -35,6 +39,7 @@ impl ModuleData { definitions: ItemScope::default(), location, is_contract, + unused_imports: HashSet::new(), } } @@ -121,6 +126,11 @@ impl ModuleData { id: ModuleDefId, is_prelude: bool, ) -> Result<(), (Ident, Ident)> { + // Empty spans could come from implicitly injected imports, and we don't want to track those + if name.span().start() < name.span().end() { + self.unused_imports.insert(name.clone()); + } + self.scope.add_item_to_namespace(name, ItemVisibility::Public, id, None, is_prelude) } @@ -137,4 +147,14 @@ impl ModuleData { pub fn value_definitions(&self) -> impl Iterator + '_ { self.definitions.values().values().flat_map(|a| a.values().map(|(id, _, _)| *id)) } + + /// Marks an ident as being used by an import. + pub fn use_import(&mut self, ident: &Ident) { + self.unused_imports.remove(ident); + } + + /// Returns the list of all unused imports at this moment. + pub fn unused_imports(&self) -> &HashSet { + &self.unused_imports + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs index 0aad50d13b2..0b0d8d735eb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -20,6 +20,8 @@ pub enum ResolverError { DuplicateDefinition { name: String, first_span: Span, second_span: Span }, #[error("Unused variable")] UnusedVariable { ident: Ident }, + #[error("Unused import")] + UnusedImport { ident: Ident }, #[error("Could not find variable in this scope")] VariableNotDeclared { name: String, span: Span }, #[error("path is not an identifier")] @@ -120,6 +122,8 @@ pub enum ResolverError { NamedTypeArgs { span: Span, item_kind: &'static str }, #[error("Associated constants may only be a field or integer type")] AssociatedConstantsMustBeNumeric { span: Span }, + #[error("Overflow in `{lhs} {op} {rhs}`")] + OverflowInType { lhs: u32, op: crate::BinaryTypeOperator, rhs: u32, span: Span }, } impl ResolverError { @@ -152,6 +156,15 @@ impl<'a> From<&'a ResolverError> for Diagnostic { ident.span(), ) } + ResolverError::UnusedImport { ident } => { + let name = &ident.0.contents; + + Diagnostic::simple_warning( + format!("unused import {name}"), + "unused import ".to_string(), + ident.span(), + ) + } ResolverError::VariableNotDeclared { name, span } => Diagnostic::simple_error( format!("cannot find `{name}` in this scope "), "not found in this scope".to_string(), @@ -480,6 +493,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) } + ResolverError::OverflowInType { lhs, op, rhs, span } => { + Diagnostic::simple_error( + format!("Overflow in `{lhs} {op} {rhs}`"), + "Overflow here".to_string(), + *span, + ) + } } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs index 379c53944e5..697c78745f9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/generics.rs @@ -160,6 +160,7 @@ fn fmt_trait_generics( write!(f, "{} = {}", named.name, named.typ)?; } } + write!(f, ">")?; } Ok(()) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs index 29b0cb7b8af..7fa33746f31 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs @@ -164,6 +164,9 @@ pub struct FuncMeta { /// If this function is from an impl (trait or regular impl), this /// is the object type of the impl. Otherwise this is None. pub self_type: Option, + + /// Custom attributes attached to this function. + pub custom_attributes: Vec, } #[derive(Debug, Clone)] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 807666f9af9..638003d3fcd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -24,7 +24,9 @@ use super::{ traits::NamedType, }; -#[derive(PartialEq, Eq, Clone, Hash, Ord, PartialOrd)] +mod arithmetic; + +#[derive(Eq, Clone, Ord, PartialOrd)] pub enum Type { /// A primitive Field type FieldElement, @@ -151,6 +153,7 @@ pub enum QuotedType { Quoted, TopLevelItem, Type, + TypedExpr, StructDefinition, TraitConstraint, TraitDefinition, @@ -739,6 +742,7 @@ impl std::fmt::Display for QuotedType { QuotedType::Quoted => write!(f, "Quoted"), QuotedType::TopLevelItem => write!(f, "TopLevelItem"), QuotedType::Type => write!(f, "Type"), + QuotedType::TypedExpr => write!(f, "TypedExpr"), QuotedType::StructDefinition => write!(f, "StructDefinition"), QuotedType::TraitDefinition => write!(f, "TraitDefinition"), QuotedType::TraitConstraint => write!(f, "TraitConstraint"), @@ -1657,132 +1661,6 @@ impl Type { } } - /// Try to canonicalize the representation of this type. - /// Currently the only type with a canonical representation is - /// `Type::Infix` where for each consecutive commutative operator - /// we sort the non-constant operands by `Type: Ord` and place all constant - /// operands at the end, constant folded. - /// - /// For example: - /// - `canonicalize[((1 + N) + M) + 2] = (M + N) + 3` - /// - `canonicalize[A + 2 * B + 3 - 2] = A + (B * 2) + 3 - 2` - pub fn canonicalize(&self) -> Type { - match self.follow_bindings() { - Type::InfixExpr(lhs, op, rhs) => { - // evaluate_to_u32 also calls canonicalize so if we just called - // `self.evaluate_to_u32()` we'd get infinite recursion. - if let (Some(lhs), Some(rhs)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { - return Type::Constant(op.function(lhs, rhs)); - } - - let lhs = lhs.canonicalize(); - let rhs = rhs.canonicalize(); - if let Some(result) = Self::try_simplify_addition(&lhs, op, &rhs) { - return result; - } - - if let Some(result) = Self::try_simplify_subtraction(&lhs, op, &rhs) { - return result; - } - - if op.is_commutative() { - return Self::sort_commutative(&lhs, op, &rhs); - } - - Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)) - } - other => other, - } - } - - fn sort_commutative(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Type { - let mut queue = vec![lhs.clone(), rhs.clone()]; - - let mut sorted = BTreeSet::new(); - - let zero_value = if op == BinaryTypeOperator::Addition { 0 } else { 1 }; - let mut constant = zero_value; - - // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. - while let Some(item) = queue.pop() { - match item.canonicalize() { - Type::InfixExpr(lhs, new_op, rhs) if new_op == op => { - queue.push(*lhs); - queue.push(*rhs); - } - Type::Constant(new_constant) => { - constant = op.function(constant, new_constant); - } - other => { - sorted.insert(other); - } - } - } - - if let Some(first) = sorted.pop_first() { - let mut typ = first.clone(); - - for rhs in sorted { - typ = Type::InfixExpr(Box::new(typ), op, Box::new(rhs.clone())); - } - - if constant != zero_value { - typ = Type::InfixExpr(Box::new(typ), op, Box::new(Type::Constant(constant))); - } - - typ - } else { - // Every type must have been a constant - Type::Constant(constant) - } - } - - /// Try to simplify an addition expression of `lhs + rhs`. - /// - /// - Simplifies `(a - b) + b` to `a`. - fn try_simplify_addition(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Option { - use BinaryTypeOperator::*; - match lhs { - Type::InfixExpr(l_lhs, l_op, l_rhs) => { - if op == Addition && *l_op == Subtraction { - // TODO: Propagate type bindings. Can do in another PR, this one is large enough. - let unifies = l_rhs.try_unify(rhs, &mut TypeBindings::new()); - if unifies.is_ok() { - return Some(l_lhs.as_ref().clone()); - } - } - None - } - _ => None, - } - } - - /// Try to simplify a subtraction expression of `lhs - rhs`. - /// - /// - Simplifies `(a + C1) - C2` to `a + (C1 - C2)` if C1 and C2 are constants. - fn try_simplify_subtraction(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Option { - use BinaryTypeOperator::*; - match lhs { - Type::InfixExpr(l_lhs, l_op, l_rhs) => { - // Simplify `(N + 2) - 1` - if op == Subtraction && *l_op == Addition { - if let (Some(lhs_const), Some(rhs_const)) = - (l_rhs.evaluate_to_u32(), rhs.evaluate_to_u32()) - { - if lhs_const > rhs_const { - let constant = Box::new(Type::Constant(lhs_const - rhs_const)); - return Some( - Type::InfixExpr(l_lhs.clone(), *l_op, constant).canonicalize(), - ); - } - } - } - None - } - _ => None, - } - } - /// Try to unify a type variable to `self`. /// This is a helper function factored out from try_unify. fn try_unify_to_type_variable( @@ -1926,7 +1804,7 @@ impl Type { Type::InfixExpr(lhs, op, rhs) => { let lhs = lhs.evaluate_to_u32()?; let rhs = rhs.evaluate_to_u32()?; - Some(op.function(lhs, rhs)) + op.function(lhs, rhs) } _ => None, } @@ -2030,17 +1908,13 @@ impl Type { Type::Forall(typevars, typ) => { assert_eq!(types.len() + implicit_generic_count, typevars.len(), "Turbofish operator used with incorrect generic count which was not caught by name resolution"); + let bindings = + (0..implicit_generic_count).map(|_| interner.next_type_variable()).chain(types); + let replacements = typevars .iter() - .enumerate() - .map(|(i, var)| { - let binding = if i < implicit_generic_count { - interner.next_type_variable() - } else { - types[i - implicit_generic_count].clone() - }; - (var.id(), (var.clone(), binding)) - }) + .zip(bindings) + .map(|(var, binding)| (var.id(), (var.clone(), binding))) .collect(); let instantiated = typ.substitute(&replacements); @@ -2457,13 +2331,13 @@ fn convert_array_expression_to_slice( impl BinaryTypeOperator { /// Perform the actual rust numeric operation associated with this operator - pub fn function(self, a: u32, b: u32) -> u32 { + pub fn function(self, a: u32, b: u32) -> Option { match self { - BinaryTypeOperator::Addition => a.wrapping_add(b), - BinaryTypeOperator::Subtraction => a.wrapping_sub(b), - BinaryTypeOperator::Multiplication => a.wrapping_mul(b), - BinaryTypeOperator::Division => a.wrapping_div(b), - BinaryTypeOperator::Modulo => a.wrapping_rem(b), + BinaryTypeOperator::Addition => a.checked_add(b), + BinaryTypeOperator::Subtraction => a.checked_sub(b), + BinaryTypeOperator::Multiplication => a.checked_mul(b), + BinaryTypeOperator::Division => a.checked_div(b), + BinaryTypeOperator::Modulo => a.checked_rem(b), } } @@ -2681,3 +2555,136 @@ impl std::fmt::Debug for StructType { write!(f, "{}", self.name) } } + +impl std::hash::Hash for Type { + fn hash(&self, state: &mut H) { + if let Some(variable) = self.get_inner_type_variable() { + if let TypeBinding::Bound(typ) = &*variable.borrow() { + typ.hash(state); + return; + } + } + + if !matches!(self, Type::TypeVariable(..) | Type::NamedGeneric(..)) { + std::mem::discriminant(self).hash(state); + } + + match self { + Type::FieldElement | Type::Bool | Type::Unit | Type::Error => (), + Type::Array(len, elem) => { + len.hash(state); + elem.hash(state); + } + Type::Slice(elem) => elem.hash(state), + Type::Integer(sign, bits) => { + sign.hash(state); + bits.hash(state); + } + Type::String(len) => len.hash(state), + Type::FmtString(len, env) => { + len.hash(state); + env.hash(state); + } + Type::Tuple(elems) => elems.hash(state), + Type::Struct(def, args) => { + def.hash(state); + args.hash(state); + } + Type::Alias(alias, args) => { + alias.hash(state); + args.hash(state); + } + Type::TypeVariable(var, _) | Type::NamedGeneric(var, ..) => var.hash(state), + Type::TraitAsType(trait_id, _, args) => { + trait_id.hash(state); + args.hash(state); + } + Type::Function(args, ret, env, is_unconstrained) => { + args.hash(state); + ret.hash(state); + env.hash(state); + is_unconstrained.hash(state); + } + Type::MutableReference(elem) => elem.hash(state), + Type::Forall(vars, typ) => { + vars.hash(state); + typ.hash(state); + } + Type::Constant(value) => value.hash(state), + Type::Quoted(typ) => typ.hash(state), + Type::InfixExpr(lhs, op, rhs) => { + lhs.hash(state); + op.hash(state); + rhs.hash(state); + } + } + } +} + +impl PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + if let Some(variable) = self.get_inner_type_variable() { + if let TypeBinding::Bound(typ) = &*variable.borrow() { + return typ == other; + } + } + + if let Some(variable) = other.get_inner_type_variable() { + if let TypeBinding::Bound(typ) = &*variable.borrow() { + return self == typ; + } + } + + use Type::*; + match (self, other) { + (FieldElement, FieldElement) | (Bool, Bool) | (Unit, Unit) | (Error, Error) => true, + (Array(lhs_len, lhs_elem), Array(rhs_len, rhs_elem)) => { + lhs_len == rhs_len && lhs_elem == rhs_elem + } + (Slice(lhs_elem), Slice(rhs_elem)) => lhs_elem == rhs_elem, + (Integer(lhs_sign, lhs_bits), Integer(rhs_sign, rhs_bits)) => { + lhs_sign == rhs_sign && lhs_bits == rhs_bits + } + (String(lhs_len), String(rhs_len)) => lhs_len == rhs_len, + (FmtString(lhs_len, lhs_env), FmtString(rhs_len, rhs_env)) => { + lhs_len == rhs_len && lhs_env == rhs_env + } + (Tuple(lhs_types), Tuple(rhs_types)) => lhs_types == rhs_types, + (Struct(lhs_struct, lhs_generics), Struct(rhs_struct, rhs_generics)) => { + lhs_struct == rhs_struct && lhs_generics == rhs_generics + } + (Alias(lhs_alias, lhs_generics), Alias(rhs_alias, rhs_generics)) => { + lhs_alias == rhs_alias && lhs_generics == rhs_generics + } + (TraitAsType(lhs_trait, _, lhs_generics), TraitAsType(rhs_trait, _, rhs_generics)) => { + lhs_trait == rhs_trait && lhs_generics == rhs_generics + } + ( + Function(lhs_args, lhs_ret, lhs_env, lhs_unconstrained), + Function(rhs_args, rhs_ret, rhs_env, rhs_unconstrained), + ) => { + let args_and_ret_eq = lhs_args == rhs_args && lhs_ret == rhs_ret; + args_and_ret_eq && lhs_env == rhs_env && lhs_unconstrained == rhs_unconstrained + } + (MutableReference(lhs_elem), MutableReference(rhs_elem)) => lhs_elem == rhs_elem, + (Forall(lhs_vars, lhs_type), Forall(rhs_vars, rhs_type)) => { + lhs_vars == rhs_vars && lhs_type == rhs_type + } + (Constant(lhs), Constant(rhs)) => lhs == rhs, + (Quoted(lhs), Quoted(rhs)) => lhs == rhs, + (InfixExpr(l_lhs, l_op, l_rhs), InfixExpr(r_lhs, r_op, r_rhs)) => { + l_lhs == r_lhs && l_op == r_op && l_rhs == r_rhs + } + // Special case: we consider unbound named generics and type variables to be equal to each + // other if their type variable ids match. This is important for some corner cases in + // monomorphization where we call `replace_named_generics_with_type_variables` but + // still want them to be equal for canonicalization checks in arithmetic generics. + // Without this we'd fail the `serialize` test. + ( + NamedGeneric(lhs_var, _, _) | TypeVariable(lhs_var, _), + NamedGeneric(rhs_var, _, _) | TypeVariable(rhs_var, _), + ) => lhs_var.id() == rhs_var.id(), + _ => false, + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs new file mode 100644 index 00000000000..ad07185dff1 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types/arithmetic.rs @@ -0,0 +1,215 @@ +use std::collections::BTreeSet; + +use crate::{BinaryTypeOperator, Type}; + +impl Type { + /// Try to canonicalize the representation of this type. + /// Currently the only type with a canonical representation is + /// `Type::Infix` where for each consecutive commutative operator + /// we sort the non-constant operands by `Type: Ord` and place all constant + /// operands at the end, constant folded. + /// + /// For example: + /// - `canonicalize[((1 + N) + M) + 2] = (M + N) + 3` + /// - `canonicalize[A + 2 * B + 3 - 2] = A + (B * 2) + 3 - 2` + pub fn canonicalize(&self) -> Type { + match self.follow_bindings() { + Type::InfixExpr(lhs, op, rhs) => { + // evaluate_to_u32 also calls canonicalize so if we just called + // `self.evaluate_to_u32()` we'd get infinite recursion. + if let (Some(lhs), Some(rhs)) = (lhs.evaluate_to_u32(), rhs.evaluate_to_u32()) { + if let Some(result) = op.function(lhs, rhs) { + return Type::Constant(result); + } + } + + let lhs = lhs.canonicalize(); + let rhs = rhs.canonicalize(); + if let Some(result) = Self::try_simplify_non_constants_in_lhs(&lhs, op, &rhs) { + return result.canonicalize(); + } + + if let Some(result) = Self::try_simplify_non_constants_in_rhs(&lhs, op, &rhs) { + return result.canonicalize(); + } + + // Try to simplify partially constant expressions in the form `(N op1 C1) op2 C2` + // where C1 and C2 are constants that can be combined (e.g. N + 5 - 3 = N + 2) + if let Some(result) = Self::try_simplify_partial_constants(&lhs, op, &rhs) { + return result.canonicalize(); + } + + if op.is_commutative() { + return Self::sort_commutative(&lhs, op, &rhs); + } + + Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)) + } + other => other, + } + } + + fn sort_commutative(lhs: &Type, op: BinaryTypeOperator, rhs: &Type) -> Type { + let mut queue = vec![lhs.clone(), rhs.clone()]; + + let mut sorted = BTreeSet::new(); + + let zero_value = if op == BinaryTypeOperator::Addition { 0 } else { 1 }; + let mut constant = zero_value; + + // Push each non-constant term to `sorted` to sort them. Recur on InfixExprs with the same operator. + while let Some(item) = queue.pop() { + match item.canonicalize() { + Type::InfixExpr(lhs, new_op, rhs) if new_op == op => { + queue.push(*lhs); + queue.push(*rhs); + } + Type::Constant(new_constant) => { + if let Some(result) = op.function(constant, new_constant) { + constant = result; + } else { + sorted.insert(Type::Constant(new_constant)); + } + } + other => { + sorted.insert(other); + } + } + } + + if let Some(first) = sorted.pop_first() { + let mut typ = first.clone(); + + for rhs in sorted { + typ = Type::InfixExpr(Box::new(typ), op, Box::new(rhs.clone())); + } + + if constant != zero_value { + typ = Type::InfixExpr(Box::new(typ), op, Box::new(Type::Constant(constant))); + } + + typ + } else { + // Every type must have been a constant + Type::Constant(constant) + } + } + + /// Try to simplify non-constant expressions in the form `(N op1 M) op2 M` + /// where the two `M` terms are expected to cancel out. + /// Precondition: `lhs & rhs are in canonical form` + /// + /// - Simplifies `(N +/- M) -/+ M` to `N` + /// - Simplifies `(N */÷ M) ÷/* M` to `N` + fn try_simplify_non_constants_in_lhs( + lhs: &Type, + op: BinaryTypeOperator, + rhs: &Type, + ) -> Option { + let Type::InfixExpr(l_lhs, l_op, l_rhs) = lhs.follow_bindings() else { + return None; + }; + + // Note that this is exact, syntactic equality, not unification. + // `rhs` is expected to already be in canonical form. + if l_op.inverse() != Some(op) || l_rhs.canonicalize() != *rhs { + return None; + } + + Some(*l_lhs) + } + + /// Try to simplify non-constant expressions in the form `N op1 (M op1 N)` + /// where the two `M` terms are expected to cancel out. + /// Precondition: `lhs & rhs are in canonical form` + /// + /// Unlike `try_simplify_non_constants_in_lhs` we can't simplify `N / (M * N)` + /// Since that should simplify to `1 / M` instead of `M`. + /// + /// - Simplifies `N +/- (M -/+ N)` to `M` + /// - Simplifies `N * (M ÷ N)` to `M` + fn try_simplify_non_constants_in_rhs( + lhs: &Type, + op: BinaryTypeOperator, + rhs: &Type, + ) -> Option { + let Type::InfixExpr(r_lhs, r_op, r_rhs) = rhs.follow_bindings() else { + return None; + }; + + // `N / (M * N)` should be simplified to `1 / M`, but we only handle + // simplifying to `M` in this function. + if op == BinaryTypeOperator::Division && r_op == BinaryTypeOperator::Multiplication { + return None; + } + + // Note that this is exact, syntactic equality, not unification. + // `lhs` is expected to already be in canonical form. + if r_op.inverse() != Some(op) || *lhs != r_rhs.canonicalize() { + return None; + } + + Some(*r_lhs) + } + + /// Given: + /// lhs = `N op C1` + /// rhs = C2 + /// Returns: `(N, op, C1, C2)` if C1 and C2 are constants. + /// Note that the operator here is within the `lhs` term, the operator + /// separating lhs and rhs is not needed. + /// Precondition: `lhs & rhs are in canonical form` + fn parse_partial_constant_expr( + lhs: &Type, + rhs: &Type, + ) -> Option<(Box, BinaryTypeOperator, u32, u32)> { + let rhs = rhs.evaluate_to_u32()?; + + let Type::InfixExpr(l_type, l_op, l_rhs) = lhs.follow_bindings() else { + return None; + }; + + let l_rhs = l_rhs.evaluate_to_u32()?; + Some((l_type, l_op, l_rhs, rhs)) + } + + /// Try to simplify partially constant expressions in the form `(N op1 C1) op2 C2` + /// where C1 and C2 are constants that can be combined (e.g. N + 5 - 3 = N + 2) + /// Precondition: `lhs & rhs are in canonical form` + /// + /// - Simplifies `(N +/- C1) +/- C2` to `N +/- (C1 +/- C2)` if C1 and C2 are constants. + /// - Simplifies `(N */÷ C1) */÷ C2` to `N */÷ (C1 */÷ C2)` if C1 and C2 are constants. + fn try_simplify_partial_constants( + lhs: &Type, + mut op: BinaryTypeOperator, + rhs: &Type, + ) -> Option { + use BinaryTypeOperator::*; + let (l_type, l_op, l_const, r_const) = Type::parse_partial_constant_expr(lhs, rhs)?; + + match (l_op, op) { + (Addition | Subtraction, Addition | Subtraction) => { + // If l_op is a subtraction we want to inverse the rhs operator. + if l_op == Subtraction { + op = op.inverse()?; + } + let result = op.function(l_const, r_const)?; + Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result)))) + } + (Multiplication | Division, Multiplication | Division) => { + // If l_op is a division we want to inverse the rhs operator. + if l_op == Division { + op = op.inverse()?; + } + // If op is a division we need to ensure it divides evenly + if op == Division && (r_const == 0 || l_const % r_const != 0) { + None + } else { + let result = op.function(l_const, r_const)?; + Some(Type::InfixExpr(l_type, l_op, Box::new(Type::Constant(result)))) + } + } + _ => None, + } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index 8ee0fca2957..585e22ce92b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -4,7 +4,10 @@ use std::{fmt, iter::Map, vec::IntoIter}; use crate::{ lexer::errors::LexerErrorKind, - node_interner::{ExprId, QuotedTypeId}, + node_interner::{ + ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, + QuotedTypeId, + }, }; /// Represents a token in noir's grammar - a word, number, @@ -28,6 +31,10 @@ pub enum BorrowedToken<'input> { BlockComment(&'input str, Option), Quote(&'input Tokens), QuotedType(QuotedTypeId), + InternedExpression(InternedExpressionKind), + InternedStatement(InternedStatementKind), + InternedLValue(InternedExpressionKind), + InternedUnresolvedTypeData(InternedUnresolvedTypeData), /// < Less, /// <= @@ -134,6 +141,14 @@ pub enum Token { /// to avoid having to tokenize it, re-parse it, and re-resolve it which /// may change the underlying type. QuotedType(QuotedTypeId), + /// A reference to an interned `ExpressionKind`. + InternedExpr(InternedExpressionKind), + /// A reference to an interned `StatementKind`. + InternedStatement(InternedStatementKind), + /// A reference to an interned `LValue`. + InternedLValue(InternedExpressionKind), + /// A reference to an interned `UnresolvedTypeData`. + InternedUnresolvedTypeData(InternedUnresolvedTypeData), /// < Less, /// <= @@ -233,6 +248,10 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::BlockComment(ref s, _style) => BorrowedToken::BlockComment(s, *_style), Token::Quote(stream) => BorrowedToken::Quote(stream), Token::QuotedType(id) => BorrowedToken::QuotedType(*id), + Token::InternedExpr(id) => BorrowedToken::InternedExpression(*id), + Token::InternedStatement(id) => BorrowedToken::InternedStatement(*id), + Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), + Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, @@ -353,8 +372,12 @@ impl fmt::Display for Token { } write!(f, "}}") } - // Quoted types only have an ID so there is nothing to display + // Quoted types and exprs only have an ID so there is nothing to display Token::QuotedType(_) => write!(f, "(type)"), + Token::InternedExpr(_) | Token::InternedStatement(_) | Token::InternedLValue(_) => { + write!(f, "(expr)") + } + Token::InternedUnresolvedTypeData(_) => write!(f, "(type)"), Token::IntType(ref i) => write!(f, "{i}"), Token::Less => write!(f, "<"), Token::LessEqual => write!(f, "<="), @@ -407,6 +430,10 @@ pub enum TokenKind { Attribute, Quote, QuotedType, + InternedExpr, + InternedStatement, + InternedLValue, + InternedUnresolvedTypeData, UnquoteMarker, } @@ -420,6 +447,10 @@ impl fmt::Display for TokenKind { TokenKind::Attribute => write!(f, "attribute"), TokenKind::Quote => write!(f, "quote"), TokenKind::QuotedType => write!(f, "quoted type"), + TokenKind::InternedExpr => write!(f, "interned expr"), + TokenKind::InternedStatement => write!(f, "interned statement"), + TokenKind::InternedLValue => write!(f, "interned lvalue"), + TokenKind::InternedUnresolvedTypeData => write!(f, "interned unresolved type"), TokenKind::UnquoteMarker => write!(f, "macro result"), } } @@ -439,6 +470,10 @@ impl Token { Token::UnquoteMarker(_) => TokenKind::UnquoteMarker, Token::Quote(_) => TokenKind::Quote, Token::QuotedType(_) => TokenKind::QuotedType, + Token::InternedExpr(_) => TokenKind::InternedExpr, + Token::InternedStatement(_) => TokenKind::InternedStatement, + Token::InternedLValue(_) => TokenKind::InternedLValue, + Token::InternedUnresolvedTypeData(_) => TokenKind::InternedUnresolvedTypeData, tok => TokenKind::Token(tok.clone()), } } @@ -835,6 +870,16 @@ pub enum SecondaryAttribute { Varargs, } +impl SecondaryAttribute { + pub(crate) fn as_custom(&self) -> Option<&str> { + if let Self::Custom(str) = self { + Some(str) + } else { + None + } + } +} + impl fmt::Display for SecondaryAttribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -927,6 +972,7 @@ pub enum Keyword { TraitDefinition, TraitImpl, Type, + TypedExpr, TypeType, Unchecked, Unconstrained, @@ -982,6 +1028,7 @@ impl fmt::Display for Keyword { Keyword::TraitDefinition => write!(f, "TraitDefinition"), Keyword::TraitImpl => write!(f, "TraitImpl"), Keyword::Type => write!(f, "type"), + Keyword::TypedExpr => write!(f, "TypedExpr"), Keyword::TypeType => write!(f, "Type"), Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), @@ -1040,6 +1087,7 @@ impl Keyword { "TraitImpl" => Keyword::TraitImpl, "type" => Keyword::Type, "Type" => Keyword::TypeType, + "TypedExpr" => Keyword::TypedExpr, "StructDefinition" => Keyword::StructDefinition, "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs index 665bf26f7b9..ce8ef3572e6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -34,7 +34,7 @@ impl MonomorphizationError { fn into_diagnostic(self) -> CustomDiagnostic { let message = match &self { MonomorphizationError::UnknownArrayLength { length, .. } => { - format!("ICE: Could not determine array length `{length}`") + format!("Could not determine array length `{length}`") } MonomorphizationError::NoDefaultType { location } => { let message = "Type annotation needed".into(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index edb831b2158..87b55540bbd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -301,6 +301,7 @@ impl<'interner> Monomorphizer<'interner> { } let meta = self.interner.function_meta(&f).clone(); + let mut func_sig = meta.function_signature(); // Follow the bindings of the function signature for entry points // which are not `main` such as foldable functions. @@ -845,6 +846,14 @@ impl<'interner> Monomorphizer<'interner> { return self.resolve_trait_method_expr(expr_id, typ, method); } + // Ensure all instantiation bindings are bound. + // This ensures even unused type variables like `fn foo() {}` have concrete types + if let Some(bindings) = self.interner.try_get_instantiation_bindings(expr_id) { + for (_, binding) in bindings.values() { + Self::check_type(binding, ident.location)?; + } + } + let definition = self.interner.definition(ident.id); let ident = match &definition.kind { DefinitionKind::Function(func_id) => { @@ -1950,6 +1959,7 @@ pub fn resolve_trait_method( TraitImplKind::Normal(impl_id) => impl_id, TraitImplKind::Assumed { object_type, trait_generics } => { let location = interner.expr_location(&expr_id); + match interner.lookup_trait_implementation( &object_type, method.trait_id, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index 2c0426f6938..32f25790e12 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -13,7 +13,11 @@ use petgraph::prelude::DiGraph; use petgraph::prelude::NodeIndex as PetGraphIndex; use rustc_hash::FxHashMap as HashMap; +use crate::ast::ExpressionKind; use crate::ast::Ident; +use crate::ast::LValue; +use crate::ast::StatementKind; +use crate::ast::UnresolvedTypeData; use crate::graph::CrateId; use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; @@ -208,6 +212,15 @@ pub struct NodeInterner { /// the actual type since types do not implement Send or Sync. quoted_types: noirc_arena::Arena, + // Interned `ExpressionKind`s during comptime code. + interned_expression_kinds: noirc_arena::Arena, + + // Interned `StatementKind`s during comptime code. + interned_statement_kinds: noirc_arena::Arena, + + // Interned `UnresolvedTypeData`s during comptime code. + interned_unresolved_type_datas: noirc_arena::Arena, + /// Determins whether to run in LSP mode. In LSP mode references are tracked. pub(crate) lsp_mode: bool, @@ -580,6 +593,15 @@ pub struct GlobalInfo { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct QuotedTypeId(noirc_arena::Index); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InternedExpressionKind(noirc_arena::Index); + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InternedStatementKind(noirc_arena::Index); + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InternedUnresolvedTypeData(noirc_arena::Index); + impl Default for NodeInterner { fn default() -> Self { NodeInterner { @@ -617,6 +639,9 @@ impl Default for NodeInterner { type_alias_ref: Vec::new(), type_ref_locations: Vec::new(), quoted_types: Default::default(), + interned_expression_kinds: Default::default(), + interned_statement_kinds: Default::default(), + interned_unresolved_type_datas: Default::default(), lsp_mode: false, location_indices: LocationIndices::default(), reference_graph: petgraph::graph::DiGraph::new(), @@ -1289,6 +1314,10 @@ impl NodeInterner { &self.instantiation_bindings[&expr_id] } + pub fn try_get_instantiation_bindings(&self, expr_id: ExprId) -> Option<&TypeBindings> { + self.instantiation_bindings.get(&expr_id) + } + pub fn get_field_index(&self, expr_id: ExprId) -> usize { self.field_indices[&expr_id] } @@ -2038,6 +2067,41 @@ impl NodeInterner { &self.quoted_types[id.0] } + pub fn push_expression_kind(&mut self, expr: ExpressionKind) -> InternedExpressionKind { + InternedExpressionKind(self.interned_expression_kinds.insert(expr)) + } + + pub fn get_expression_kind(&self, id: InternedExpressionKind) -> &ExpressionKind { + &self.interned_expression_kinds[id.0] + } + + pub fn push_statement_kind(&mut self, statement: StatementKind) -> InternedStatementKind { + InternedStatementKind(self.interned_statement_kinds.insert(statement)) + } + + pub fn get_statement_kind(&self, id: InternedStatementKind) -> &StatementKind { + &self.interned_statement_kinds[id.0] + } + + pub fn push_lvalue(&mut self, lvalue: LValue) -> InternedExpressionKind { + self.push_expression_kind(lvalue.as_expression().kind) + } + + pub fn get_lvalue(&self, id: InternedExpressionKind, span: Span) -> LValue { + LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) + } + + pub fn push_unresolved_type_data( + &mut self, + typ: UnresolvedTypeData, + ) -> InternedUnresolvedTypeData { + InternedUnresolvedTypeData(self.interned_unresolved_type_datas.insert(typ)) + } + + pub fn get_unresolved_type_data(&self, id: InternedUnresolvedTypeData) -> &UnresolvedTypeData { + &self.interned_unresolved_type_datas[id.0] + } + /// Returns the type of an operator (which is always a function), along with its return type. pub fn get_infix_operator_type( &self, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs index f1972bcb9b5..11944cd3304 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -25,7 +25,7 @@ use noirc_errors::Span; pub use parser::path::path_no_turbofish; pub use parser::traits::trait_bound; pub use parser::{ - block, expression, fresh_statement, parse_program, parse_type, pattern, top_level_items, + block, expression, fresh_statement, lvalue, parse_program, parse_type, pattern, top_level_items, }; #[derive(Debug, Clone)] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs index 56c80ee1ce0..8a894ec2b83 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -73,7 +73,9 @@ mod test_helpers; use literals::literal; use path::{maybe_empty_path, path}; -use primitives::{dereference, ident, negation, not, nothing, right_shift_operator, token_kind}; +use primitives::{ + dereference, ident, interned_expr, negation, not, nothing, right_shift_operator, token_kind, +}; use traits::where_clause; /// Entry function for the parser - also handles lexing internally. @@ -487,6 +489,7 @@ where continue_statement(), return_statement(expr_parser.clone()), comptime_statement(expr_parser.clone(), expr_no_constructors, statement), + interned_statement(), expr_parser.map(StatementKind::Expression), )) }) @@ -526,6 +529,15 @@ where keyword(Keyword::Comptime).ignore_then(comptime_statement).map(StatementKind::Comptime) } +pub(super) fn interned_statement() -> impl NoirParser { + token_kind(TokenKind::InternedStatement).map(|token| match token { + Token::InternedStatement(id) => StatementKind::Interned(id), + _ => { + unreachable!("token_kind(InternedStatement) guarantees we parse an interned statement") + } + }) +} + /// Comptime in an expression position only accepts entire blocks fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a where @@ -642,7 +654,7 @@ enum LValueRhs { Index(Expression, Span), } -fn lvalue<'a, P>(expr_parser: P) -> impl NoirParser + 'a +pub fn lvalue<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, { @@ -655,7 +667,15 @@ where let parenthesized = lvalue.delimited_by(just(Token::LeftParen), just(Token::RightParen)); - let term = choice((parenthesized, dereferences, l_ident)); + let interned = + token_kind(TokenKind::InternedLValue).map_with_span(|token, span| match token { + Token::InternedLValue(id) => LValue::Interned(id, span), + _ => unreachable!( + "token_kind(InternedLValue) guarantees we parse an interned lvalue" + ), + }); + + let term = choice((parenthesized, dereferences, l_ident, interned)); let l_member_rhs = just(Token::Dot).ignore_then(field_name()).map_with_span(LValueRhs::MemberAccess); @@ -1154,6 +1174,7 @@ where literal(), as_trait_path(parse_type()).map(ExpressionKind::AsTraitPath), macro_quote_marker(), + interned_expr(), )) .map_with_span(Expression::new) .or(parenthesized(expr_parser.clone()).map_with_span(|sub_expr, span| { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs index 9145fb945c9..c1516e2c927 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -119,6 +119,13 @@ pub(super) fn macro_quote_marker() -> impl NoirParser { }) } +pub(super) fn interned_expr() -> impl NoirParser { + token_kind(TokenKind::InternedExpr).map(|token| match token { + Token::InternedExpr(id) => ExpressionKind::Interned(id), + _ => unreachable!("token_kind(InternedExpr) guarantees we parse an interned expr"), + }) +} + #[cfg(test)] mod test { use crate::parser::parser::{ diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs index c655ab8c5a4..9dabb8b83b6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -40,6 +40,7 @@ pub(super) fn parse_type_inner<'a>( function_type(recursive_type_parser.clone()), mutable_reference_type(recursive_type_parser.clone()), as_trait_path_type(recursive_type_parser), + interned_unresolved_type(), )) } @@ -87,6 +88,7 @@ pub(super) fn comptime_type() -> impl NoirParser { type_of_quoted_types(), top_level_item_type(), quoted_type(), + typed_expr_type(), )) } @@ -158,6 +160,12 @@ fn quoted_type() -> impl NoirParser { .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Quoted).with_span(span)) } +/// This is the type of a typed/resolved expression. +fn typed_expr_type() -> impl NoirParser { + keyword(Keyword::TypedExpr) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::TypedExpr).with_span(span)) +} + /// This is the type of an already resolved type. /// The only way this can appear in the token input is if an already resolved `Type` object /// was spliced into a macro's token stream via the `$` operator. @@ -168,6 +176,15 @@ pub(super) fn resolved_type() -> impl NoirParser { }) } +pub(super) fn interned_unresolved_type() -> impl NoirParser { + token_kind(TokenKind::InternedUnresolvedTypeData).map_with_span(|token, span| match token { + Token::InternedUnresolvedTypeData(id) => UnresolvedTypeData::Interned(id).with_span(span), + _ => unreachable!( + "token_kind(InternedUnresolvedTypeData) guarantees we parse an interned unresolved type" + ), + }) +} + pub(super) fn string_type() -> impl NoirParser { keyword(Keyword::String) .ignore_then(type_expression().delimited_by(just(Token::Less), just(Token::Greater))) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index cc4aae7f447..870c781b89d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -76,15 +76,21 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation extern_prelude: BTreeMap::new(), }; + let debug_comptime_in_file = None; + let enable_arithmetic_generics = false; + let error_on_unused_imports = true; + let macro_processors = &[]; + // Now we want to populate the CrateDefMap using the DefCollector errors.extend(DefCollector::collect_crate_and_dependencies( def_map, &mut context, program.clone().into_sorted(), root_file_id, - None, // No debug_comptime_in_file - false, // Disallow arithmetic generics - &[], // No macro processors + debug_comptime_in_file, + enable_arithmetic_generics, + error_on_unused_imports, + macro_processors, )); } (program, context, errors) @@ -2424,6 +2430,10 @@ fn use_super() { mod foo { use super::some_func; + + fn bar() { + some_func(); + } } "#; assert_no_errors(src); @@ -3187,3 +3197,37 @@ fn as_trait_path_syntax_no_impl() { use CompilationError::TypeError; assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); } + +#[test] +fn errors_on_unused_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + trait Foo { + } + } + + use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedImport { ident }) = &errors[0].0 + else { + panic!("Expected an unused import error"); + }; + + assert_eq!(ident.to_string(), "bar"); +} diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index c34d14b6153..293523d7c15 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -77,6 +77,7 @@ "devcontainer", "direnv", "eddsa", + "elab", "Elligator", "endianness", "envrc", diff --git a/noir/noir-repo/docs/docs/noir/concepts/comptime.md b/noir/noir-repo/docs/docs/noir/concepts/comptime.md index 2b5c29538b9..ed55a541fbd 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/comptime.md +++ b/noir/noir-repo/docs/docs/noir/concepts/comptime.md @@ -232,6 +232,7 @@ The following is an incomplete list of some `comptime` types along with some use - `fn fields(self) -> [(Quoted, Type)]` - Return the name and type of each field - `TraitConstraint`: A trait constraint such as `From` +- `TypedExpr`: A type-checked expression. - `UnresolvedType`: A syntactic notation that refers to a Noir type that hasn't been resolved yet There are many more functions available by exploring the `std::meta` module and its submodules. diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx b/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx index 95da2030843..a0c87c29259 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx @@ -20,7 +20,7 @@ fn main() -> pub u32 { } ``` -To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or +To write a slice literal, use a preceding ampersand as in: `&[0; 2]` or `&[1, 2, 3]`. It is important to note that slices are not references to arrays. In Noir, diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/expr.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/expr.md index 0a32b2b04fc..57f0fce24c1 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/expr.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/expr.md @@ -12,6 +12,12 @@ title: Expr If this expression is an array, this returns a slice of each element in the array. +### as_assert + +#include_code as_assert noir_stdlib/src/meta/expr.nr rust + +If this expression is an assert, this returns the assert expression and the optional message. + ### as_assign #include_code as_assign noir_stdlib/src/meta/expr.nr rust @@ -161,3 +167,35 @@ comptime { #include_code is_continue noir_stdlib/src/meta/expr.nr rust `true` if this expression is `continue`. + +### modify + +#include_code modify noir_stdlib/src/meta/expr.nr rust + +Applies a mapping function to this expression and to all of its sub-expressions. +`f` will be applied to each sub-expression first, then applied to the expression itself. + +This happens recursively for every expression within `self`. + +For example, calling `modify` on `(&[1], &[2, 3])` with an `f` that returns `Option::some` +for expressions that are integers, doubling them, would return `(&[2], &[4, 6])`. + +### quoted + +#include_code quoted noir_stdlib/src/meta/expr.nr rust + +Returns this expression as a `Quoted` value. It's the same as `quote { $self }`. + +### resolve + +#include_code resolve noir_stdlib/src/meta/expr.nr rust + +Resolves and type-checks this expression and returns the result as a `TypedExpr`. + +The `in_function` argument specifies where the expression is resolved: +- If it's `none`, the expression is resolved in the function where `resolve` was called +- If it's `some`, the expression is resolved in the given function + +If any names used by this expression are not in scope or if there are any type errors, +this will give compiler errors as if the expression was written directly into +the current `comptime` function. \ No newline at end of file diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/function_def.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/function_def.md index 4b359a9d343..5657e05fff7 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/function_def.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/function_def.md @@ -7,6 +7,20 @@ a function definition in the source program. ## Methods +### body + +#include_code body noir_stdlib/src/meta/function_def.nr rust + +Returns the body of the function as an expression. This is only valid +on functions in the current crate which have not yet been resolved. +This means any functions called at compile-time are invalid targets for this method. + +### has_named_attribute + +#include_code has_named_attribute noir_stdlib/src/meta/function_def.nr rust + +Returns true if this function has a custom attribute with the given name. + ### name #include_code name noir_stdlib/src/meta/function_def.nr rust @@ -33,8 +47,6 @@ Mutate the function body to a new expression. This is only valid on functions in the current crate which have not yet been resolved. This means any functions called at compile-time are invalid targets for this method. -Requires the new body to be a valid expression. - ### set_parameters #include_code set_parameters noir_stdlib/src/meta/function_def.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/op.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/op.md index 37d4cb746ac..d8b154edc02 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/op.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/op.md @@ -37,6 +37,12 @@ Returns `true` if this operator is `-`. `true` if this operator is `*` +#### quoted + +#include_code unary_quoted noir_stdlib/src/meta/op.nr rust + +Returns this operator as a `Quoted` value. + ### BinaryOp Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&`, `|`, `^`, `>>`, or `<<`. @@ -132,3 +138,9 @@ Represents a binary operator. One of `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, ` #include_code is_shift_right noir_stdlib/src/meta/op.nr rust `true` if this operator is `<<` + +#### quoted + +#include_code binary_quoted noir_stdlib/src/meta/op.nr rust + +Returns this operator as a `Quoted` value. \ No newline at end of file diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md index bad6435e94a..0b6f8d5f77d 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md @@ -45,6 +45,12 @@ if the type is signed, as well as the number of bits of this integer type. If this is a slice type, return the element type of the slice. +### as_str + +#include_code as_str noir_stdlib/src/meta/typ.nr rust + +If this is a `str` type, returns the length `N` as a type. + ### as_struct #include_code as_struct noir_stdlib/src/meta/typ.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/typed_expr.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/typed_expr.md new file mode 100644 index 00000000000..eacfd9c1230 --- /dev/null +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/typed_expr.md @@ -0,0 +1,13 @@ +--- +title: TypedExpr +--- + +`std::meta::typed_expr` contains methods on the built-in `TypedExpr` type for resolved and type-checked expressions. + +## Methods + +### as_function_definition + +#include_code as_function_definition noir_stdlib/src/meta/typed_expr.nr rust + +If this expression refers to a function definitions, returns it. Otherwise returns `Option::none()`. \ No newline at end of file diff --git a/noir/noir-repo/noir_stdlib/src/array.nr b/noir/noir-repo/noir_stdlib/src/array.nr index cef79e7c7f6..23683a54e45 100644 --- a/noir/noir-repo/noir_stdlib/src/array.nr +++ b/noir/noir-repo/noir_stdlib/src/array.nr @@ -3,6 +3,7 @@ use crate::option::Option; use crate::convert::From; impl [T; N] { + /// Returns the length of the slice. #[builtin(array_len)] pub fn len(self) -> u32 {} diff --git a/noir/noir-repo/noir_stdlib/src/collections/map.nr b/noir/noir-repo/noir_stdlib/src/collections/map.nr index bd50f345356..4607b06d667 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/map.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/map.nr @@ -77,10 +77,10 @@ impl Slot { // While conducting lookup, we iterate attempt from 0 to N - 1 due to heuristic, // that if we have went that far without finding desired, // it is very unlikely to be after - performance will be heavily degraded. -impl HashMap { +impl HashMap { // Creates a new instance of HashMap with specified BuildHasher. // docs:start:with_hasher - pub fn with_hasher(_build_hasher: B) -> Self + pub fn with_hasher(_build_hasher: B) -> Self where B: BuildHasher { // docs:end:with_hasher @@ -99,7 +99,7 @@ impl HashMap { // Returns true if the map contains a value for the specified key. // docs:start:contains_key - pub fn contains_key( + pub fn contains_key( self, key: K ) -> bool @@ -183,7 +183,7 @@ impl HashMap { // For each key-value entry applies mutator function. // docs:start:iter_mut - pub fn iter_mut( + pub fn iter_mut( &mut self, f: fn(K, V) -> (K, V) ) @@ -208,7 +208,7 @@ impl HashMap { // For each key applies mutator function. // docs:start:iter_keys_mut - pub fn iter_keys_mut( + pub fn iter_keys_mut( &mut self, f: fn(K) -> K ) @@ -278,7 +278,7 @@ impl HashMap { // Get the value by key. If it does not exist, returns none(). // docs:start:get - pub fn get( + pub fn get( self, key: K ) -> Option @@ -313,7 +313,7 @@ impl HashMap { // Insert key-value entry. In case key was already present, value is overridden. // docs:start:insert - pub fn insert( + pub fn insert( &mut self, key: K, value: V @@ -356,7 +356,7 @@ impl HashMap { // Removes a key-value entry. If key is not present, HashMap remains unchanged. // docs:start:remove - pub fn remove( + pub fn remove( &mut self, key: K ) @@ -388,7 +388,7 @@ impl HashMap { } // Apply HashMap's hasher onto key to obtain pre-hash for probing. - fn hash( + fn hash( self, key: K ) -> u32 diff --git a/noir/noir-repo/noir_stdlib/src/collections/umap.nr b/noir/noir-repo/noir_stdlib/src/collections/umap.nr index 86ae79ea644..c552c053a92 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/umap.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/umap.nr @@ -76,10 +76,10 @@ impl Slot { // While conducting lookup, we iterate attempt from 0 to N - 1 due to heuristic, // that if we have went that far without finding desired, // it is very unlikely to be after - performance will be heavily degraded. -impl UHashMap { +impl UHashMap { // Creates a new instance of UHashMap with specified BuildHasher. // docs:start:with_hasher - pub fn with_hasher(_build_hasher: B) -> Self + pub fn with_hasher(_build_hasher: B) -> Self where B: BuildHasher { // docs:end:with_hasher @@ -88,7 +88,7 @@ impl UHashMap { Self { _table, _len, _build_hasher } } - pub fn with_hasher_and_capacity(_build_hasher: B, capacity: u32) -> Self + pub fn with_hasher_and_capacity(_build_hasher: B, capacity: u32) -> Self where B: BuildHasher { // docs:end:with_hasher @@ -110,7 +110,7 @@ impl UHashMap { // Returns true if the map contains a value for the specified key. // docs:start:contains_key - pub fn contains_key( + pub fn contains_key( self, key: K ) -> bool @@ -194,7 +194,7 @@ impl UHashMap { // For each key-value entry applies mutator function. // docs:start:iter_mut - unconstrained pub fn iter_mut( + unconstrained pub fn iter_mut( &mut self, f: fn(K, V) -> (K, V) ) @@ -216,7 +216,7 @@ impl UHashMap { // For each key applies mutator function. // docs:start:iter_keys_mut - unconstrained pub fn iter_keys_mut( + unconstrained pub fn iter_keys_mut( &mut self, f: fn(K) -> K ) @@ -283,7 +283,7 @@ impl UHashMap { // Get the value by key. If it does not exist, returns none(). // docs:start:get - unconstrained pub fn get( + unconstrained pub fn get( self, key: K ) -> Option @@ -315,7 +315,7 @@ impl UHashMap { // Insert key-value entry. In case key was already present, value is overridden. // docs:start:insert - unconstrained pub fn insert( + unconstrained pub fn insert( &mut self, key: K, value: V @@ -353,7 +353,7 @@ impl UHashMap { } } - unconstrained fn try_resize(&mut self) + unconstrained fn try_resize(&mut self) where B: BuildHasher, K: Eq + Hash, H: Hasher { if self.len() + 1 >= self.capacity() / 2 { let capacity = self.capacity() * 2; @@ -368,7 +368,7 @@ impl UHashMap { // Removes a key-value entry. If key is not present, UHashMap remains unchanged. // docs:start:remove - unconstrained pub fn remove( + unconstrained pub fn remove( &mut self, key: K ) @@ -397,7 +397,7 @@ impl UHashMap { } // Apply UHashMap's hasher onto key to obtain pre-hash for probing. - fn hash( + fn hash( self, key: K ) -> u32 diff --git a/noir/noir-repo/noir_stdlib/src/field/mod.nr b/noir/noir-repo/noir_stdlib/src/field/mod.nr index 4b6deaa1106..534ac012beb 100644 --- a/noir/noir-repo/noir_stdlib/src/field/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/field/mod.nr @@ -2,35 +2,85 @@ mod bn254; use bn254::lt as bn254_lt; impl Field { + /// Asserts that `self` can be represented in `bit_size` bits. + /// + /// # Failures + /// Causes a constraint failure for `Field` values exceeding `2^{bit_size}`. + pub fn assert_max_bit_size(self, bit_size: u32) { + crate::assert_constant(bit_size); + assert(bit_size < modulus_num_bits() as u32); + self.__assert_max_bit_size(bit_size); + } + + #[builtin(apply_range_constraint)] + fn __assert_max_bit_size(self, bit_size: u32) {} + + /// Decomposes `self` into its little endian bit decomposition as a `[u1]` slice of length `bit_size`. + /// This slice will be zero padded should not all bits be necessary to represent `self`. + /// + /// # Failures + /// Causes a constraint failure for `Field` values exceeding `2^{bit_size}` as the resulting slice will not + /// be able to represent the original `Field`. + /// + /// # Safety + /// Values of `bit_size` equal to or greater than the number of bits necessary to represent the `Field` modulus + /// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will + /// wrap around due to overflow when verifying the decomposition. pub fn to_le_bits(self: Self, bit_size: u32) -> [u1] { crate::assert_constant(bit_size); self.__to_le_bits(bit_size) } + /// Decomposes `self` into its big endian bit decomposition as a `[u1]` slice of length `bit_size`. + /// This slice will be zero padded should not all bits be necessary to represent `self`. + /// + /// # Failures + /// Causes a constraint failure for `Field` values exceeding `2^{bit_size}` as the resulting slice will not + /// be able to represent the original `Field`. + /// + /// # Safety + /// Values of `bit_size` equal to or greater than the number of bits necessary to represent the `Field` modulus + /// (e.g. 254 for the BN254 field) allow for multiple bit decompositions. This is due to how the `Field` will + /// wrap around due to overflow when verifying the decomposition. pub fn to_be_bits(self: Self, bit_size: u32) -> [u1] { crate::assert_constant(bit_size); self.__to_be_bits(bit_size) } + /// See `Field.to_be_bits` #[builtin(to_le_bits)] fn __to_le_bits(self, _bit_size: u32) -> [u1] {} + /// See `Field.to_le_bits` #[builtin(to_be_bits)] fn __to_be_bits(self, bit_size: u32) -> [u1] {} - #[builtin(apply_range_constraint)] - fn __assert_max_bit_size(self, bit_size: u32) {} - - pub fn assert_max_bit_size(self: Self, bit_size: u32) { - crate::assert_constant(bit_size); - assert(bit_size < modulus_num_bits() as u32); - self.__assert_max_bit_size(bit_size); - } - + /// Decomposes `self` into its little endian byte decomposition as a `[u8]` slice of length `byte_size`. + /// This slice will be zero padded should not all bytes be necessary to represent `self`. + /// + /// # Failures + /// Causes a constraint failure for `Field` values exceeding `2^{8*byte_size}` as the resulting slice will not + /// be able to represent the original `Field`. + /// + /// # Safety + /// Values of `byte_size` equal to or greater than the number of bytes necessary to represent the `Field` modulus + /// (e.g. 32 for the BN254 field) allow for multiple byte decompositions. This is due to how the `Field` will + /// wrap around due to overflow when verifying the decomposition. pub fn to_le_bytes(self: Self, byte_size: u32) -> [u8] { self.to_le_radix(256, byte_size) } + /// Decomposes `self` into its big endian byte decomposition as a `[u8]` slice of length `byte_size`. + /// This slice will be zero padded should not all bytes be necessary to represent `self`. + /// + /// # Failures + /// Causes a constraint failure for `Field` values exceeding `2^{8*byte_size}` as the resulting slice will not + /// be able to represent the original `Field`. + /// + /// # Safety + /// Values of `byte_size` equal to or greater than the number of bytes necessary to represent the `Field` modulus + /// (e.g. 32 for the BN254 field) allow for multiple byte decompositions. This is due to how the `Field` will + /// wrap around due to overflow when verifying the decomposition. pub fn to_be_bytes(self: Self, byte_size: u32) -> [u8] { self.to_be_radix(256, byte_size) } @@ -47,7 +97,6 @@ impl Field { self.__to_be_radix(radix, result_len) } - // decompose `_self` into a `_result_len` vector over the `_radix` basis // `_radix` must be less than 256 #[builtin(to_le_radix)] fn __to_le_radix(self, radix: u32, result_len: u32) -> [u8] {} diff --git a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr index bb8a9cc2ce2..0c31d238f66 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr @@ -1,19 +1,27 @@ +use crate::collections::vec::Vec; +use crate::runtime::is_unconstrained; + global LIMBS_PER_BLOCK = 17; //BLOCK_SIZE / 8; global NUM_KECCAK_LANES = 25; global BLOCK_SIZE = 136; //(1600 - BITS * 2) / WORD_SIZE; global WORD_SIZE = 8; -use crate::collections::vec::Vec; - #[foreign(keccakf1600)] fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} #[no_predicates] -pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u8; 32] { +pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { assert(N >= message_size); - for i in 0..N { - if i >= message_size { - input[i] = 0; + let mut block_bytes = [0; BLOCK_SIZE]; + if is_unconstrained() { + for i in 0..message_size { + block_bytes[i] = input[i]; + } + } else { + for i in 0..N { + if i < message_size { + block_bytes[i] = input[i]; + } } } @@ -24,11 +32,6 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u let real_max_blocks = (message_size + BLOCK_SIZE) / BLOCK_SIZE; let real_blocks_bytes = real_max_blocks * BLOCK_SIZE; - let mut block_bytes = [0; BLOCK_SIZE]; - for i in 0..N { - block_bytes[i] = input[i]; - } - block_bytes[message_size] = 1; block_bytes[real_blocks_bytes - 1] = 0x80; @@ -36,28 +39,28 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u // means we need to swap our byte ordering let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE; for i in 0..num_limbs { - let mut temp = [0; 8]; - for j in 0..8 { - temp[j] = block_bytes[8*i+j]; + let mut temp = [0; WORD_SIZE]; + let word_size_times_i = WORD_SIZE * i; + for j in 0..WORD_SIZE { + temp[j] = block_bytes[word_size_times_i+j]; } - for j in 0..8 { - block_bytes[8 * i + j] = temp[7 - j]; + for j in 0..WORD_SIZE { + block_bytes[word_size_times_i + j] = temp[7 - j]; } } - let byte_size = max_blocks_length; + let mut sliced_buffer = Vec::new(); - for _i in 0..num_limbs { - sliced_buffer.push(0); - } // populate a vector of 64-bit limbs from our byte array for i in 0..num_limbs { + let word_size_times_i = i * WORD_SIZE; + let ws_times_i_plus_7 = word_size_times_i + 7; let mut sliced = 0; - if (i * WORD_SIZE + WORD_SIZE > byte_size) { - let slice_size = byte_size - (i * WORD_SIZE); + if (word_size_times_i + WORD_SIZE > max_blocks_length) { + let slice_size = max_blocks_length - word_size_times_i; let byte_shift = (WORD_SIZE - slice_size) * 8; let mut v = 1; for k in 0..slice_size { - sliced += v * (block_bytes[i * WORD_SIZE+7-k] as Field); + sliced += v * (block_bytes[ws_times_i_plus_7-k] as Field); v *= 256; } let w = 1 << (byte_shift as u8); @@ -65,22 +68,20 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u } else { let mut v = 1; for k in 0..WORD_SIZE { - sliced += v * (block_bytes[i * WORD_SIZE+7-k] as Field); + sliced += v * (block_bytes[ws_times_i_plus_7-k] as Field); v *= 256; } } - sliced_buffer.set(i, sliced as u64); + + sliced_buffer.push(sliced as u64); } //2. sponge_absorb - let num_blocks = max_blocks; let mut state : [u64;NUM_KECCAK_LANES]= [0; NUM_KECCAK_LANES]; - let mut under_block = true; - for i in 0..num_blocks { - if i == real_max_blocks { - under_block = false; - } - if under_block { + // When in an unconstrained runtime we can take advantage of runtime loop bounds, + // thus allowing us to simplify the loop body. + if is_unconstrained() { + for i in 0..real_max_blocks { if (i == 0) { for j in 0..LIMBS_PER_BLOCK { state[j] = sliced_buffer.get(j); @@ -92,6 +93,22 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u } state = keccakf1600(state); } + } else { + // `real_max_blocks` is guaranteed to at least be `1` + // We peel out the first block as to avoid a conditional inside of the loop. + // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime. + for j in 0..LIMBS_PER_BLOCK { + state[j] = sliced_buffer.get(j); + } + state = keccakf1600(state); + for i in 1..max_blocks { + if i < real_max_blocks { + for j in 0..LIMBS_PER_BLOCK { + state[j] = state[j] ^ sliced_buffer.get(i * LIMBS_PER_BLOCK + j); + } + state = keccakf1600(state); + } + } } //3. sponge_squeeze diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr index 9626da0cf97..902d3cc8104 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr @@ -26,73 +26,40 @@ impl Poseidon2 { result } - fn perform_duplex(&mut self) -> [Field; RATE] { - // zero-pad the cache - for i in 0..RATE { - if i >= self.cache_size { - self.cache[i] = 0; - } - } + fn perform_duplex(&mut self) { // add the cache into sponge state for i in 0..RATE { - self.state[i] += self.cache[i]; + // We effectively zero-pad the cache by only adding to the state + // cache that is less than the specified `cache_size` + if i < self.cache_size { + self.state[i] += self.cache[i]; + } } self.state = crate::hash::poseidon2_permutation(self.state, 4); - // return `RATE` number of field elements from the sponge state. - let mut result = [0; RATE]; - for i in 0..RATE { - result[i] = self.state[i]; - } - result } fn absorb(&mut self, input: Field) { - if (!self.squeeze_mode) & (self.cache_size == RATE) { + assert(!self.squeeze_mode); + if self.cache_size == RATE { // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache - let _ = self.perform_duplex(); + self.perform_duplex(); self.cache[0] = input; self.cache_size = 1; - } else if (!self.squeeze_mode) & (self.cache_size != RATE) { + } else { // If we're absorbing, and the cache is not full, add the input into the cache self.cache[self.cache_size] = input; self.cache_size += 1; - } else if self.squeeze_mode { - // If we're in squeeze mode, switch to absorb mode and add the input into the cache. - // N.B. I don't think this code path can be reached?! - self.cache[0] = input; - self.cache_size = 1; - self.squeeze_mode = false; } } fn squeeze(&mut self) -> Field { - if self.squeeze_mode & (self.cache_size == 0) { - // If we're in squeze mode and the cache is empty, there is nothing left to squeeze out of the sponge! - // Switch to absorb mode. - self.squeeze_mode = false; - self.cache_size = 0; - } - if !self.squeeze_mode { - // If we're in absorb mode, apply sponge permutation to compress the cache, populate cache with compressed - // state and switch to squeeze mode. Note: this code block will execute if the previous `if` condition was - // matched - let new_output_elements = self.perform_duplex(); - self.squeeze_mode = true; - for i in 0..RATE { - self.cache[i] = new_output_elements[i]; - } - self.cache_size = RATE; - } - // By this point, we should have a non-empty cache. Pop one item off the top of the cache and return it. - let result = self.cache[0]; - for i in 1..RATE { - if i < self.cache_size { - self.cache[i - 1] = self.cache[i]; - } - } - self.cache_size -= 1; - self.cache[self.cache_size] = 0; - result + assert(!self.squeeze_mode); + // If we're in absorb mode, apply sponge permutation to compress the cache. + self.perform_duplex(); + self.squeeze_mode = true; + + // Pop one item off the top of the permutation and return it. + self.state[0] } fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field { diff --git a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr index 5035be4b73e..50cf2f965f9 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/sha256.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/sha256.nr @@ -1,3 +1,5 @@ +use crate::runtime::is_unconstrained; + // Implementation of SHA-256 mapping a byte array of variable length to // 32 bytes. @@ -17,82 +19,230 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { sha256_var(msg, N as u64) } +// Convert 64-byte array to array of 16 u32s +fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] { + let mut msg32: [u32; 16] = [0; 16]; + + for i in 0..16 { + let mut msg_field: Field = 0; + for j in 0..4 { + msg_field = msg_field * 256 + msg[64 - 4*(i + 1) + j] as Field; + } + msg32[15 - i] = msg_field as u32; + } + + msg32 +} + +unconstrained fn build_msg_block_iter(msg: [u8; N], message_size: u64, msg_start: u32) -> ([u8; 64], u64) { + let mut msg_block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; + let mut msg_byte_ptr: u64 = 0; // Message byte pointer + let mut msg_end = msg_start + BLOCK_SIZE; + if msg_end > N { + msg_end = N; + } + for k in msg_start..msg_end { + if k as u64 < message_size { + msg_block[msg_byte_ptr] = msg[k]; + msg_byte_ptr = msg_byte_ptr + 1; + } + } + (msg_block, msg_byte_ptr) +} + +// Verify the block we are compressing was appropriately constructed +fn verify_msg_block( + msg: [u8; N], + message_size: u64, + msg_block: [u8; 64], + msg_start: u32 +) -> u64 { + let mut msg_byte_ptr: u64 = 0; // Message byte pointer + let mut msg_end = msg_start + BLOCK_SIZE; + let mut extra_bytes = 0; + if msg_end > N { + msg_end = N; + extra_bytes = msg_end - N; + } + + for k in msg_start..msg_end { + if k as u64 < message_size { + assert_eq(msg_block[msg_byte_ptr], msg[k]); + msg_byte_ptr = msg_byte_ptr + 1; + } + } + + msg_byte_ptr +} + +global BLOCK_SIZE = 64; +global ZERO = 0; + // Variable size SHA-256 hash pub fn sha256_var(msg: [u8; N], message_size: u64) -> [u8; 32] { - let mut msg_block: [u8; 64] = [0; 64]; + let num_blocks = N / BLOCK_SIZE; + let mut msg_block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; let mut h: [u32; 8] = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]; // Intermediate hash, starting with the canonical initial value - let mut i: u64 = 0; // Message byte pointer - for k in 0..N { - if k as u64 < message_size { - // Populate msg_block - msg_block[i] = msg[k]; - i = i + 1; - if i == 64 { - // Enough to hash block - h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h); - - i = 0; + let mut msg_byte_ptr = 0; // Pointer into msg_block + + for i in 0..num_blocks { + let msg_start = BLOCK_SIZE * i; + let (new_msg_block, new_msg_byte_ptr) = unsafe { + build_msg_block_iter(msg, message_size, msg_start) + }; + if msg_start as u64 < message_size { + msg_block = new_msg_block; + } + + if !is_unconstrained() { + // Verify the block we are compressing was appropriately constructed + let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); + if msg_start as u64 < message_size { + msg_byte_ptr = new_msg_byte_ptr; } + } else if msg_start as u64 < message_size { + msg_byte_ptr = new_msg_byte_ptr; + } + + // If the block is filled, compress it. + // An un-filled block is handled after this loop. + if msg_byte_ptr == 64 { + h = sha256_compression(msg_u8_to_u32(msg_block), h); } } + + let modulo = N % BLOCK_SIZE; + // Handle setup of the final msg block. + // This case is only hit if the msg is less than the block size, + // or our message cannot be evenly split into blocks. + if modulo != 0 { + let msg_start = BLOCK_SIZE * num_blocks; + let (new_msg_block, new_msg_byte_ptr) = unsafe { + build_msg_block_iter(msg, message_size, msg_start) + }; + + if msg_start as u64 < message_size { + msg_block = new_msg_block; + } + + if !is_unconstrained() { + let new_msg_byte_ptr = verify_msg_block(msg, message_size, msg_block, msg_start); + if msg_start as u64 < message_size { + msg_byte_ptr = new_msg_byte_ptr; + } + } else if msg_start as u64 < message_size { + msg_byte_ptr = new_msg_byte_ptr; + } + } + + if msg_byte_ptr == BLOCK_SIZE as u64 { + msg_byte_ptr = 0; + } + + // This variable is used to get around the compiler under-constrained check giving a warning. + // We want to check against a constant zero, but if it does not come from the circuit inputs + // or return values the compiler check will issue a warning. + let zero = msg_block[0] - msg_block[0]; + // Pad the rest such that we have a [u32; 2] block at the end representing the length - // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - msg_block[i] = 1 << 7; - i = i + 1; - // If i >= 57, there aren't enough bits in the current message block to accomplish this, so - // the 1 and 0s fill up the current block, which we then compress accordingly. - if i >= 57 { + // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). + msg_block[msg_byte_ptr] = 1 << 7; + let last_block = msg_block; + msg_byte_ptr = msg_byte_ptr + 1; + + unsafe { + let (new_msg_block, new_msg_byte_ptr) = pad_msg_block(msg_block, msg_byte_ptr); + msg_block = new_msg_block; + if crate::runtime::is_unconstrained() { + msg_byte_ptr = new_msg_byte_ptr; + } + } + + if !crate::runtime::is_unconstrained() { + for i in 0..64 { + assert_eq(msg_block[i], last_block[i]); + } + + // If i >= 57, there aren't enough bits in the current message block to accomplish this, so + // the 1 and 0s fill up the current block, which we then compress accordingly. // Not enough bits (64) to store length. Fill up with zeros. - if i < 64 { - for _i in 57..64 { - if i <= 63 { - msg_block[i] = 0; - i += 1; - } + for _i in 57..64 { + if msg_byte_ptr <= 63 & msg_byte_ptr >= 57 { + assert_eq(msg_block[msg_byte_ptr], zero); + msg_byte_ptr += 1; } } - h = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), h); + } + + if msg_byte_ptr >= 57 { + h = sha256_compression(msg_u8_to_u32(msg_block), h); - i = 0; + msg_byte_ptr = 0; } - let len = 8 * message_size; - let len_bytes = (len as Field).to_le_bytes(8); - for _i in 0..64 { - // In any case, fill blocks up with zeros until the last 64 (i.e. until i = 56). - if i < 56 { - msg_block[i] = 0; - i = i + 1; - } else if i < 64 { - for j in 0..8 { - msg_block[63 - j] = len_bytes[j]; + msg_block = unsafe { + attach_len_to_msg_block(msg_block, msg_byte_ptr, message_size) + }; + + if !crate::runtime::is_unconstrained() { + for i in 0..56 { + if i < msg_byte_ptr { + assert_eq(msg_block[i], last_block[i]); + } else { + assert_eq(msg_block[i], zero); } - i += 8; + } + + let len = 8 * message_size; + let len_bytes = (len as Field).to_be_bytes(8); + for i in 56..64 { + assert_eq(msg_block[i], len_bytes[i - 56]); } } + hash_final_block(msg_block, h) } -// Convert 64-byte array to array of 16 u32s -fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] { - let mut msg32: [u32; 16] = [0; 16]; - - for i in 0..16 { - let mut msg_field: Field = 0; - for j in 0..4 { - msg_field = msg_field * 256 + msg[64 - 4*(i + 1) + j] as Field; +unconstrained fn pad_msg_block(mut msg_block: [u8; 64], mut msg_byte_ptr: u64) -> ([u8; 64], u64) { + // If i >= 57, there aren't enough bits in the current message block to accomplish this, so + // the 1 and 0s fill up the current block, which we then compress accordingly. + if msg_byte_ptr >= 57 { + // Not enough bits (64) to store length. Fill up with zeros. + if msg_byte_ptr < 64 { + for _ in 57..64 { + if msg_byte_ptr <= 63 { + msg_block[msg_byte_ptr] = 0; + msg_byte_ptr += 1; + } + } } - msg32[15 - i] = msg_field as u32; } + (msg_block, msg_byte_ptr) +} - msg32 +unconstrained fn attach_len_to_msg_block(mut msg_block: [u8; 64], mut msg_byte_ptr: u64, message_size: u64) -> [u8; 64] { + let len = 8 * message_size; + let len_bytes = (len as Field).to_be_bytes(8); + for _i in 0..64 { + // In any case, fill blocks up with zeros until the last 64 (i.e. until msg_byte_ptr = 56). + if msg_byte_ptr < 56 { + msg_block[msg_byte_ptr] = 0; + msg_byte_ptr = msg_byte_ptr + 1; + } else if msg_byte_ptr < 64 { + for j in 0..8 { + msg_block[msg_byte_ptr + j] = len_bytes[j]; + } + msg_byte_ptr += 8; + } + } + msg_block } fn hash_final_block(msg_block: [u8; 64], mut state: [u32; 8]) -> [u8; 32] { let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes // Hash final padded block - state = crate::hash::sha256_compression(msg_u8_to_u32(msg_block), state); + state = sha256_compression(msg_u8_to_u32(msg_block), state); // Return final hash as byte array for j in 0..8 { @@ -104,3 +254,4 @@ fn hash_final_block(msg_block: [u8; 64], mut state: [u32; 8]) -> [u8; 32] { out_h } + diff --git a/noir/noir-repo/noir_stdlib/src/meta/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index ee3980f8f54..9b5eee03229 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -8,6 +8,11 @@ impl Expr { fn as_array(self) -> Option<[Expr]> {} // docs:end:as_array + #[builtin(expr_as_assert)] + // docs:start:as_assert + fn as_assert(self) -> Option<(Expr, Option)> {} + // docs:end:as_assert + #[builtin(expr_as_assign)] // docs:start:as_assign fn as_assign(self) -> Option<(Expr, Expr)> {} @@ -110,4 +115,522 @@ impl Expr { // docs:start:is_continue fn is_continue(self) -> bool {} // docs:end:is_continue + + // docs:start:modify + fn modify(self, f: fn[Env](Expr) -> Option) -> Expr { + // docs:end:modify + let result = modify_array(self, f); + let result = result.or_else(|| modify_assert(self, f)); + let result = result.or_else(|| modify_assign(self, f)); + let result = result.or_else(|| modify_binary_op(self, f)); + let result = result.or_else(|| modify_block(self, f)); + let result = result.or_else(|| modify_cast(self, f)); + let result = result.or_else(|| modify_comptime(self, f)); + let result = result.or_else(|| modify_if(self, f)); + let result = result.or_else(|| modify_index(self, f)); + let result = result.or_else(|| modify_function_call(self, f)); + let result = result.or_else(|| modify_member_access(self, f)); + let result = result.or_else(|| modify_method_call(self, f)); + let result = result.or_else(|| modify_repeated_element_array(self, f)); + let result = result.or_else(|| modify_repeated_element_slice(self, f)); + let result = result.or_else(|| modify_slice(self, f)); + let result = result.or_else(|| modify_tuple(self, f)); + let result = result.or_else(|| modify_unary_op(self, f)); + let result = result.or_else(|| modify_unsafe(self, f)); + if result.is_some() { + let result = result.unwrap_unchecked(); + let modified = f(result); + modified.unwrap_or(result) + } else { + f(self).unwrap_or(self) + } + } + + // docs:start:quoted + fn quoted(self) -> Quoted { + // docs:end:quoted + quote { $self } + } + + #[builtin(expr_resolve)] + // docs:start:resolve + fn resolve(self, in_function: Option) -> TypedExpr {} + // docs:end:resolve +} + +fn modify_array(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_array().map( + |exprs: [Expr]| { + let exprs = modify_expressions(exprs, f); + new_array(exprs) + } + ) +} + +fn modify_assert(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_assert().map( + |expr: (Expr, Option)| { + let (predicate, msg) = expr; + let predicate = predicate.modify(f); + let msg = msg.map(|msg: Expr| msg.modify(f)); + new_assert(predicate, msg) + } + ) +} + +fn modify_assign(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_assign().map( + |expr: (Expr, Expr)| { + let (lhs, rhs) = expr; + let lhs = lhs.modify(f); + let rhs = rhs.modify(f); + new_assign(lhs, rhs) + } + ) +} + +fn modify_binary_op(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_binary_op().map( + |expr: (Expr, BinaryOp, Expr)| { + let (lhs, op, rhs) = expr; + let lhs = lhs.modify(f); + let rhs = rhs.modify(f); + new_binary_op(lhs, op, rhs) + } + ) +} + +fn modify_block(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_block().map( + |exprs: [Expr]| { + let exprs = modify_expressions(exprs, f); + new_block(exprs) + } + ) +} + +fn modify_cast(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_cast().map( + |expr: (Expr, UnresolvedType)| { + let (expr, typ) = expr; + let expr = expr.modify(f); + new_cast(expr, typ) + } + ) +} + +fn modify_comptime(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_comptime().map( + |exprs: [Expr]| { + let exprs = exprs.map(|expr: Expr| expr.modify(f)); + new_comptime(exprs) + } + ) +} + +fn modify_function_call(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_function_call().map( + |expr: (Expr, [Expr])| { + let (function, arguments) = expr; + let function = function.modify(f); + let arguments = arguments.map(|arg: Expr| arg.modify(f)); + new_function_call(function, arguments) + } + ) +} + +fn modify_if(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_if().map( + |expr: (Expr, Expr, Option)| { + let (condition, consequence, alternative) = expr; + let condition = condition.modify(f); + let consequence = consequence.modify(f); + let alternative = alternative.map(|alternative: Expr| alternative.modify(f)); + new_if(condition, consequence, alternative) + } + ) +} + +fn modify_index(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_index().map( + |expr: (Expr, Expr)| { + let (object, index) = expr; + let object = object.modify(f); + let index = index.modify(f); + new_index(object, index) + } + ) +} + +fn modify_member_access(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_member_access().map( + |expr: (Expr, Quoted)| { + let (object, name) = expr; + let object = object.modify(f); + new_member_access(object, name) + } + ) +} + +fn modify_method_call(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_method_call().map( + |expr: (Expr, Quoted, [UnresolvedType], [Expr])| { + let (object, name, generics, arguments) = expr; + let object = object.modify(f); + let arguments = arguments.map(|arg: Expr| arg.modify(f)); + new_method_call(object, name, generics, arguments) + } + ) +} + +fn modify_repeated_element_array(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_repeated_element_array().map( + |expr: (Expr, Expr)| { + let (expr, length) = expr; + let expr = expr.modify(f); + let length = length.modify(f); + new_repeated_element_array(expr, length) + } + ) +} + +fn modify_repeated_element_slice(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_repeated_element_slice().map( + |expr: (Expr, Expr)| { + let (expr, length) = expr; + let expr = expr.modify(f); + let length = length.modify(f); + new_repeated_element_slice(expr, length) + } + ) +} + +fn modify_slice(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_slice().map( + |exprs: [Expr]| { + let exprs = modify_expressions(exprs, f); + new_slice(exprs) + } + ) +} + +fn modify_tuple(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_tuple().map( + |exprs: [Expr]| { + let exprs = modify_expressions(exprs, f); + new_tuple(exprs) + } + ) +} + +fn modify_unary_op(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_unary_op().map( + |expr: (UnaryOp, Expr)| { + let (op, rhs) = expr; + let rhs = rhs.modify(f); + new_unary_op(op, rhs) + } + ) +} + +fn modify_unsafe(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_unsafe().map( + |exprs: [Expr]| { + let exprs = exprs.map(|expr: Expr| expr.modify(f)); + new_unsafe(exprs) + } + ) +} + +fn modify_expressions(exprs: [Expr], f: fn[Env](Expr) -> Option) -> [Expr] { + exprs.map(|expr: Expr| expr.modify(f)) +} + +fn new_array(exprs: [Expr]) -> Expr { + let exprs = join_expressions(exprs, quote { , }); + quote { [$exprs]}.as_expr().unwrap() +} + +fn new_assert(predicate: Expr, msg: Option) -> Expr { + if msg.is_some() { + let msg = msg.unwrap(); + quote { assert($predicate, $msg) }.as_expr().unwrap() + } else { + quote { assert($predicate) }.as_expr().unwrap() + } +} + +fn new_assign(lhs: Expr, rhs: Expr) -> Expr { + quote { $lhs = $rhs }.as_expr().unwrap() +} + +fn new_binary_op(lhs: Expr, op: BinaryOp, rhs: Expr) -> Expr { + let op = op.quoted(); + quote { ($lhs) $op ($rhs) }.as_expr().unwrap() +} + +fn new_block(exprs: [Expr]) -> Expr { + let exprs = join_expressions(exprs, quote { ; }); + quote { { $exprs }}.as_expr().unwrap() +} + +fn new_cast(expr: Expr, typ: UnresolvedType) -> Expr { + quote { ($expr) as $typ }.as_expr().unwrap() +} + +fn new_comptime(exprs: [Expr]) -> Expr { + let exprs = join_expressions(exprs, quote { ; }); + quote { comptime { $exprs }}.as_expr().unwrap() +} + +fn new_if(condition: Expr, consequence: Expr, alternative: Option) -> Expr { + if alternative.is_some() { + let alternative = alternative.unwrap(); + quote { if $condition { $consequence } else { $alternative }}.as_expr().unwrap() + } else { + quote { if $condition { $consequence } }.as_expr().unwrap() + } +} + +fn new_index(object: Expr, index: Expr) -> Expr { + quote { $object[$index] }.as_expr().unwrap() +} + +fn new_member_access(object: Expr, name: Quoted) -> Expr { + quote { $object.$name }.as_expr().unwrap() +} + +fn new_function_call(function: Expr, arguments: [Expr]) -> Expr { + let arguments = join_expressions(arguments, quote { , }); + + quote { $function($arguments) }.as_expr().unwrap() +} + +fn new_method_call(object: Expr, name: Quoted, generics: [UnresolvedType], arguments: [Expr]) -> Expr { + let arguments = join_expressions(arguments, quote { , }); + + if generics.len() == 0 { + quote { $object.$name($arguments) }.as_expr().unwrap() + } else { + let generics = generics.map(|generic| quote { $generic }).join(quote { , }); + quote { $object.$name::<$generics>($arguments) }.as_expr().unwrap() + } +} + +fn new_repeated_element_array(expr: Expr, length: Expr) -> Expr { + quote { [$expr; $length] }.as_expr().unwrap() +} + +fn new_repeated_element_slice(expr: Expr, length: Expr) -> Expr { + quote { &[$expr; $length] }.as_expr().unwrap() +} + +fn new_slice(exprs: [Expr]) -> Expr { + let exprs = join_expressions(exprs, quote { , }); + quote { &[$exprs]}.as_expr().unwrap() +} + +fn new_tuple(exprs: [Expr]) -> Expr { + let exprs = join_expressions(exprs, quote { , }); + quote { ($exprs) }.as_expr().unwrap() +} + +fn new_unary_op(op: UnaryOp, rhs: Expr) -> Expr { + let op = op.quoted(); + quote { $op($rhs) }.as_expr().unwrap() +} + +fn new_unsafe(exprs: [Expr]) -> Expr { + let exprs = join_expressions(exprs, quote { ; }); + quote { unsafe { $exprs }}.as_expr().unwrap() +} + +fn join_expressions(exprs: [Expr], separator: Quoted) -> Quoted { + exprs.map(|expr: Expr| expr.quoted()).join(separator) +} + +mod tests { + use crate::meta::op::UnaryOp; + use crate::meta::op::BinaryOp; + + #[test] + fn test_expr_as_array() { + comptime + { + let expr = quote { [1, 2, 4] }.as_expr().unwrap(); + let elems = expr.as_array().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (2, false)); + assert_eq(elems[2].as_integer().unwrap(), (4, false)); + } + } + + #[test] + fn test_expr_as_integer() { + comptime + { + let expr = quote { 1 }.as_expr().unwrap(); + assert_eq((1, false), expr.as_integer().unwrap()); + + let expr = quote { -2 }.as_expr().unwrap(); + assert_eq((2, true), expr.as_integer().unwrap()); + } + } + + #[test] + fn test_expr_as_binary_op() { + comptime + { + assert(get_binary_op(quote { x + y }).is_add()); + assert(get_binary_op(quote { x - y }).is_subtract()); + assert(get_binary_op(quote { x * y }).is_multiply()); + assert(get_binary_op(quote { x / y }).is_divide()); + assert(get_binary_op(quote { x == y }).is_equal()); + assert(get_binary_op(quote { x != y }).is_not_equal()); + assert(get_binary_op(quote { x < y }).is_less_than()); + assert(get_binary_op(quote { x <= y }).is_less_than_or_equal()); + assert(get_binary_op(quote { x > y }).is_greater_than()); + assert(get_binary_op(quote { x >= y }).is_greater_than_or_equal()); + assert(get_binary_op(quote { x & y }).is_and()); + assert(get_binary_op(quote { x | y }).is_or()); + assert(get_binary_op(quote { x ^ y }).is_xor()); + assert(get_binary_op(quote { x >> y }).is_shift_right()); + assert(get_binary_op(quote { x << y }).is_shift_left()); + assert(get_binary_op(quote { x % y }).is_modulo()); + } + } + + #[test] + fn test_expr_as_bool() { + comptime + { + let expr = quote { false }.as_expr().unwrap(); + assert(expr.as_bool().unwrap() == false); + + let expr = quote { true }.as_expr().unwrap(); + assert_eq(expr.as_bool().unwrap(), true); + } + } + + #[test] + fn test_expr_as_function_call() { + comptime + { + let expr = quote { foo(42) }.as_expr().unwrap(); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (42, false)); + } + } + + #[test] + fn test_expr_as_if() { + comptime + { + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let (_condition, _consequence, alternative) = expr.as_if().unwrap(); + assert(alternative.is_some()); + } + } + + #[test] + fn test_expr_as_index() { + comptime + { + let expr = quote { foo[bar] }.as_expr().unwrap(); + assert(expr.as_index().is_some()); + } + } + + #[test] + fn test_expr_as_member_access() { + comptime + { + let expr = quote { foo.bar }.as_expr().unwrap(); + let (_, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); + } + } + + #[test] + fn test_expr_as_repeated_element_array() { + comptime + { + let expr = quote { [1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_array().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_repeated_element_slice() { + comptime + { + let expr = quote { &[1; 3] }.as_expr().unwrap(); + let (expr, length) = expr.as_repeated_element_slice().unwrap(); + assert_eq(expr.as_integer().unwrap(), (1, false)); + assert_eq(length.as_integer().unwrap(), (3, false)); + } + } + + #[test] + fn test_expr_as_slice() { + comptime + { + let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); + let elems = expr.as_slice().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (1, false)); + assert_eq(elems[1].as_integer().unwrap(), (3, false)); + assert_eq(elems[2].as_integer().unwrap(), (5, false)); + } + } + + #[test] + fn test_expr_as_tuple() { + comptime + { + let expr = quote { (1, 2) }.as_expr().unwrap(); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + } + } + + #[test] + fn test_expr_as_unary_op() { + comptime + { + assert(get_unary_op(quote { -x }).is_minus()); + assert(get_unary_op(quote { !x }).is_not()); + assert(get_unary_op(quote { &mut x }).is_mutable_reference()); + assert(get_unary_op(quote { *x }).is_dereference()); + } + } + + #[test] + fn test_automatically_unwraps_parenthesized_expression() { + comptime + { + let expr = quote { ((if 1 { 2 })) }.as_expr().unwrap(); + assert(expr.as_if().is_some()); + } + } + + comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { + let expr = quoted.as_expr().unwrap(); + let (op, _) = expr.as_unary_op().unwrap(); + op + } + + comptime fn get_binary_op(quoted: Quoted) -> BinaryOp { + let expr = quoted.as_expr().unwrap(); + let (_, op, _) = expr.as_binary_op().unwrap(); + op + } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/function_def.nr b/noir/noir-repo/noir_stdlib/src/meta/function_def.nr index 7ac8803e7e4..cbbbfb2f901 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/function_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/function_def.nr @@ -1,4 +1,14 @@ impl FunctionDefinition { + #[builtin(function_def_body)] + // docs:start:body + fn body(self) -> Expr {} + // docs:end:body + + #[builtin(function_def_has_named_attribute)] + // docs:start:has_named_attribute + fn has_named_attribute(self, name: Quoted) -> bool {} + // docs:end:has_named_attribute + #[builtin(function_def_name)] // docs:start:name fn name(self) -> Quoted {} @@ -16,7 +26,7 @@ impl FunctionDefinition { #[builtin(function_def_set_body)] // docs:start:set_body - fn set_body(self, body: Quoted) {} + fn set_body(self, body: Expr) {} // docs:end:set_body #[builtin(function_def_set_parameters)] diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index be1b12540c9..24398054467 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -7,6 +7,7 @@ mod trait_constraint; mod trait_def; mod trait_impl; mod typ; +mod typed_expr; mod quoted; mod unresolved_type; diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index 9c892c4d80b..f3060a1648b 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/op.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -26,6 +26,22 @@ impl UnaryOp { // docs:end:is_dereference self.op == 3 } + + // docs:start:unary_quoted + pub fn quoted(self) -> Quoted { + // docs:end:unary_quoted + if self.is_minus() { + quote { - } + } else if self.is_not() { + quote { ! } + } else if self.is_mutable_reference() { + quote { &mut } + } else if self.is_dereference() { + quote { * } + } else { + crate::mem::zeroed() + } + } } struct BinaryOp { @@ -128,5 +144,45 @@ impl BinaryOp { // docs:end:is_modulo self.op == 15 } + + // docs:start:binary_quoted + pub fn quoted(self) -> Quoted { + // docs:end:binary_quoted + if self.is_add() { + quote { + } + } else if self.is_subtract() { + quote { - } + } else if self.is_multiply() { + quote { * } + } else if self.is_divide() { + quote { / } + } else if self.is_equal() { + quote { == } + } else if self.is_not_equal() { + quote { != } + } else if self.is_less_than() { + quote { < } + } else if self.is_less_than_or_equal() { + quote { <= } + } else if self.is_greater_than() { + quote { > } + } else if self.is_greater_than_or_equal() { + quote { >= } + } else if self.is_and() { + quote { & } + } else if self.is_or() { + quote { | } + } else if self.is_xor() { + quote { ^ } + } else if self.is_shift_right() { + quote { >> } + } else if self.is_shift_left() { + quote { << } + } else if self.is_modulo() { + quote { % } + } else { + crate::mem::zeroed() + } + } } diff --git a/noir/noir-repo/noir_stdlib/src/meta/typ.nr b/noir/noir-repo/noir_stdlib/src/meta/typ.nr index a3f35b28e43..12dc91a4925 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/typ.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/typ.nr @@ -22,6 +22,11 @@ impl Type { fn as_slice(self) -> Option {} // docs:end:as_slice + #[builtin(type_as_str)] +// docs:start:as_str + fn as_str(self) -> Option {} + // docs:end:as_str + #[builtin(type_as_struct)] // docs:start:as_struct fn as_struct(self) -> Option<(StructDefinition, [Type])> {} diff --git a/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr b/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr new file mode 100644 index 00000000000..8daede97438 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/typed_expr.nr @@ -0,0 +1,8 @@ +use crate::option::Option; + +impl TypedExpr { + #[builtin(typed_expr_as_function_definition)] + // docs:start:as_function_definition + fn as_function_definition(self) -> Option {} + // docs:end:as_function_definition +} diff --git a/noir/noir-repo/noir_stdlib/src/option.nr b/noir/noir-repo/noir_stdlib/src/option.nr index 8d6d9ef970d..5b6b36679f8 100644 --- a/noir/noir-repo/noir_stdlib/src/option.nr +++ b/noir/noir-repo/noir_stdlib/src/option.nr @@ -116,7 +116,7 @@ impl Option { } /// If self is Some, return self. Otherwise, return `default()`. - pub fn or_else(self, default: fn[Env]() -> Self) -> Self { + pub fn or_else(self, default: fn[Env]() -> Self) -> Self { if self._is_some { self } else { default() } } diff --git a/noir/noir-repo/noir_stdlib/src/slice.nr b/noir/noir-repo/noir_stdlib/src/slice.nr index f9aa98a9ecd..66c69db65f0 100644 --- a/noir/noir-repo/noir_stdlib/src/slice.nr +++ b/noir/noir-repo/noir_stdlib/src/slice.nr @@ -1,6 +1,7 @@ use crate::append::Append; impl [T] { + /// Returns the length of the slice. #[builtin(array_len)] pub fn len(self) -> u32 {} @@ -37,8 +38,8 @@ impl [T] { #[builtin(slice_remove)] pub fn remove(self, index: u32) -> (Self, T) {} - // Append each element of the `other` slice to the end of `self`. - // This returns a new slice and leaves both input slices unchanged. + /// Append each element of the `other` slice to the end of `self`. + /// This returns a new slice and leaves both input slices unchanged. pub fn append(mut self, other: Self) -> Self { for elem in other { self = self.push_back(elem); diff --git a/noir/noir-repo/scripts/install_bb.sh b/noir/noir-repo/scripts/install_bb.sh index 91b9180f138..015091c07cf 100755 --- a/noir/noir-repo/scripts/install_bb.sh +++ b/noir/noir-repo/scripts/install_bb.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION="0.48.0" +VERSION="0.51.1" BBUP_PATH=~/.bb/bbup diff --git a/noir/noir-repo/test_programs/compile_failure/unspecified_generic/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/unspecified_generic/Nargo.toml new file mode 100644 index 00000000000..15b97018f2d --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/unspecified_generic/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unspecified_generic" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_failure/unspecified_generic/src/main.nr b/noir/noir-repo/test_programs/compile_failure/unspecified_generic/src/main.nr new file mode 100644 index 00000000000..f26d794d567 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/unspecified_generic/src/main.nr @@ -0,0 +1,5 @@ +fn foo() {} + +fn main() { + foo(); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr index 6cd13ab0e2f..ad8dff6c7b9 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/arithmetic_generics/src/main.nr @@ -7,6 +7,9 @@ fn main() { let _ = split_first([1, 2, 3]); let _ = push_multiple([1, 2, 3]); + + test_constant_folding::<10>(); + test_non_constant_folding::<10, 20>(); } fn split_first(array: [T; N]) -> (T, [T; N - 1]) { @@ -101,3 +104,31 @@ fn demo_proof() -> Equiv, (Equiv, (), W, () let p3: Equiv, (), W, ()> = add_equiv_r::(p3_sub); equiv_trans(equiv_trans(p1, p2), p3) } + +fn test_constant_folding() { + // N + C1 - C2 = N + (C1 - C2) + let _: W = W:: {}; + + // N - C1 + C2 = N - (C1 - C2) + let _: W = W:: {}; + + // N * C1 / C2 = N * (C1 / C2) + let _: W = W:: {}; + + // N / C1 * C2 = N / (C1 / C2) + let _: W = W:: {}; +} + +fn test_non_constant_folding() { + // N + M - M = N + let _: W = W:: {}; + + // N - M + M = N + let _: W = W:: {}; + + // N * M / M = N + let _: W = W:: {}; + + // N / M * M = N + let _: W = W:: {}; +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr index ce09ba86e49..8528bbce153 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_function_definition/src/main.nr @@ -32,6 +32,8 @@ comptime fn function_attr(f: FunctionDefinition) { // Check FunctionDefinition::name assert_eq(f.name(), quote { foo }); + + assert(f.has_named_attribute(quote { function_attr })); } #[mutate_add_one] @@ -49,7 +51,7 @@ comptime fn mutate_add_one(f: FunctionDefinition) { assert_eq(f.return_type(), type_of(0)); // fn add_one(x: Field) -> Field { x + 1 } - f.set_body(quote { x + 1 }); + f.set_body(quote { x + 1 }.as_expr().unwrap()); } fn main() { diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml new file mode 100644 index 00000000000..47c8654804d --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_keccak" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr new file mode 100644 index 00000000000..3cde32b6ba9 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_keccak/src/main.nr @@ -0,0 +1,31 @@ +// Tests a very simple program. +// +// The features being tested is keccak256 in brillig +fn main() { + comptime + { + let x = 0xbd; + let result = [ + 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, 0x4b, 0x3b, 0x1a, 0xbf + ]; + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = keccak256([x as u8], 1); + assert(digest == result); + //#1399: variable message size + let message_size = 4; + let hash_a = keccak256([1, 2, 3, 4], message_size); + let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); + } +} + +comptime fn keccak256(data: [u8; N], msg_len: u32) -> [u8; 32] { + std::hash::keccak256(data, msg_len) +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml index 8a976597c5b..c9a6f1e150e 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_impl/Nargo.toml @@ -4,4 +4,4 @@ type = "bin" authors = [""] compiler_version = ">=0.31.0" -[dependencies] +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr index 6d98d1d173b..0b15c5605b3 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -110,6 +110,11 @@ fn main() { assert(!struct_does_not_implement_some_trait.implements(some_trait_field)); let _trait_impl = struct_implements_some_trait.get_trait_impl(some_trait_i32).unwrap(); + + // Check Type::as_str + let str_type = quote { str<10> }.as_type(); + let constant = str_type.as_str().unwrap(); + assert_eq(constant.as_constant().unwrap(), 10); } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/Nargo.toml new file mode 100644 index 00000000000..02586f5e926 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "embedded_curve_add_simplification" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr new file mode 100644 index 00000000000..39992a6454b --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/embedded_curve_add_simplification/src/main.nr @@ -0,0 +1,11 @@ +use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul}; + +fn main() { + let zero = EmbeddedCurvePoint::point_at_infinity(); + let g1 = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false }; + + assert(g1 + zero == g1); + assert(g1 - g1 == zero); + assert(g1 - zero == g1); + assert(zero + zero == zero); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/inject_context_attribute/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/inject_context_attribute/Nargo.toml new file mode 100644 index 00000000000..10f9cb1f9e2 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/inject_context_attribute/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "inject_context_attribute" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/inject_context_attribute/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/inject_context_attribute/src/main.nr new file mode 100644 index 00000000000..26be3135133 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/inject_context_attribute/src/main.nr @@ -0,0 +1,63 @@ +struct Context { + value: Field, +} + +#[inject_context] +fn foo(x: Field) { + if true { + // 20 + 1 => 21 + bar(qux(x + 1) + zero()); + } else { + assert(false); + } +} + +#[inject_context] +fn bar(x: Field) { + let expected = _context.value; + assert_eq(x, expected); +} + +#[inject_context] +fn qux(x: Field) -> Field { + // 21 * 2 => 42 + x * 2 +} + +fn zero() -> Field { + 0 +} + +fn inject_context(f: FunctionDefinition) { + // Add a `_context: Context` parameter to the function + let parameters = f.parameters(); + let parameters = parameters.push_front((quote { _context }, quote { Context }.as_type())); + f.set_parameters(parameters); + + // Create a new body where every function call has `_context` added to the list of arguments. + let body = f.body().modify(|expr| mapping_function(expr, f)); + f.set_body(body); +} + +fn mapping_function(expr: Expr, f: FunctionDefinition) -> Option { + expr.as_function_call().and_then( + |func_call: (Expr, [Expr])| { + let (name, arguments) = func_call; + name.resolve(Option::some(f)).as_function_definition().and_then( + |function_definition: FunctionDefinition| { + if function_definition.has_named_attribute(quote { inject_context }) { + let arguments = arguments.push_front(quote { _context }.as_expr().unwrap()); + let arguments = arguments.map(|arg: Expr| arg.quoted()).join(quote { , }); + Option::some(quote { $name($arguments) }.as_expr().unwrap()) + } else { + Option::none() + } + } + )} + ) +} + +fn main() { + let context = Context { value: 42 }; + foo(context, 20); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr index 88b8dc57196..de58271cae6 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/method_call_regression/src/main.nr @@ -1,14 +1,14 @@ fn main() { - // s: Struct - let s = Struct { b: () }; + // s: Struct + let s = Struct { a: 0, b: () }; // Regression for #3089 s.foo(); } -struct Struct { b: B } +struct Struct { a: A, b: B } // Before the fix, this candidate is searched first, binding ? to `u8` permanently. -impl Struct { +impl Struct { fn foo(self) {} } @@ -18,6 +18,6 @@ impl Struct { // With the fix, the type of `s` correctly no longer changes until a // method is actually selected. So this candidate is now valid since // `Struct` unifies with `Struct` with `? = u32`. -impl Struct { +impl Struct { fn foo(self) {} } diff --git a/noir/noir-repo/test_programs/compile_success_empty/option/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/option/src/main.nr index c5f321256b1..d135b2d88b8 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/option/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/option/src/main.nr @@ -1,6 +1,6 @@ fn main() { let ten = 10; // giving this a name, to ensure that the Option functions work with closures - let none = Option::none(); + let none: Option = Option::none(); let some = Option::some(3); assert(none.is_none()); diff --git a/noir/noir-repo/test_programs/compile_success_empty/poseidon2_simplification/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/poseidon2_simplification/Nargo.toml new file mode 100644 index 00000000000..fbf2c11b220 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/poseidon2_simplification/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "poseidon2_simplification" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/poseidon2_simplification/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/poseidon2_simplification/src/main.nr new file mode 100644 index 00000000000..423dfcc4d3b --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/poseidon2_simplification/src/main.nr @@ -0,0 +1,7 @@ +use std::hash::poseidon2; + +fn main() { + let digest = poseidon2::Poseidon2::hash([0], 1); + let expected_digest = 0x2710144414c3a5f2354f4c08d52ed655b9fe253b4bf12cb9ad3de693d9b1db11; + assert_eq(digest, expected_digest); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/Nargo.toml new file mode 100644 index 00000000000..599f06ac3d2 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "schnorr_simplification" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr new file mode 100644 index 00000000000..e1095cd7fe2 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/schnorr_simplification/src/main.nr @@ -0,0 +1,78 @@ +use std::embedded_curve_ops; + +// Note: If main has any unsized types, then the verifier will never be able +// to figure out the circuit instance +fn main() { + let message = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let pub_key_x = 0x04b260954662e97f00cab9adb773a259097f7a274b83b113532bce27fa3fb96a; + let pub_key_y = 0x2fd51571db6c08666b0edfbfbc57d432068bccd0110a39b166ab243da0037197; + let signature = [ + 1, + 13, + 119, + 112, + 212, + 39, + 233, + 41, + 84, + 235, + 255, + 93, + 245, + 172, + 186, + 83, + 157, + 253, + 76, + 77, + 33, + 128, + 178, + 15, + 214, + 67, + 105, + 107, + 177, + 234, + 77, + 48, + 27, + 237, + 155, + 84, + 39, + 84, + 247, + 27, + 22, + 8, + 176, + 230, + 24, + 115, + 145, + 220, + 254, + 122, + 135, + 179, + 171, + 4, + 214, + 202, + 64, + 199, + 19, + 84, + 239, + 138, + 124, + 12 + ]; + + let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); + assert(valid_signature); +} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml new file mode 100644 index 00000000000..ce98d000bcb --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_regression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sha256_regression" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml new file mode 100644 index 00000000000..ea0a0f2e8a7 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_regression/Prover.toml @@ -0,0 +1,14 @@ +msg_just_over_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116] +msg_multiple_of_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99] +msg_just_under_block = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59] +msg_big_not_block_multiple = [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 115, 45, 97, 115, 99, 105, 105, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 72, 101, 108, 108, 111, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 56, 70, 56, 49, 57, 68, 51, 50, 45, 66, 54, 65, 67, 45, 52, 56, 57, 68, 45, 57, 55, 55, 70, 45, 52, 51, 56, 66, 66, 67, 52, 67, 65, 66, 50, 55, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 83, 97, 116, 44, 32, 50, 54, 32, 65, 117, 103, 32, 50, 48, 50, 51, 32, 49, 50, 58, 50, 53, 58, 50, 50, 32, 43, 48, 52, 48, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 54, 57, 51, 48, 51, 56, 51, 51, 55, 59, 32, 98, 104, 61, 55, 120, 81, 77, 68, 117, 111, 86, 86, 85, 52, 109, 48, 87, 48, 87, 82, 86, 83, 114, 86, 88, 77, 101, 71, 83, 73, 65, 83, 115, 110, 117, 99, 75, 57, 100, 74, 115, 114, 99, 43, 118, 85, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61] +msg_big_with_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +msg_big_no_padding = [48, 130, 1, 37, 2, 1, 0, 48, 11, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 48, 130, 1, 17, 48, 37, 2, 1, 1, 4, 32, 176, 223, 31, 133, 108, 84, 158, 102, 70, 11, 165, 175, 196, 12, 201, 130, 25, 131, 46, 125, 156, 194, 28, 23, 55, 133, 157, 164, 135, 136, 220, 78, 48, 37, 2, 1, 2, 4, 32, 190, 82, 180, 235, 222, 33, 79, 50, 152, 136, 142, 35, 116, 224, 6, 242, 156, 141, 128, 248, 10, 61, 98, 86, 248, 45, 207, 210, 90, 232, 175, 38, 48, 37, 2, 1, 3, 4, 32, 0, 194, 104, 108, 237, 246, 97, 230, 116, 198, 69, 110, 26, 87, 17, 89, 110, 199, 108, 250, 36, 21, 39, 87, 110, 102, 250, 213, 174, 131, 171, 174, 48, 37, 2, 1, 11, 4, 32, 136, 155, 87, 144, 111, 15, 152, 127, 85, 25, 154, 81, 20, 58, 51, 75, 193, 116, 234, 0, 60, 30, 29, 30, 183, 141, 72, 247, 255, 203, 100, 124, 48, 37, 2, 1, 12, 4, 32, 41, 234, 106, 78, 31, 11, 114, 137, 237, 17, 92, 71, 134, 47, 62, 78, 189, 233, 201, 214, 53, 4, 47, 189, 201, 133, 6, 121, 34, 131, 64, 142, 48, 37, 2, 1, 13, 4, 32, 91, 222, 210, 193, 62, 222, 104, 82, 36, 41, 138, 253, 70, 15, 148, 208, 156, 45, 105, 171, 241, 195, 185, 43, 217, 162, 146, 201, 222, 89, 238, 38, 48, 37, 2, 1, 14, 4, 32, 76, 123, 216, 13, 51, 227, 72, 245, 59, 193, 238, 166, 103, 49, 23, 164, 171, 188, 194, 197, 156, 187, 249, 28, 198, 95, 69, 15, 182, 56, 54, 38] +message_size = 297 + +# Results matched against ethers library +result_just_over_block = [91, 122, 146, 93, 52, 109, 133, 148, 171, 61, 156, 70, 189, 238, 153, 7, 222, 184, 94, 24, 65, 114, 192, 244, 207, 199, 87, 232, 192, 224, 171, 207] +result_multiple_of_block = [116, 90, 151, 31, 78, 22, 138, 180, 211, 189, 69, 76, 227, 200, 155, 29, 59, 123, 154, 60, 47, 153, 203, 129, 157, 251, 48, 2, 79, 11, 65, 47] +result_just_under_block = [143, 140, 76, 173, 222, 123, 102, 68, 70, 149, 207, 43, 39, 61, 34, 79, 216, 252, 213, 165, 74, 16, 110, 74, 29, 64, 138, 167, 30, 1, 9, 119] +result_big = [112, 144, 73, 182, 208, 98, 9, 238, 54, 229, 61, 145, 222, 17, 72, 62, 148, 222, 186, 55, 192, 82, 220, 35, 66, 47, 193, 200, 22, 38, 26, 186] +result_big_with_padding = [32, 85, 108, 174, 127, 112, 178, 182, 8, 43, 134, 123, 192, 211, 131, 66, 184, 240, 212, 181, 240, 180, 106, 195, 24, 117, 54, 129, 19, 10, 250, 53] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr new file mode 100644 index 00000000000..83049640ac4 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_regression/src/main.nr @@ -0,0 +1,39 @@ +// A bunch of different test cases for sha256_var in the stdlib +fn main( + msg_just_over_block: [u8; 68], + result_just_over_block: pub [u8; 32], + msg_multiple_of_block: [u8; 448], + result_multiple_of_block: pub [u8; 32], + // We want to make sure we are testing a message with a size >= 57 but < 64 + msg_just_under_block: [u8; 60], + result_just_under_block: pub [u8; 32], + msg_big_not_block_multiple: [u8; 472], + result_big: pub [u8; 32], + // This message is only 297 elements and we want to hash only a variable amount + msg_big_with_padding: [u8; 700], + // This is the same as `msg_big_with_padding` but with no padding + msg_big_no_padding: [u8; 297], + message_size: u64, + result_big_with_padding: pub [u8; 32] +) { + let hash = std::hash::sha256_var(msg_just_over_block, msg_just_over_block.len() as u64); + assert_eq(hash, result_just_over_block); + + let hash = std::hash::sha256_var(msg_multiple_of_block, msg_multiple_of_block.len() as u64); + assert_eq(hash, result_multiple_of_block); + + let hash = std::hash::sha256_var(msg_just_under_block, msg_just_under_block.len() as u64); + assert_eq(hash, result_just_under_block); + + let hash = std::hash::sha256_var( + msg_big_not_block_multiple, + msg_big_not_block_multiple.len() as u64 + ); + assert_eq(hash, result_big); + + let hash_padding = std::hash::sha256_var(msg_big_with_padding, message_size); + assert_eq(hash_padding, result_big_with_padding); + + let hash_no_padding = std::hash::sha256_var(msg_big_no_padding, message_size); + assert_eq(hash_no_padding, result_big_with_padding); +} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml new file mode 100644 index 00000000000..3e141ee5d5f --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sha256_var_size_regression" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml new file mode 100644 index 00000000000..df632a42858 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/Prover.toml @@ -0,0 +1,3 @@ +enable = [true, false] +foo = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] +toggle = false diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr new file mode 100644 index 00000000000..de1c2b23c5f --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_size_regression/src/main.nr @@ -0,0 +1,17 @@ +global NUM_HASHES = 2; + +fn main(foo: [u8; 95], toggle: bool, enable: [bool; NUM_HASHES]) { + let mut result = [[0; 32]; NUM_HASHES]; + let mut const_result = [[0; 32]; NUM_HASHES]; + let size: Field = 93 + toggle as Field * 2; + for i in 0..NUM_HASHES { + if enable[i] { + result[i] = std::sha256::sha256_var(foo, size as u64); + const_result[i] = std::sha256::sha256_var(foo, 93); + } + } + + for i in 0..NUM_HASHES { + assert_eq(result[i], const_result[i]); + } +} diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml new file mode 100644 index 00000000000..e8f3e6bbe64 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "sha256_var_witness_const_regression" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml new file mode 100644 index 00000000000..7b91051c1a0 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/Prover.toml @@ -0,0 +1,2 @@ +input = [0, 0] +toggle = false \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr new file mode 100644 index 00000000000..97c4435d41d --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/sha256_var_witness_const_regression/src/main.nr @@ -0,0 +1,9 @@ +fn main(input: [u8; 2], toggle: bool) { + let size: Field = 1 + toggle as Field; + assert(!toggle); + + let variable_sha = std::sha256::sha256_var(input, size as u64); + let constant_sha = std::sha256::sha256_var(input, 1); + + assert_eq(variable_sha, constant_sha); +} diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_expr/Nargo.toml b/noir/noir-repo/test_programs/noir_test_success/comptime_expr/Nargo.toml index 5abcddf244c..a40da9c5f28 100644 --- a/noir/noir-repo/test_programs/noir_test_success/comptime_expr/Nargo.toml +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_expr/Nargo.toml @@ -4,4 +4,4 @@ type = "bin" authors = [""] compiler_version = ">=0.31.0" -[dependencies] +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/noir_test_success/comptime_expr/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/comptime_expr/src/main.nr index 329e97dc9d9..1488783c72c 100644 --- a/noir/noir-repo/test_programs/noir_test_success/comptime_expr/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/comptime_expr/src/main.nr @@ -15,6 +15,54 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_array() { + comptime + { + let expr = quote { [1, 2, 4] }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let elems = expr.as_array().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (2, false)); + assert_eq(elems[1].as_integer().unwrap(), (4, false)); + assert_eq(elems[2].as_integer().unwrap(), (8, false)); + } + } + + #[test] + fn test_expr_as_assert() { + comptime + { + let expr = quote { assert(true) }.as_expr().unwrap(); + let (predicate, msg) = expr.as_assert().unwrap(); + assert_eq(predicate.as_bool().unwrap(), true); + assert(msg.is_none()); + + let expr = quote { assert(false, "oops") }.as_expr().unwrap(); + let (predicate, msg) = expr.as_assert().unwrap(); + assert_eq(predicate.as_bool().unwrap(), false); + assert(msg.is_some()); + } + } + + #[test] + fn test_expr_mutate_for_assert() { + comptime + { + let expr = quote { assert(1) }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (predicate, msg) = expr.as_assert().unwrap(); + assert_eq(predicate.as_integer().unwrap(), (2, false)); + assert(msg.is_none()); + + let expr = quote { assert(1, 2) }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (predicate, msg) = expr.as_assert().unwrap(); + assert_eq(predicate.as_integer().unwrap(), (2, false)); + assert_eq(msg.unwrap().as_integer().unwrap(), (4, false)); + } + } + #[test] fn test_expr_as_assign() { comptime @@ -26,6 +74,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_assign() { + comptime + { + let expr = quote { { a = 1; } }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let exprs = expr.as_block().unwrap(); + let (_lhs, rhs) = exprs[0].as_assign().unwrap(); + assert_eq(rhs.as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_expr_as_block() { comptime @@ -43,6 +103,24 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_block() { + comptime + { + let expr = quote { { 1; 4; 23 } }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let exprs = expr.as_block().unwrap(); + assert_eq(exprs.len(), 3); + assert_eq(exprs[0].as_integer().unwrap(), (2, false)); + assert_eq(exprs[1].as_integer().unwrap(), (8, false)); + assert_eq(exprs[2].as_integer().unwrap(), (46, false)); + + assert(exprs[0].has_semicolon()); + assert(exprs[1].has_semicolon()); + assert(!exprs[2].has_semicolon()); + } + } + #[test] fn test_expr_as_method_call() { comptime @@ -61,6 +139,25 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_method_call() { + comptime + { + let expr = quote { foo.bar(3, 4) }.as_expr().unwrap(); + let expr = expr.modify(times_two); + + let (_object, name, generics, arguments) = expr.as_method_call().unwrap(); + + assert_eq(name, quote { bar }); + + assert_eq(generics.len(), 0); + + assert_eq(arguments.len(), 2); + assert_eq(arguments[0].as_integer().unwrap(), (6, false)); + assert_eq(arguments[1].as_integer().unwrap(), (8, false)); + } + } + #[test] fn test_expr_as_integer() { comptime @@ -73,6 +170,17 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_integer() { + comptime + { + let expr = quote { 1 }.as_expr().unwrap(); + let expr = expr.modify(times_two); + + assert_eq((2, false), expr.as_integer().unwrap()); + } + } + #[test] fn test_expr_as_binary_op() { comptime @@ -96,6 +204,20 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_binary_op() { + comptime + { + let expr = quote { 3 + 4 }.as_expr().unwrap(); + let expr = expr.modify(times_two); + + let (lhs, op, rhs) = expr.as_binary_op().unwrap(); + assert_eq(lhs.as_integer().unwrap(), (6, false)); + assert(op.is_add()); + assert_eq(rhs.as_integer().unwrap(), (8, false)); + } + } + #[test] fn test_expr_as_bool() { comptime @@ -119,6 +241,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_cast() { + comptime + { + let expr = quote { 1 as Field }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (expr, typ) = expr.as_cast().unwrap(); + assert_eq(expr.as_integer().unwrap(), (2, false)); + assert(typ.is_field()); + } + } + #[test] fn test_expr_as_comptime() { comptime @@ -129,6 +263,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_comptime() { + comptime + { + let expr = quote { comptime { 1; 4; 23 } }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let exprs = expr.as_comptime().unwrap(); + assert_eq(exprs.len(), 3); + assert_eq(exprs[0].as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_expr_as_comptime_as_statement() { comptime @@ -157,6 +303,18 @@ mod tests { } // docs:end:as_expr_example + #[test] + fn test_expr_mutate_for_function_call() { + comptime + { + let expr = quote { foo(42) }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (_function, args) = expr.as_function_call().unwrap(); + assert_eq(args.len(), 1); + assert_eq(args[0].as_integer().unwrap(), (84, false)); + } + } + #[test] fn test_expr_as_if() { comptime @@ -171,6 +329,29 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_if() { + comptime + { + let expr = quote { if 1 { 2 } }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (condition, consequence, alternative) = expr.as_if().unwrap(); + assert_eq(condition.as_integer().unwrap(), (2, false)); + let consequence = consequence.as_block().unwrap()[0].as_block().unwrap()[0]; + assert_eq(consequence.as_integer().unwrap(), (4, false)); + assert(alternative.is_none()); + + let expr = quote { if 1 { 2 } else { 3 } }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (condition, consequence, alternative) = expr.as_if().unwrap(); + assert_eq(condition.as_integer().unwrap(), (2, false)); + let consequence = consequence.as_block().unwrap()[0].as_block().unwrap()[0]; + assert_eq(consequence.as_integer().unwrap(), (4, false)); + let alternative = alternative.unwrap().as_block().unwrap()[0].as_block().unwrap()[0]; + assert_eq(alternative.as_integer().unwrap(), (6, false)); + } + } + #[test] fn test_expr_as_index() { comptime @@ -180,6 +361,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_index() { + comptime + { + let expr = quote { 1[2] }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (object, index) = expr.as_index().unwrap(); + assert_eq(object.as_integer().unwrap(), (2, false)); + assert_eq(index.as_integer().unwrap(), (4, false)); + } + } + #[test] fn test_expr_as_member_access() { comptime @@ -190,6 +383,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_member_access() { + comptime + { + let expr = quote { 1.bar }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (expr, name) = expr.as_member_access().unwrap(); + assert_eq(name, quote { bar }); + assert_eq(expr.as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_expr_as_member_access_with_an_lvalue() { comptime @@ -213,6 +418,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_repeated_element_array() { + comptime + { + let expr = quote { [1; 3] }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (expr, length) = expr.as_repeated_element_array().unwrap(); + assert_eq(expr.as_integer().unwrap(), (2, false)); + assert_eq(length.as_integer().unwrap(), (6, false)); + } + } + #[test] fn test_expr_as_repeated_element_slice() { comptime @@ -224,6 +441,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_repeated_element_slice() { + comptime + { + let expr = quote { &[1; 3] }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (expr, length) = expr.as_repeated_element_slice().unwrap(); + assert_eq(expr.as_integer().unwrap(), (2, false)); + assert_eq(length.as_integer().unwrap(), (6, false)); + } + } + #[test] fn test_expr_as_slice() { comptime @@ -237,6 +466,20 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_slice() { + comptime + { + let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let elems = expr.as_slice().unwrap(); + assert_eq(elems.len(), 3); + assert_eq(elems[0].as_integer().unwrap(), (2, false)); + assert_eq(elems[1].as_integer().unwrap(), (6, false)); + assert_eq(elems[2].as_integer().unwrap(), (10, false)); + } + } + #[test] fn test_expr_as_tuple() { comptime @@ -247,6 +490,19 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_tuple() { + comptime + { + let expr = quote { (1, 2) }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let tuple_exprs = expr.as_tuple().unwrap(); + assert_eq(tuple_exprs.len(), 2); + assert_eq(tuple_exprs[0].as_integer().unwrap(), (2, false)); + assert_eq(tuple_exprs[1].as_integer().unwrap(), (4, false)); + } + } + #[test] fn test_expr_as_unary_op() { comptime @@ -258,6 +514,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_unary_op() { + comptime + { + let expr = quote { -(1) }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (op, expr) = expr.as_unary_op().unwrap(); + assert(op.is_minus()); + assert_eq(expr.as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_expr_as_unsafe() { comptime @@ -268,6 +536,18 @@ mod tests { } } + #[test] + fn test_expr_mutate_for_unsafe() { + comptime + { + let expr = quote { unsafe { 1; 4; 23 } }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let exprs = expr.as_unsafe().unwrap(); + assert_eq(exprs.len(), 3); + assert_eq(exprs[0].as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_expr_is_break() { comptime @@ -297,6 +577,17 @@ mod tests { } } + #[test] + fn test_resolve_to_function_definition() { + comptime + { + let expr = quote { times_two }.as_expr().unwrap(); + let func = expr.resolve(Option::none()).as_function_definition().unwrap(); + assert_eq(func.name(), quote { times_two }); + assert_eq(func.parameters().len(), 1); + } + } + comptime fn get_unary_op(quoted: Quoted) -> UnaryOp { let expr = quoted.as_expr().unwrap(); let (op, _) = expr.as_unary_op().unwrap(); @@ -308,6 +599,16 @@ mod tests { let (_, op, _) = expr.as_binary_op().unwrap(); op } + + comptime fn times_two(expr: Expr) -> Option { + expr.as_integer().and_then( + |integer: (Field, bool)| { + let (value, _) = integer; + let value = value * 2; + quote { $value }.as_expr() + } + ) + } } fn main() {} diff --git a/noir/noir-repo/test_programs/noir_test_success/embedded_curve_ops/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/embedded_curve_ops/src/main.nr index 0c2c333fa62..760df58c34a 100644 --- a/noir/noir-repo/test_programs/noir_test_success/embedded_curve_ops/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/embedded_curve_ops/src/main.nr @@ -4,7 +4,6 @@ use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_sca fn test_infinite_point() { let zero = EmbeddedCurvePoint::point_at_infinity(); - let zero = EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true }; let g1 = EmbeddedCurvePoint { x: 1, y: 17631683881184975370165255887551781615748388533673675138860, is_infinite: false }; let g2 = g1 + g1; diff --git a/noir/noir-repo/test_programs/rebuild.sh b/noir/noir-repo/test_programs/rebuild.sh index 6507660f408..73d1fb012ec 100755 --- a/noir/noir-repo/test_programs/rebuild.sh +++ b/noir/noir-repo/test_programs/rebuild.sh @@ -79,7 +79,7 @@ fi parallel -j7 process_dir {} "$current_dir" ::: ${dirs_to_process[@]} # Process directories in parallel -parallel -j0 process_dir {} "$current_dir" ::: "${dirs_to_process[@]}" +parallel -j7 process_dir {} "$current_dir" ::: ${dirs_to_process[@]} # Check rebuild.log for failures if [ -f "$current_dir/rebuild.log" ]; then diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 353a6ade904..c15895d801f 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -31,7 +31,7 @@ async-lsp = { workspace = true, features = ["omni-trait"] } serde_with = "3.2.0" thiserror.workspace = true fm.workspace = true -rayon = "1.8.0" +rayon.workspace = true fxhash.workspace = true convert_case = "0.6.0" diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 4d2186badc3..8b030c9e0aa 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; -use noirc_driver::{check_crate, file_manager_with_stdlib}; +use noirc_driver::{check_crate, file_manager_with_stdlib, CheckOptions}; use noirc_errors::{DiagnosticKind, FileDiagnostic}; use crate::types::{ @@ -132,7 +132,11 @@ pub(crate) fn process_workspace_for_noir_document( let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); - let file_diagnostics = match check_crate(&mut context, crate_id, &Default::default()) { + let options = CheckOptions { + error_on_unused_imports: package.error_on_unused_imports(), + ..Default::default() + }; + let file_diagnostics = match check_crate(&mut context, crate_id, &options) { Ok(((), warnings)) => warnings, Err(errors_and_warnings) => errors_and_warnings, }; diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index c61f92795ad..cb27a9684a1 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -16,11 +16,10 @@ use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ AsTraitPath, BlockExpression, CallExpression, ConstructorExpression, Expression, - ExpressionKind, ForLoopStatement, Ident, IfExpression, ItemVisibility, LValue, Lambda, - LetStatement, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, - NoirTraitImpl, Path, PathKind, PathSegment, Pattern, Statement, StatementKind, TraitItem, - TypeImpl, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, - UseTree, UseTreeKind, + ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, ItemVisibility, + Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, NoirFunction, + NoirStruct, NoirTraitImpl, Path, PathKind, PathSegment, Pattern, Statement, TypeImpl, + UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UseTree, UseTreeKind, Visitor, }, graph::{CrateId, Dependency}, hir::{ @@ -33,7 +32,7 @@ use noirc_frontend::{ hir_def::traits::Trait, macros_api::{ModuleDefId, NodeInterner}, node_interner::ReferenceId, - parser::{Item, ItemKind}, + parser::{Item, ItemKind, ParsedSubModule}, ParsedModule, StructType, Type, }; use sort_text::underscore_sort_text; @@ -48,7 +47,6 @@ mod completion_items; mod kinds; mod sort_text; mod tests; -mod traversal; pub(crate) fn on_completion_request( state: &mut LspState, @@ -163,7 +161,7 @@ impl<'a> NodeFinder<'a> { } fn find(&mut self, parsed_module: &ParsedModule) -> Option { - self.find_in_parsed_module(parsed_module); + parsed_module.accept(self); if self.completion_items.is_empty() { None @@ -181,1156 +179,1058 @@ impl<'a> NodeFinder<'a> { } } - fn find_in_item(&mut self, item: &Item) { - if let ItemKind::Import(..) = &item.kind { - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { - self.auto_import_line = (lsp_location.range.end.line + 1) as usize; - } - } - - if !self.includes_span(item.span) { + fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { + let location = + Location::new(constructor_expression.type_name.last_ident().span(), self.file); + let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { return; - } - - match &item.kind { - ItemKind::Import(use_tree) => { - let mut prefixes = Vec::new(); - self.find_in_use_tree(use_tree, &mut prefixes); - } - ItemKind::Submodules(parsed_sub_module) => { - // Switch `self.module_id` to the submodule - let previous_module_id = self.module_id; - - let def_map = &self.def_maps[&self.module_id.krate]; - let Some(module_data) = def_map.modules().get(self.module_id.local_id.0) else { - return; - }; - if let Some(child_module) = module_data.children.get(&parsed_sub_module.name) { - self.module_id = - ModuleId { krate: self.module_id.krate, local_id: *child_module }; - } - - let old_auto_import_line = self.auto_import_line; - self.nesting += 1; - - if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { - self.auto_import_line = (lsp_location.range.start.line + 1) as usize; - } + }; - self.find_in_parsed_module(&parsed_sub_module.contents); + let struct_type = self.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); - // Restore the old module before continuing - self.module_id = previous_module_id; - self.nesting -= 1; - self.auto_import_line = old_auto_import_line; - } - ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), - ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), - ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), - ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement, false), - ItemKind::TypeAlias(noir_type_alias) => self.find_in_noir_type_alias(noir_type_alias), - ItemKind::Struct(noir_struct) => self.find_in_noir_struct(noir_struct), - ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), - ItemKind::ModuleDecl(_) => (), + // First get all of the struct's fields + let mut fields = HashMap::new(); + let fields_as_written = struct_type.get_fields_as_written(); + for (field, typ) in &fields_as_written { + fields.insert(field, typ); } - } - - fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { - let old_type_parameters = self.type_parameters.clone(); - self.collect_type_parameters_in_generics(&noir_function.def.generics); - for param in &noir_function.def.parameters { - self.find_in_unresolved_type(¶m.typ); + // Remove the ones that already exists in the constructor + for (field, _) in &constructor_expression.fields { + fields.remove(&field.0.contents); } - self.find_in_function_return_type(&noir_function.def.return_type); - - self.local_variables.clear(); - for param in &noir_function.def.parameters { - self.collect_local_variables(¶m.pattern); + for (field, typ) in fields { + self.completion_items.push(struct_field_completion_item(field, typ)); } + } - self.find_in_block_expression(&noir_function.def.body); - - self.type_parameters = old_type_parameters; + fn find_in_path(&mut self, path: &Path, requested_items: RequestedItems) { + self.find_in_path_impl(path, requested_items, false); } - fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { - self.find_in_path(&noir_trait_impl.trait_name, RequestedItems::OnlyTypes); - self.find_in_unresolved_type(&noir_trait_impl.object_type); + fn find_in_path_impl( + &mut self, + path: &Path, + requested_items: RequestedItems, + mut in_the_middle: bool, + ) { + if !self.includes_span(path.span) { + return; + } - self.type_parameters.clear(); - self.collect_type_parameters_in_generics(&noir_trait_impl.impl_generics); + let after_colons = self.byte == Some(b':'); - for item in &noir_trait_impl.items { - self.find_in_trait_impl_item(item); - } + let mut idents: Vec = Vec::new(); - self.type_parameters.clear(); - } + // Find in which ident we are in, and in which part of it + // (it could be that we are completting in the middle of an ident) + for segment in &path.segments { + let ident = &segment.ident; - fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { - self.find_in_unresolved_type(&type_impl.object_type); + // Check if we are at the end of the ident + if self.byte_index == ident.span().end() as usize { + idents.push(ident.clone()); + break; + } - self.type_parameters.clear(); - self.collect_type_parameters_in_generics(&type_impl.generics); + // Check if we are in the middle of an ident + if self.includes_span(ident.span()) { + // If so, take the substring and push that as the list of idents + // we'll do autocompletion for + let offset = self.byte_index - ident.span().start() as usize; + let substring = ident.0.contents[0..offset].to_string(); + let ident = Ident::new( + substring, + Span::from(ident.span().start()..ident.span().start() + offset as u32), + ); + idents.push(ident); + in_the_middle = true; + break; + } - for (method, span) in &type_impl.methods { - self.find_in_noir_function(method); + idents.push(ident.clone()); - // Optimization: stop looking in functions past the completion cursor - if span.end() as usize > self.byte_index { + // Stop if the cursor is right after this ident and '::' + if after_colons && self.byte_index == ident.span().end() as usize + 2 { break; } } - self.type_parameters.clear(); - } + if idents.len() < path.segments.len() { + in_the_middle = true; + } - fn find_in_noir_struct(&mut self, noir_struct: &NoirStruct) { - self.type_parameters.clear(); - self.collect_type_parameters_in_generics(&noir_struct.generics); + let prefix; + let at_root; - for (_name, unresolved_type) in &noir_struct.fields { - self.find_in_unresolved_type(unresolved_type); + if after_colons { + prefix = String::new(); + at_root = false; + } else { + prefix = idents.pop().unwrap().to_string(); + at_root = idents.is_empty(); } - self.type_parameters.clear(); - } + let prefix = prefix.to_case(Case::Snake); - fn find_in_trait_item(&mut self, trait_item: &TraitItem) { - match trait_item { - TraitItem::Function { - name: _, - generics, - parameters, - return_type, - where_clause, - body, - } => { - let old_type_parameters = self.type_parameters.clone(); - self.collect_type_parameters_in_generics(generics); - - for (_name, unresolved_type) in parameters { - self.find_in_unresolved_type(unresolved_type); - } + let is_single_segment = !after_colons && idents.is_empty() && path.kind == PathKind::Plain; + let module_id; - self.find_in_function_return_type(return_type); + let module_completion_kind = if after_colons || !idents.is_empty() { + ModuleCompletionKind::DirectChildren + } else { + ModuleCompletionKind::AllVisibleItems + }; - for unresolved_trait_constraint in where_clause { - self.find_in_unresolved_type(&unresolved_trait_constraint.typ); - } + // When completing in the middle of an ident, we don't want to complete + // with function parameters because there might already be function parameters, + // and in the middle of a path it leads to code that won't compile + let function_completion_kind = if in_the_middle { + FunctionCompletionKind::Name + } else { + FunctionCompletionKind::NameAndParameters + }; - if let Some(body) = body { - self.local_variables.clear(); - for (name, _) in parameters { - self.local_variables.insert(name.to_string(), name.span()); - } - self.find_in_block_expression(body); - }; + if idents.is_empty() { + module_id = self.module_id; + } else { + let Some(module_def_id) = self.resolve_path(idents) else { + return; + }; - self.type_parameters = old_type_parameters; + match module_def_id { + ModuleDefId::ModuleId(id) => module_id = id, + ModuleDefId::TypeId(struct_id) => { + let struct_type = self.interner.get_struct(struct_id); + self.complete_type_methods( + &Type::Struct(struct_type, vec![]), + &prefix, + FunctionKind::Any, + function_completion_kind, + ); + return; + } + ModuleDefId::FunctionId(_) => { + // There's nothing inside a function + return; + } + ModuleDefId::TypeAliasId(type_alias_id) => { + let type_alias = self.interner.get_type_alias(type_alias_id); + let type_alias = type_alias.borrow(); + self.complete_type_methods( + &type_alias.typ, + &prefix, + FunctionKind::Any, + function_completion_kind, + ); + return; + } + ModuleDefId::TraitId(trait_id) => { + let trait_ = self.interner.get_trait(trait_id); + self.complete_trait_methods( + trait_, + &prefix, + FunctionKind::Any, + function_completion_kind, + ); + return; + } + ModuleDefId::GlobalId(_) => return, } - TraitItem::Constant { name: _, typ, default_value } => { - self.find_in_unresolved_type(typ); + } + + self.complete_in_module( + module_id, + &prefix, + path.kind, + at_root, + module_completion_kind, + function_completion_kind, + requested_items, + ); - if let Some(default_value) = default_value { - self.find_in_expression(default_value); + if is_single_segment { + match requested_items { + RequestedItems::AnyItems => { + self.local_variables_completion(&prefix); + self.builtin_functions_completion(&prefix, function_completion_kind); + self.builtin_values_completion(&prefix); + } + RequestedItems::OnlyTypes => { + self.builtin_types_completion(&prefix); + self.type_parameters_completion(&prefix); } } - TraitItem::Type { name: _ } => (), + self.complete_auto_imports(&prefix, requested_items, function_completion_kind); } } - pub(super) fn find_in_call_expression(&mut self, call_expression: &CallExpression) { - // Check if it's this case: - // - // foo::b>|<(...) - // - // In this case we want to suggest items in foo but if they are functions - // we don't want to insert arguments, because they are already there (even if - // they could be wrong) just because inserting them would lead to broken code. - if let ExpressionKind::Variable(path) = &call_expression.func.kind { - if self.includes_span(path.span) { - self.find_in_path_impl(path, RequestedItems::AnyItems, true); - return; + fn local_variables_completion(&mut self, prefix: &str) { + for (name, span) in &self.local_variables { + if name_matches(name, prefix) { + let location = Location::new(*span, self.file); + let description = if let Some(ReferenceId::Local(definition_id)) = + self.interner.reference_at_location(location) + { + let typ = self.interner.definition_type(definition_id); + Some(typ.to_string()) + } else { + None + }; + + self.completion_items.push(simple_completion_item( + name, + CompletionItemKind::VARIABLE, + description, + )); } } + } - // Check if it's this case: - // - // foo.>|<(...) - // - // "foo." is actually broken, but it's parsed as "foo", so this is seen - // as "foo(...)" but if we are at a dot right after "foo" it means it's - // the above case and we want to suggest methods of foo's type. - let after_dot = self.byte == Some(b'.'); - if after_dot && call_expression.func.span.end() as usize == self.byte_index - 1 { - let location = Location::new(call_expression.func.span, self.file); - if let Some(typ) = self.interner.type_at_location(location) { - let typ = typ.follow_bindings(); - let prefix = ""; - self.complete_type_fields_and_methods(&typ, prefix, FunctionCompletionKind::Name); - return; + fn type_parameters_completion(&mut self, prefix: &str) { + for name in &self.type_parameters { + if name_matches(name, prefix) { + self.completion_items.push(simple_completion_item( + name, + CompletionItemKind::TYPE_PARAMETER, + None, + )); } } - - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); } - pub(super) fn find_in_method_call_expression( - &mut self, - method_call_expression: &MethodCallExpression, - ) { - // Check if it's this case: - // - // foo.b>|<(...) - // - // In this case we want to suggest items in foo but if they are functions - // we don't want to insert arguments, because they are already there (even if - // they could be wrong) just because inserting them would lead to broken code. - if self.includes_span(method_call_expression.method_name.span()) { - let location = Location::new(method_call_expression.object.span, self.file); - if let Some(typ) = self.interner.type_at_location(location) { - let typ = typ.follow_bindings(); - let prefix = method_call_expression.method_name.to_string(); - let offset = - self.byte_index - method_call_expression.method_name.span().start() as usize; - let prefix = prefix[0..offset].to_string(); - self.complete_type_fields_and_methods(&typ, &prefix, FunctionCompletionKind::Name); - return; - } - } - - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - } - - fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { - let old_local_variables = self.local_variables.clone(); - for statement in &block_expression.statements { - self.find_in_statement(statement); - - // Optimization: stop looking in statements past the completion cursor - if statement.span.end() as usize > self.byte_index { - break; - } - } - self.local_variables = old_local_variables; - } - - fn find_in_statement(&mut self, statement: &Statement) { - match &statement.kind { - StatementKind::Let(let_statement) => { - self.find_in_let_statement(let_statement, true); - } - StatementKind::Constrain(constrain_statement) => { - self.find_in_constrain_statement(constrain_statement); - } - StatementKind::Expression(expression) => { - self.find_in_expression(expression); - } - StatementKind::Assign(assign_statement) => { - self.find_in_assign_statement(assign_statement); - } - StatementKind::For(for_loop_statement) => { - self.find_in_for_loop_statement(for_loop_statement); - } - StatementKind::Comptime(statement) => { - // When entering a comptime block, regular local variables shouldn't be offered anymore - let old_local_variables = self.local_variables.clone(); - self.local_variables.clear(); - - self.find_in_statement(statement); - - self.local_variables = old_local_variables; + fn find_in_use_tree(&mut self, use_tree: &UseTree, prefixes: &mut Vec) { + match &use_tree.kind { + UseTreeKind::Path(ident, alias) => { + prefixes.push(use_tree.prefix.clone()); + self.find_in_use_tree_path(prefixes, ident, alias); + prefixes.pop(); } - StatementKind::Semi(expression) => { - self.find_in_expression(expression); + UseTreeKind::List(use_trees) => { + prefixes.push(use_tree.prefix.clone()); + for use_tree in use_trees { + self.find_in_use_tree(use_tree, prefixes); + } + prefixes.pop(); } - StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), } } - fn find_in_let_statement( + fn find_in_use_tree_path( &mut self, - let_statement: &LetStatement, - collect_local_variables: bool, + prefixes: &Vec, + ident: &Ident, + alias: &Option, ) { - self.find_in_unresolved_type(&let_statement.r#type); - self.find_in_expression(&let_statement.expression); - - if collect_local_variables { - self.collect_local_variables(&let_statement.pattern); + if let Some(_alias) = alias { + // Won't handle completion if there's an alias (for now) + return; } - } - fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { - let old_local_variables = self.local_variables.clone(); - let ident = &for_loop_statement.identifier; - self.local_variables.insert(ident.to_string(), ident.span()); + let after_colons = self.byte == Some(b':'); + let at_ident_end = self.byte_index == ident.span().end() as usize; + let at_ident_colons_end = + after_colons && self.byte_index - 2 == ident.span().end() as usize; - self.find_in_for_range(&for_loop_statement.range); - self.find_in_expression(&for_loop_statement.block); + if !(at_ident_end || at_ident_colons_end) { + return; + } - self.local_variables = old_local_variables; - } + let path_kind = prefixes[0].kind; - fn find_in_lvalue(&mut self, lvalue: &LValue) { - match lvalue { - LValue::Ident(ident) => { - if self.byte == Some(b'.') && ident.span().end() as usize == self.byte_index - 1 { - let location = Location::new(ident.span(), self.file); - if let Some(ReferenceId::Local(definition_id)) = - self.interner.find_referenced(location) - { - let typ = self.interner.definition_type(definition_id); - let prefix = ""; - self.complete_type_fields_and_methods( - &typ, - prefix, - FunctionCompletionKind::NameAndParameters, - ); - } - } - } - LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), - LValue::Index { array, index, span: _ } => { - self.find_in_lvalue(array); - self.find_in_expression(index); + let mut segments: Vec = Vec::new(); + for prefix in prefixes { + for segment in &prefix.segments { + segments.push(segment.ident.clone()); } - LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), } - } - - fn find_in_expression(&mut self, expression: &Expression) { - match &expression.kind { - ExpressionKind::Literal(literal) => self.find_in_literal(literal), - ExpressionKind::Block(block_expression) => { - self.find_in_block_expression(block_expression); - } - ExpressionKind::Prefix(prefix_expression) => { - self.find_in_expression(&prefix_expression.rhs); - } - ExpressionKind::Index(index_expression) => { - self.find_in_index_expression(index_expression); - } - ExpressionKind::Call(call_expression) => { - self.find_in_call_expression(call_expression); - } - ExpressionKind::MethodCall(method_call_expression) => { - self.find_in_method_call_expression(method_call_expression); - } - ExpressionKind::Constructor(constructor_expression) => { - self.find_in_constructor_expression(constructor_expression); - } - ExpressionKind::MemberAccess(member_access_expression) => { - self.find_in_member_access_expression(member_access_expression); - } - ExpressionKind::Cast(cast_expression) => { - self.find_in_cast_expression(cast_expression); - } - ExpressionKind::Infix(infix_expression) => { - self.find_in_infix_expression(infix_expression); - } - ExpressionKind::If(if_expression) => { - self.find_in_if_expression(if_expression); - } - ExpressionKind::Variable(path) => { - self.find_in_path(path, RequestedItems::AnyItems); - } - ExpressionKind::Tuple(expressions) => { - self.find_in_expressions(expressions); - } - ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - ExpressionKind::Parenthesized(expression) => { - self.find_in_expression(expression); - } - ExpressionKind::Unquote(expression) => { - self.find_in_expression(expression); - } - ExpressionKind::Comptime(block_expression, _) => { - // When entering a comptime block, regular local variables shouldn't be offered anymore - let old_local_variables = self.local_variables.clone(); - self.local_variables.clear(); - self.find_in_block_expression(block_expression); + let module_completion_kind = ModuleCompletionKind::DirectChildren; + let function_completion_kind = FunctionCompletionKind::Name; + let requested_items = RequestedItems::AnyItems; - self.local_variables = old_local_variables; - } - ExpressionKind::Unsafe(block_expression, _) => { - self.find_in_block_expression(block_expression); - } - ExpressionKind::AsTraitPath(as_trait_path) => { - self.find_in_as_trait_path(as_trait_path); - } - ExpressionKind::Quote(_) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), - } + if after_colons { + // We are right after "::" + segments.push(ident.clone()); - // "foo." (no identifier afterwards) is parsed as the expression on the left hand-side of the dot. - // Here we check if there's a dot at the completion position, and if the expression - // ends right before the dot. If so, it means we want to complete the expression's type fields and methods. - // We only do this after visiting nested expressions, because in an expression like `foo & bar.` we want - // to complete for `bar`, not for `foo & bar`. - if self.completion_items.is_empty() - && self.byte == Some(b'.') - && expression.span.end() as usize == self.byte_index - 1 - { - let location = Location::new(expression.span, self.file); - if let Some(typ) = self.interner.type_at_location(location) { - let typ = typ.follow_bindings(); + if let Some(module_id) = self.resolve_module(segments) { let prefix = ""; - self.complete_type_fields_and_methods( - &typ, + let at_root = false; + self.complete_in_module( + module_id, prefix, - FunctionCompletionKind::NameAndParameters, + path_kind, + at_root, + module_completion_kind, + function_completion_kind, + requested_items, + ); + }; + } else { + // We are right after the last segment + let prefix = ident.to_string().to_case(Case::Snake); + if segments.is_empty() { + let at_root = true; + self.complete_in_module( + self.module_id, + &prefix, + path_kind, + at_root, + module_completion_kind, + function_completion_kind, + requested_items, + ); + } else if let Some(module_id) = self.resolve_module(segments) { + let at_root = false; + self.complete_in_module( + module_id, + &prefix, + path_kind, + at_root, + module_completion_kind, + function_completion_kind, + requested_items, ); } } } - fn find_in_constructor_expression(&mut self, constructor_expression: &ConstructorExpression) { - self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); - - // Check if we need to autocomplete the field name - if constructor_expression - .fields - .iter() - .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) - { - self.complete_constructor_field_name(constructor_expression); - return; + fn collect_local_variables(&mut self, pattern: &Pattern) { + match pattern { + Pattern::Identifier(ident) => { + self.local_variables.insert(ident.to_string(), ident.span()); + } + Pattern::Mutable(pattern, _, _) => self.collect_local_variables(pattern), + Pattern::Tuple(patterns, _) => { + for pattern in patterns { + self.collect_local_variables(pattern); + } + } + Pattern::Struct(_, patterns, _) => { + for (_, pattern) in patterns { + self.collect_local_variables(pattern); + } + } } + } - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); + fn collect_type_parameters_in_generics(&mut self, generics: &UnresolvedGenerics) { + for generic in generics { + self.collect_type_parameters_in_generic(generic); } } - fn complete_constructor_field_name(&mut self, constructor_expression: &ConstructorExpression) { - let location = - Location::new(constructor_expression.type_name.last_ident().span(), self.file); - let Some(ReferenceId::Struct(struct_id)) = self.interner.find_referenced(location) else { - return; + fn collect_type_parameters_in_generic(&mut self, generic: &UnresolvedGeneric) { + match generic { + UnresolvedGeneric::Variable(ident) => { + self.type_parameters.insert(ident.to_string()); + } + UnresolvedGeneric::Numeric { ident, typ: _ } => { + self.type_parameters.insert(ident.to_string()); + } + UnresolvedGeneric::Resolved(..) => (), }; - - let struct_type = self.interner.get_struct(struct_id); - let struct_type = struct_type.borrow(); - - // First get all of the struct's fields - let mut fields = HashMap::new(); - let fields_as_written = struct_type.get_fields_as_written(); - for (field, typ) in &fields_as_written { - fields.insert(field, typ); - } - - // Remove the ones that already exists in the constructor - for (field, _) in &constructor_expression.fields { - fields.remove(&field.0.contents); - } - - for (field, typ) in fields { - self.completion_items.push(struct_field_completion_item(field, typ)); - } } - fn find_in_member_access_expression( + fn complete_type_fields_and_methods( &mut self, - member_access_expression: &MemberAccessExpression, + typ: &Type, + prefix: &str, + function_completion_kind: FunctionCompletionKind, ) { - let ident = &member_access_expression.rhs; - - if self.byte_index == ident.span().end() as usize { - // Assuming member_access_expression is of the form `foo.bar`, we are right after `bar` - let location = Location::new(member_access_expression.lhs.span, self.file); - if let Some(typ) = self.interner.type_at_location(location) { - let typ = typ.follow_bindings(); - let prefix = ident.to_string().to_case(Case::Snake); - self.complete_type_fields_and_methods( - &typ, - &prefix, - FunctionCompletionKind::NameAndParameters, + match typ { + Type::Struct(struct_type, generics) => { + self.complete_struct_fields(&struct_type.borrow(), generics, prefix); + } + Type::MutableReference(typ) => { + return self.complete_type_fields_and_methods( + typ, + prefix, + function_completion_kind, ); - return; } + Type::Alias(type_alias, _) => { + let type_alias = type_alias.borrow(); + return self.complete_type_fields_and_methods( + &type_alias.typ, + prefix, + function_completion_kind, + ); + } + Type::Tuple(types) => { + self.complete_tuple_fields(types); + } + Type::FieldElement + | Type::Array(_, _) + | Type::Slice(_) + | Type::Integer(_, _) + | Type::Bool + | Type::String(_) + | Type::FmtString(_, _) + | Type::Unit + | Type::TypeVariable(_, _) + | Type::TraitAsType(_, _, _) + | Type::NamedGeneric(_, _, _) + | Type::Function(..) + | Type::Forall(_, _) + | Type::Constant(_) + | Type::Quoted(_) + | Type::InfixExpr(_, _, _) + | Type::Error => (), } - - self.find_in_expression(&member_access_expression.lhs); - } - - fn find_in_if_expression(&mut self, if_expression: &IfExpression) { - self.find_in_expression(&if_expression.condition); - - let old_local_variables = self.local_variables.clone(); - self.find_in_expression(&if_expression.consequence); - self.local_variables = old_local_variables; - - if let Some(alternative) = &if_expression.alternative { - let old_local_variables = self.local_variables.clone(); - self.find_in_expression(alternative); - self.local_variables = old_local_variables; - } + + self.complete_type_methods( + typ, + prefix, + FunctionKind::SelfType(typ), + function_completion_kind, + ); } - fn find_in_lambda(&mut self, lambda: &Lambda) { - for (_, unresolved_type) in &lambda.parameters { - self.find_in_unresolved_type(unresolved_type); - } + fn complete_type_methods( + &mut self, + typ: &Type, + prefix: &str, + function_kind: FunctionKind, + function_completion_kind: FunctionCompletionKind, + ) { + let Some(methods_by_name) = self.interner.get_type_methods(typ) else { + return; + }; - let old_local_variables = self.local_variables.clone(); - for (pattern, _) in &lambda.parameters { - self.collect_local_variables(pattern); + for (name, methods) in methods_by_name { + for func_id in methods.iter() { + if name_matches(name, prefix) { + if let Some(completion_item) = self.function_completion_item( + func_id, + function_completion_kind, + function_kind, + ) { + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); + } + } + } } - - self.find_in_expression(&lambda.body); - - self.local_variables = old_local_variables; - } - - fn find_in_as_trait_path(&mut self, as_trait_path: &AsTraitPath) { - self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); } - fn find_in_unresolved_type(&mut self, unresolved_type: &UnresolvedType) { - if !self.includes_span(unresolved_type.span) { - return; + fn complete_trait_methods( + &mut self, + trait_: &Trait, + prefix: &str, + function_kind: FunctionKind, + function_completion_kind: FunctionCompletionKind, + ) { + for (name, func_id) in &trait_.method_ids { + if name_matches(name, prefix) { + if let Some(completion_item) = + self.function_completion_item(*func_id, function_completion_kind, function_kind) + { + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); + } + } } + } - match &unresolved_type.typ { - UnresolvedTypeData::Array(_, unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - UnresolvedTypeData::Slice(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - UnresolvedTypeData::Parenthesized(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - UnresolvedTypeData::Named(path, unresolved_types, _) => { - self.find_in_path(path, RequestedItems::OnlyTypes); - self.find_in_type_args(unresolved_types); - } - UnresolvedTypeData::TraitAsType(path, unresolved_types) => { - self.find_in_path(path, RequestedItems::OnlyTypes); - self.find_in_type_args(unresolved_types); - } - UnresolvedTypeData::MutableReference(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - UnresolvedTypeData::Tuple(unresolved_types) => { - self.find_in_unresolved_types(unresolved_types); - } - UnresolvedTypeData::Function(args, ret, env, _) => { - self.find_in_unresolved_types(args); - self.find_in_unresolved_type(ret); - self.find_in_unresolved_type(env); - } - UnresolvedTypeData::AsTraitPath(as_trait_path) => { - self.find_in_as_trait_path(as_trait_path); + fn complete_struct_fields( + &mut self, + struct_type: &StructType, + generics: &[Type], + prefix: &str, + ) { + for (name, typ) in &struct_type.get_fields(generics) { + if name_matches(name, prefix) { + self.completion_items.push(struct_field_completion_item(name, typ)); } - UnresolvedTypeData::Expression(_) - | UnresolvedTypeData::FormatString(_, _) - | UnresolvedTypeData::String(_) - | UnresolvedTypeData::Unspecified - | UnresolvedTypeData::Quoted(_) - | UnresolvedTypeData::FieldElement - | UnresolvedTypeData::Integer(_, _) - | UnresolvedTypeData::Bool - | UnresolvedTypeData::Unit - | UnresolvedTypeData::Resolved(_) - | UnresolvedTypeData::Error => (), } } - fn find_in_path(&mut self, path: &Path, requested_items: RequestedItems) { - self.find_in_path_impl(path, requested_items, false); + fn complete_tuple_fields(&mut self, types: &[Type]) { + for (index, typ) in types.iter().enumerate() { + self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + } } - fn find_in_path_impl( + #[allow(clippy::too_many_arguments)] + fn complete_in_module( &mut self, - path: &Path, + module_id: ModuleId, + prefix: &str, + path_kind: PathKind, + at_root: bool, + module_completion_kind: ModuleCompletionKind, + function_completion_kind: FunctionCompletionKind, requested_items: RequestedItems, - mut in_the_middle: bool, ) { - if !self.includes_span(path.span) { + let def_map = &self.def_maps[&module_id.krate]; + let Some(mut module_data) = def_map.modules().get(module_id.local_id.0) else { return; + }; + + if at_root { + match path_kind { + PathKind::Crate => { + let Some(root_module_data) = def_map.modules().get(def_map.root().0) else { + return; + }; + module_data = root_module_data; + } + PathKind::Super => { + let Some(parent) = module_data.parent else { + return; + }; + let Some(parent_module_data) = def_map.modules().get(parent.0) else { + return; + }; + module_data = parent_module_data; + } + PathKind::Dep => (), + PathKind::Plain => (), + } } - let after_colons = self.byte == Some(b':'); + let function_kind = FunctionKind::Any; - let mut idents: Vec = Vec::new(); + let items = match module_completion_kind { + ModuleCompletionKind::DirectChildren => module_data.definitions(), + ModuleCompletionKind::AllVisibleItems => module_data.scope(), + }; - // Find in which ident we are in, and in which part of it - // (it could be that we are completting in the middle of an ident) - for segment in &path.segments { - let ident = &segment.ident; + for ident in items.names() { + let name = &ident.0.contents; - // Check if we are at the end of the ident - if self.byte_index == ident.span().end() as usize { - idents.push(ident.clone()); - break; - } + if name_matches(name, prefix) { + let per_ns = module_data.find_name(ident); + if let Some((module_def_id, visibility, _)) = per_ns.types { + if is_visible(module_id, self.module_id, visibility, self.def_maps) { + if let Some(completion_item) = self.module_def_id_completion_item( + module_def_id, + name.clone(), + function_completion_kind, + function_kind, + requested_items, + ) { + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); + } + } + } - // Check if we are in the middle of an ident - if self.includes_span(ident.span()) { - // If so, take the substring and push that as the list of idents - // we'll do autocompletion for - let offset = self.byte_index - ident.span().start() as usize; - let substring = ident.0.contents[0..offset].to_string(); - let ident = Ident::new( - substring, - Span::from(ident.span().start()..ident.span().start() + offset as u32), - ); - idents.push(ident); - in_the_middle = true; - break; + if let Some((module_def_id, visibility, _)) = per_ns.values { + if is_visible(module_id, self.module_id, visibility, self.def_maps) { + if let Some(completion_item) = self.module_def_id_completion_item( + module_def_id, + name.clone(), + function_completion_kind, + function_kind, + requested_items, + ) { + self.completion_items.push(completion_item); + self.suggested_module_def_ids.insert(module_def_id); + } + } + } } + } - idents.push(ident.clone()); + if at_root && path_kind == PathKind::Plain { + for dependency in self.dependencies { + let dependency_name = dependency.as_name(); + if name_matches(&dependency_name, prefix) { + self.completion_items.push(crate_completion_item(dependency_name)); + } + } - // Stop if the cursor is right after this ident and '::' - if after_colons && self.byte_index == ident.span().end() as usize + 2 { - break; + if name_matches("crate::", prefix) { + self.completion_items.push(simple_completion_item( + "crate::", + CompletionItemKind::KEYWORD, + None, + )); } - } - if idents.len() < path.segments.len() { - in_the_middle = true; + if module_data.parent.is_some() && name_matches("super::", prefix) { + self.completion_items.push(simple_completion_item( + "super::", + CompletionItemKind::KEYWORD, + None, + )); + } } + } - let prefix; - let at_root; - - if after_colons { - prefix = String::new(); - at_root = false; + fn resolve_module(&self, segments: Vec) -> Option { + if let Some(ModuleDefId::ModuleId(module_id)) = self.resolve_path(segments) { + Some(module_id) } else { - prefix = idents.pop().unwrap().to_string(); - at_root = idents.is_empty(); + None } + } - let prefix = prefix.to_case(Case::Snake); - - let is_single_segment = !after_colons && idents.is_empty() && path.kind == PathKind::Plain; - let module_id; - - let module_completion_kind = if after_colons || !idents.is_empty() { - ModuleCompletionKind::DirectChildren - } else { - ModuleCompletionKind::AllVisibleItems - }; + fn resolve_path(&self, segments: Vec) -> Option { + let last_segment = segments.last().unwrap().clone(); - // When completing in the middle of an ident, we don't want to complete - // with function parameters because there might already be function parameters, - // and in the middle of a path it leads to code that won't compile - let function_completion_kind = if in_the_middle { - FunctionCompletionKind::Name - } else { - FunctionCompletionKind::NameAndParameters - }; + let path_segments = segments.into_iter().map(PathSegment::from).collect(); + let path = Path { segments: path_segments, kind: PathKind::Plain, span: Span::default() }; - if idents.is_empty() { - module_id = self.module_id; - } else { - let Some(module_def_id) = self.resolve_path(idents) else { - return; - }; + let path_resolver = StandardPathResolver::new(self.root_module_id); + if let Ok(path_resolution) = path_resolver.resolve(self.def_maps, path, &mut None) { + return Some(path_resolution.module_def_id); + } - match module_def_id { - ModuleDefId::ModuleId(id) => module_id = id, - ModuleDefId::TypeId(struct_id) => { - let struct_type = self.interner.get_struct(struct_id); - self.complete_type_methods( - &Type::Struct(struct_type, vec![]), - &prefix, - FunctionKind::Any, - function_completion_kind, - ); - return; - } - ModuleDefId::FunctionId(_) => { - // There's nothing inside a function - return; - } - ModuleDefId::TypeAliasId(type_alias_id) => { - let type_alias = self.interner.get_type_alias(type_alias_id); - let type_alias = type_alias.borrow(); - self.complete_type_methods( - &type_alias.typ, - &prefix, - FunctionKind::Any, - function_completion_kind, - ); - return; - } - ModuleDefId::TraitId(trait_id) => { - let trait_ = self.interner.get_trait(trait_id); - self.complete_trait_methods( - trait_, - &prefix, - FunctionKind::Any, - function_completion_kind, - ); - return; - } - ModuleDefId::GlobalId(_) => return, + // If we can't resolve a path trough lookup, let's see if the last segment is bound to a type + let location = Location::new(last_segment.span(), self.file); + if let Some(reference_id) = self.interner.find_referenced(location) { + if let Some(id) = module_def_id_from_reference_id(reference_id) { + return Some(id); } } - self.complete_in_module( - module_id, - &prefix, - path.kind, - at_root, - module_completion_kind, - function_completion_kind, - requested_items, - ); + None + } - if is_single_segment { - match requested_items { - RequestedItems::AnyItems => { - self.local_variables_completion(&prefix); - self.builtin_functions_completion(&prefix, function_completion_kind); - self.builtin_values_completion(&prefix); - } - RequestedItems::OnlyTypes => { - self.builtin_types_completion(&prefix); - self.type_parameters_completion(&prefix); - } + fn includes_span(&self, span: Span) -> bool { + span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + } +} + +impl<'a> Visitor for NodeFinder<'a> { + fn visit_item(&mut self, item: &Item) -> bool { + if let ItemKind::Import(..) = &item.kind { + if let Some(lsp_location) = to_lsp_location(self.files, self.file, item.span) { + self.auto_import_line = (lsp_location.range.end.line + 1) as usize; } - self.complete_auto_imports(&prefix, requested_items, function_completion_kind); } + + self.includes_span(item.span) + } + + fn visit_import(&mut self, use_tree: &UseTree) -> bool { + let mut prefixes = Vec::new(); + self.find_in_use_tree(use_tree, &mut prefixes); + false + } + + fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, span: Span) -> bool { + // Switch `self.module_id` to the submodule + let previous_module_id = self.module_id; + + let def_map = &self.def_maps[&self.module_id.krate]; + let Some(module_data) = def_map.modules().get(self.module_id.local_id.0) else { + return false; + }; + if let Some(child_module) = module_data.children.get(&parsed_sub_module.name) { + self.module_id = ModuleId { krate: self.module_id.krate, local_id: *child_module }; + } + + let old_auto_import_line = self.auto_import_line; + self.nesting += 1; + + if let Some(lsp_location) = to_lsp_location(self.files, self.file, span) { + self.auto_import_line = (lsp_location.range.start.line + 1) as usize; + } + + parsed_sub_module.accept_children(self); + + // Restore the old module before continuing + self.module_id = previous_module_id; + self.nesting -= 1; + self.auto_import_line = old_auto_import_line; + + false } - fn local_variables_completion(&mut self, prefix: &str) { - for (name, span) in &self.local_variables { - if name_matches(name, prefix) { - let location = Location::new(*span, self.file); - let description = if let Some(ReferenceId::Local(definition_id)) = - self.interner.reference_at_location(location) - { - let typ = self.interner.definition_type(definition_id); - Some(typ.to_string()) - } else { - None - }; + fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { + let old_type_parameters = self.type_parameters.clone(); + self.collect_type_parameters_in_generics(&noir_function.def.generics); - self.completion_items.push(simple_completion_item( - name, - CompletionItemKind::VARIABLE, - description, - )); - } + for param in &noir_function.def.parameters { + param.typ.accept(self); + } + + noir_function.def.return_type.accept(self); + + self.local_variables.clear(); + for param in &noir_function.def.parameters { + self.collect_local_variables(¶m.pattern); } + + noir_function.def.body.accept(Some(span), self); + + self.type_parameters = old_type_parameters; + + false } - fn type_parameters_completion(&mut self, prefix: &str) { - for name in &self.type_parameters { - if name_matches(name, prefix) { - self.completion_items.push(simple_completion_item( - name, - CompletionItemKind::TYPE_PARAMETER, - None, - )); - } + fn visit_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl, _: Span) -> bool { + self.find_in_path(&noir_trait_impl.trait_name, RequestedItems::OnlyTypes); + noir_trait_impl.object_type.accept(self); + + self.type_parameters.clear(); + self.collect_type_parameters_in_generics(&noir_trait_impl.impl_generics); + + for item in &noir_trait_impl.items { + item.accept(self); } + + self.type_parameters.clear(); + + false } - fn find_in_use_tree(&mut self, use_tree: &UseTree, prefixes: &mut Vec) { - match &use_tree.kind { - UseTreeKind::Path(ident, alias) => { - prefixes.push(use_tree.prefix.clone()); - self.find_in_use_tree_path(prefixes, ident, alias); - prefixes.pop(); - } - UseTreeKind::List(use_trees) => { - prefixes.push(use_tree.prefix.clone()); - for use_tree in use_trees { - self.find_in_use_tree(use_tree, prefixes); - } - prefixes.pop(); + fn visit_type_impl(&mut self, type_impl: &TypeImpl, _: Span) -> bool { + type_impl.object_type.accept(self); + + self.type_parameters.clear(); + self.collect_type_parameters_in_generics(&type_impl.generics); + + for (method, span) in &type_impl.methods { + method.accept(*span, self); + + // Optimization: stop looking in functions past the completion cursor + if span.end() as usize > self.byte_index { + break; } } + + self.type_parameters.clear(); + + false } - fn find_in_use_tree_path( - &mut self, - prefixes: &Vec, - ident: &Ident, - alias: &Option, - ) { - if let Some(_alias) = alias { - // Won't handle completion if there's an alias (for now) - return; + fn visit_noir_struct(&mut self, noir_struct: &NoirStruct, _: Span) -> bool { + self.type_parameters.clear(); + self.collect_type_parameters_in_generics(&noir_struct.generics); + + for (_name, unresolved_type) in &noir_struct.fields { + unresolved_type.accept(self); } - let after_colons = self.byte == Some(b':'); - let at_ident_end = self.byte_index == ident.span().end() as usize; - let at_ident_colons_end = - after_colons && self.byte_index - 2 == ident.span().end() as usize; + self.type_parameters.clear(); - if !(at_ident_end || at_ident_colons_end) { - return; + false + } + + fn visit_trait_item_function( + &mut self, + _name: &Ident, + generics: &UnresolvedGenerics, + parameters: &[(Ident, UnresolvedType)], + return_type: &noirc_frontend::ast::FunctionReturnType, + where_clause: &[noirc_frontend::ast::UnresolvedTraitConstraint], + body: &Option, + ) -> bool { + let old_type_parameters = self.type_parameters.clone(); + self.collect_type_parameters_in_generics(generics); + + for (_name, unresolved_type) in parameters { + unresolved_type.accept(self); } - let path_kind = prefixes[0].kind; + return_type.accept(self); - let mut segments: Vec = Vec::new(); - for prefix in prefixes { - for segment in &prefix.segments { - segments.push(segment.ident.clone()); - } + for unresolved_trait_constraint in where_clause { + unresolved_trait_constraint.typ.accept(self); } - let module_completion_kind = ModuleCompletionKind::DirectChildren; - let function_completion_kind = FunctionCompletionKind::Name; - let requested_items = RequestedItems::AnyItems; + if let Some(body) = body { + self.local_variables.clear(); + for (name, _) in parameters { + self.local_variables.insert(name.to_string(), name.span()); + } + body.accept(None, self); + }; - if after_colons { - // We are right after "::" - segments.push(ident.clone()); + self.type_parameters = old_type_parameters; - if let Some(module_id) = self.resolve_module(segments) { + false + } + + fn visit_call_expression(&mut self, call_expression: &CallExpression, _: Span) -> bool { + // + // foo::b>|<(...) + // + // In this case we want to suggest items in foo but if they are functions + // we don't want to insert arguments, because they are already there (even if + // they could be wrong) just because inserting them would lead to broken code. + if let ExpressionKind::Variable(path) = &call_expression.func.kind { + if self.includes_span(path.span) { + self.find_in_path_impl(path, RequestedItems::AnyItems, true); + return false; + } + } + + // Check if it's this case: + // + // foo.>|<(...) + // + // "foo." is actually broken, but it's parsed as "foo", so this is seen + // as "foo(...)" but if we are at a dot right after "foo" it means it's + // the above case and we want to suggest methods of foo's type. + let after_dot = self.byte == Some(b'.'); + if after_dot && call_expression.func.span.end() as usize == self.byte_index - 1 { + let location = Location::new(call_expression.func.span, self.file); + if let Some(typ) = self.interner.type_at_location(location) { + let typ = typ.follow_bindings(); let prefix = ""; - let at_root = false; - self.complete_in_module( - module_id, - prefix, - path_kind, - at_root, - module_completion_kind, - function_completion_kind, - requested_items, - ); - }; - } else { - // We are right after the last segment - let prefix = ident.to_string().to_case(Case::Snake); - if segments.is_empty() { - let at_root = true; - self.complete_in_module( - self.module_id, - &prefix, - path_kind, - at_root, - module_completion_kind, - function_completion_kind, - requested_items, - ); - } else if let Some(module_id) = self.resolve_module(segments) { - let at_root = false; - self.complete_in_module( - module_id, - &prefix, - path_kind, - at_root, - module_completion_kind, - function_completion_kind, - requested_items, - ); + self.complete_type_fields_and_methods(&typ, prefix, FunctionCompletionKind::Name); + return false; } } + + true } - fn collect_local_variables(&mut self, pattern: &Pattern) { - match pattern { - Pattern::Identifier(ident) => { - self.local_variables.insert(ident.to_string(), ident.span()); - } - Pattern::Mutable(pattern, _, _) => self.collect_local_variables(pattern), - Pattern::Tuple(patterns, _) => { - for pattern in patterns { - self.collect_local_variables(pattern); - } - } - Pattern::Struct(_, patterns, _) => { - for (_, pattern) in patterns { - self.collect_local_variables(pattern); - } + fn visit_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + _: Span, + ) -> bool { + // Check if it's this case: + // + // foo.b>|<(...) + // + // In this case we want to suggest items in foo but if they are functions + // we don't want to insert arguments, because they are already there (even if + // they could be wrong) just because inserting them would lead to broken code. + if self.includes_span(method_call_expression.method_name.span()) { + let location = Location::new(method_call_expression.object.span, self.file); + if let Some(typ) = self.interner.type_at_location(location) { + let typ = typ.follow_bindings(); + let prefix = method_call_expression.method_name.to_string(); + let offset = + self.byte_index - method_call_expression.method_name.span().start() as usize; + let prefix = prefix[0..offset].to_string(); + self.complete_type_fields_and_methods(&typ, &prefix, FunctionCompletionKind::Name); + return false; } } + + true } - fn collect_type_parameters_in_generics(&mut self, generics: &UnresolvedGenerics) { - for generic in generics { - self.collect_type_parameters_in_generic(generic); + fn visit_block_expression( + &mut self, + block_expression: &BlockExpression, + _: Option, + ) -> bool { + let old_local_variables = self.local_variables.clone(); + for statement in &block_expression.statements { + statement.accept(self); + + // Optimization: stop looking in statements past the completion cursor + if statement.span.end() as usize > self.byte_index { + break; + } } + self.local_variables = old_local_variables; + + false } - fn collect_type_parameters_in_generic(&mut self, generic: &UnresolvedGeneric) { - match generic { - UnresolvedGeneric::Variable(ident) => { - self.type_parameters.insert(ident.to_string()); - } - UnresolvedGeneric::Numeric { ident, typ: _ } => { - self.type_parameters.insert(ident.to_string()); - } - UnresolvedGeneric::Resolved(..) => (), - }; + fn visit_let_statement(&mut self, let_statement: &LetStatement) -> bool { + let_statement.accept_children(self); + self.collect_local_variables(&let_statement.pattern); + false } - fn complete_type_fields_and_methods( - &mut self, - typ: &Type, - prefix: &str, - function_completion_kind: FunctionCompletionKind, - ) { - match typ { - Type::Struct(struct_type, generics) => { - self.complete_struct_fields(&struct_type.borrow(), generics, prefix); - } - Type::MutableReference(typ) => { - return self.complete_type_fields_and_methods( - typ, - prefix, - function_completion_kind, - ); - } - Type::Alias(type_alias, _) => { - let type_alias = type_alias.borrow(); - return self.complete_type_fields_and_methods( - &type_alias.typ, - prefix, - function_completion_kind, - ); - } - Type::Tuple(types) => { - self.complete_tuple_fields(types); - } - Type::FieldElement - | Type::Array(_, _) - | Type::Slice(_) - | Type::Integer(_, _) - | Type::Bool - | Type::String(_) - | Type::FmtString(_, _) - | Type::Unit - | Type::TypeVariable(_, _) - | Type::TraitAsType(_, _, _) - | Type::NamedGeneric(_, _, _) - | Type::Function(..) - | Type::Forall(_, _) - | Type::Constant(_) - | Type::Quoted(_) - | Type::InfixExpr(_, _, _) - | Type::Error => (), + fn visit_global(&mut self, let_statement: &LetStatement, _: Span) -> bool { + let_statement.accept_children(self); + false + } + + fn visit_comptime_statement(&mut self, statement: &Statement) -> bool { + // When entering a comptime block, regular local variables shouldn't be offered anymore + let old_local_variables = self.local_variables.clone(); + self.local_variables.clear(); + + statement.accept(self); + + self.local_variables = old_local_variables; + + false + } + + fn visit_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) -> bool { + let old_local_variables = self.local_variables.clone(); + let ident = &for_loop_statement.identifier; + self.local_variables.insert(ident.to_string(), ident.span()); + + for_loop_statement.accept_children(self); + + self.local_variables = old_local_variables; + + false + } + + fn visit_lvalue_ident(&mut self, ident: &Ident) { + if self.byte == Some(b'.') && ident.span().end() as usize == self.byte_index - 1 { + let location = Location::new(ident.span(), self.file); + if let Some(ReferenceId::Local(definition_id)) = self.interner.find_referenced(location) + { + let typ = self.interner.definition_type(definition_id); + let prefix = ""; + self.complete_type_fields_and_methods( + &typ, + prefix, + FunctionCompletionKind::NameAndParameters, + ); + } } + } - self.complete_type_methods( - typ, - prefix, - FunctionKind::SelfType(typ), - function_completion_kind, - ); + fn visit_variable(&mut self, path: &Path, _: Span) -> bool { + self.find_in_path(path, RequestedItems::AnyItems); + false } - fn complete_type_methods( - &mut self, - typ: &Type, - prefix: &str, - function_kind: FunctionKind, - function_completion_kind: FunctionCompletionKind, - ) { - let Some(methods_by_name) = self.interner.get_type_methods(typ) else { - return; - }; + fn visit_expression(&mut self, expression: &Expression) -> bool { + expression.accept_children(self); - for (name, methods) in methods_by_name { - for func_id in methods.iter() { - if name_matches(name, prefix) { - if let Some(completion_item) = self.function_completion_item( - func_id, - function_completion_kind, - function_kind, - ) { - self.completion_items.push(completion_item); - self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); - } - } + // "foo." (no identifier afterwards) is parsed as the expression on the left hand-side of the dot. + // Here we check if there's a dot at the completion position, and if the expression + // ends right before the dot. If so, it means we want to complete the expression's type fields and methods. + // We only do this after visiting nested expressions, because in an expression like `foo & bar.` we want + // to complete for `bar`, not for `foo & bar`. + if self.completion_items.is_empty() + && self.byte == Some(b'.') + && expression.span.end() as usize == self.byte_index - 1 + { + let location = Location::new(expression.span, self.file); + if let Some(typ) = self.interner.type_at_location(location) { + let typ = typ.follow_bindings(); + let prefix = ""; + self.complete_type_fields_and_methods( + &typ, + prefix, + FunctionCompletionKind::NameAndParameters, + ); } } + + false } - fn complete_trait_methods( + fn visit_comptime_expression( &mut self, - trait_: &Trait, - prefix: &str, - function_kind: FunctionKind, - function_completion_kind: FunctionCompletionKind, - ) { - for (name, func_id) in &trait_.method_ids { - if name_matches(name, prefix) { - if let Some(completion_item) = - self.function_completion_item(*func_id, function_completion_kind, function_kind) - { - self.completion_items.push(completion_item); - self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); - } - } - } + block_expression: &BlockExpression, + span: Span, + ) -> bool { + // When entering a comptime block, regular local variables shouldn't be offered anymore + let old_local_variables = self.local_variables.clone(); + self.local_variables.clear(); + + block_expression.accept(Some(span), self); + + self.local_variables = old_local_variables; + + false } - fn complete_struct_fields( + fn visit_constructor_expression( &mut self, - struct_type: &StructType, - generics: &[Type], - prefix: &str, - ) { - for (name, typ) in &struct_type.get_fields(generics) { - if name_matches(name, prefix) { - self.completion_items.push(struct_field_completion_item(name, typ)); - } + constructor_expression: &ConstructorExpression, + _: Span, + ) -> bool { + self.find_in_path(&constructor_expression.type_name, RequestedItems::OnlyTypes); + + // Check if we need to autocomplete the field name + if constructor_expression + .fields + .iter() + .any(|(field_name, _)| field_name.span().end() as usize == self.byte_index) + { + self.complete_constructor_field_name(constructor_expression); + return false; } - } - fn complete_tuple_fields(&mut self, types: &[Type]) { - for (index, typ) in types.iter().enumerate() { - self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + for (_field_name, expression) in &constructor_expression.fields { + expression.accept(self); } + + false } - #[allow(clippy::too_many_arguments)] - fn complete_in_module( + fn visit_member_access_expression( &mut self, - module_id: ModuleId, - prefix: &str, - path_kind: PathKind, - at_root: bool, - module_completion_kind: ModuleCompletionKind, - function_completion_kind: FunctionCompletionKind, - requested_items: RequestedItems, - ) { - let def_map = &self.def_maps[&module_id.krate]; - let Some(mut module_data) = def_map.modules().get(module_id.local_id.0) else { - return; - }; + member_access_expression: &MemberAccessExpression, + _: Span, + ) -> bool { + let ident = &member_access_expression.rhs; - if at_root { - match path_kind { - PathKind::Crate => { - let Some(root_module_data) = def_map.modules().get(def_map.root().0) else { - return; - }; - module_data = root_module_data; - } - PathKind::Super => { - let Some(parent) = module_data.parent else { - return; - }; - let Some(parent_module_data) = def_map.modules().get(parent.0) else { - return; - }; - module_data = parent_module_data; - } - PathKind::Dep => (), - PathKind::Plain => (), + if self.byte_index == ident.span().end() as usize { + // Assuming member_access_expression is of the form `foo.bar`, we are right after `bar` + let location = Location::new(member_access_expression.lhs.span, self.file); + if let Some(typ) = self.interner.type_at_location(location) { + let typ = typ.follow_bindings(); + let prefix = ident.to_string().to_case(Case::Snake); + self.complete_type_fields_and_methods( + &typ, + &prefix, + FunctionCompletionKind::NameAndParameters, + ); + return false; } } - let function_kind = FunctionKind::Any; - - let items = match module_completion_kind { - ModuleCompletionKind::DirectChildren => module_data.definitions(), - ModuleCompletionKind::AllVisibleItems => module_data.scope(), - }; + true + } - for ident in items.names() { - let name = &ident.0.contents; + fn visit_if_expression(&mut self, if_expression: &IfExpression, _: Span) -> bool { + if_expression.condition.accept(self); - if name_matches(name, prefix) { - let per_ns = module_data.find_name(ident); - if let Some((module_def_id, visibility, _)) = per_ns.types { - if is_visible(module_id, self.module_id, visibility, self.def_maps) { - if let Some(completion_item) = self.module_def_id_completion_item( - module_def_id, - name.clone(), - function_completion_kind, - function_kind, - requested_items, - ) { - self.completion_items.push(completion_item); - self.suggested_module_def_ids.insert(module_def_id); - } - } - } + let old_local_variables = self.local_variables.clone(); + if_expression.consequence.accept(self); + self.local_variables = old_local_variables; - if let Some((module_def_id, visibility, _)) = per_ns.values { - if is_visible(module_id, self.module_id, visibility, self.def_maps) { - if let Some(completion_item) = self.module_def_id_completion_item( - module_def_id, - name.clone(), - function_completion_kind, - function_kind, - requested_items, - ) { - self.completion_items.push(completion_item); - self.suggested_module_def_ids.insert(module_def_id); - } - } - } - } + if let Some(alternative) = &if_expression.alternative { + let old_local_variables = self.local_variables.clone(); + alternative.accept(self); + self.local_variables = old_local_variables; } - if at_root && path_kind == PathKind::Plain { - for dependency in self.dependencies { - let dependency_name = dependency.as_name(); - if name_matches(&dependency_name, prefix) { - self.completion_items.push(crate_completion_item(dependency_name)); - } - } - - if name_matches("crate::", prefix) { - self.completion_items.push(simple_completion_item( - "crate::", - CompletionItemKind::KEYWORD, - None, - )); - } + false + } - if module_data.parent.is_some() && name_matches("super::", prefix) { - self.completion_items.push(simple_completion_item( - "super::", - CompletionItemKind::KEYWORD, - None, - )); - } + fn visit_lambda(&mut self, lambda: &Lambda, _: Span) -> bool { + for (_, unresolved_type) in &lambda.parameters { + unresolved_type.accept(self); } - } - fn resolve_module(&self, segments: Vec) -> Option { - if let Some(ModuleDefId::ModuleId(module_id)) = self.resolve_path(segments) { - Some(module_id) - } else { - None + let old_local_variables = self.local_variables.clone(); + for (pattern, _) in &lambda.parameters { + self.collect_local_variables(pattern); } - } - fn resolve_path(&self, segments: Vec) -> Option { - let last_segment = segments.last().unwrap().clone(); + lambda.body.accept(self); - let path_segments = segments.into_iter().map(PathSegment::from).collect(); - let path = Path { segments: path_segments, kind: PathKind::Plain, span: Span::default() }; + self.local_variables = old_local_variables; - let path_resolver = StandardPathResolver::new(self.root_module_id); - if let Ok(path_resolution) = path_resolver.resolve(self.def_maps, path, &mut None) { - return Some(path_resolution.module_def_id); - } + false + } - // If we can't resolve a path trough lookup, let's see if the last segment is bound to a type - let location = Location::new(last_segment.span(), self.file); - if let Some(reference_id) = self.interner.find_referenced(location) { - if let Some(id) = module_def_id_from_reference_id(reference_id) { - return Some(id); - } - } + fn visit_as_trait_path(&mut self, as_trait_path: &AsTraitPath, _: Span) -> bool { + self.find_in_path(&as_trait_path.trait_path, RequestedItems::OnlyTypes); - None + false } - fn includes_span(&self, span: Span) -> bool { - span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize + fn visit_unresolved_type(&mut self, unresolved_type: &UnresolvedType) -> bool { + self.includes_span(unresolved_type.span) + } + + fn visit_named_type( + &mut self, + path: &Path, + unresolved_types: &GenericTypeArgs, + _: Span, + ) -> bool { + self.find_in_path(path, RequestedItems::OnlyTypes); + unresolved_types.accept(self); + false } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs index 162e1616832..32f777dec34 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/auto_import.rs @@ -164,6 +164,7 @@ fn module_id_path( segments.push(&module_attributes.name); let mut current_attributes = module_attributes; + loop { let Some(parent_local_id) = current_attributes.parent else { break; @@ -193,7 +194,7 @@ fn module_id_path( } if !is_relative { - // We don't record module attriubtes for the root module, + // We don't record module attributes for the root module, // so we handle that case separately if let CrateId::Root(_) = target_module_id.krate { segments.push("crate"); diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs index b9c4ce2358a..54340075b15 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -3,7 +3,10 @@ use noirc_frontend::token::Keyword; use strum::IntoEnumIterator; use super::{ - completion_items::{simple_completion_item, snippet_completion_item}, + completion_items::{ + completion_item_with_trigger_parameter_hints_command, simple_completion_item, + snippet_completion_item, + }, kinds::FunctionCompletionKind, name_matches, NodeFinder, }; @@ -31,12 +34,16 @@ impl<'a> NodeFinder<'a> { } } - self.completion_items.push(snippet_completion_item( - label, - CompletionItemKind::FUNCTION, - insert_text, - description, - )); + self.completion_items.push( + completion_item_with_trigger_parameter_hints_command( + snippet_completion_item( + label, + CompletionItemKind::FUNCTION, + insert_text, + description, + ), + ), + ); } } } @@ -90,10 +97,12 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { Keyword::Expr => Some("Expr"), Keyword::Field => Some("Field"), Keyword::FunctionDefinition => Some("FunctionDefinition"), + Keyword::Quoted => Some("Quoted"), Keyword::StructDefinition => Some("StructDefinition"), Keyword::TraitConstraint => Some("TraitConstraint"), Keyword::TraitDefinition => Some("TraitDefinition"), Keyword::TraitImpl => Some("TraitImpl"), + Keyword::TypedExpr => Some("TypedExpr"), Keyword::TypeType => Some("Type"), Keyword::UnresolvedType => Some("UnresolvedType"), @@ -122,7 +131,6 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { | Keyword::Module | Keyword::Mut | Keyword::Pub - | Keyword::Quoted | Keyword::Return | Keyword::ReturnData | Keyword::String @@ -200,6 +208,7 @@ pub(super) fn keyword_builtin_function(keyword: &Keyword) -> Option NodeFinder<'a> { - pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); - } - } - - pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - - pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - - pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - - pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - - pub(super) fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), - } - } - - pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } - } - } - - pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); - } - - pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } - - pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); - } - - pub(super) fn find_in_unresolved_types(&mut self, unresolved_type: &[UnresolvedType]) { - for unresolved_type in unresolved_type { - self.find_in_unresolved_type(unresolved_type); - } - } - - pub(super) fn find_in_type_args(&mut self, generics: &GenericTypeArgs) { - self.find_in_unresolved_types(&generics.ordered_args); - for (_name, typ) in &generics.named_args { - self.find_in_unresolved_type(typ); - } - } - - pub(super) fn find_in_function_return_type(&mut self, return_type: &FunctionReturnType) { - match return_type { - noirc_frontend::ast::FunctionReturnType::Default(_) => (), - noirc_frontend::ast::FunctionReturnType::Ty(unresolved_type) => { - self.find_in_unresolved_type(unresolved_type); - } - } - } - - pub(super) fn find_in_noir_type_alias(&mut self, noir_type_alias: &NoirTypeAlias) { - self.find_in_unresolved_type(&noir_type_alias.typ); - } -} diff --git a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs index bda246f7c98..e06a3451681 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -10,9 +10,9 @@ use noirc_errors::Span; use noirc_frontend::{ ast::{ Expression, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, - NoirTraitImpl, TraitImplItem, TraitItem, TypeImpl, UnresolvedType, UnresolvedTypeData, + NoirTraitImpl, TypeImpl, UnresolvedType, UnresolvedTypeData, Visitor, }, - parser::{Item, ItemKind, ParsedSubModule}, + parser::ParsedSubModule, ParsedModule, }; @@ -40,8 +40,7 @@ pub(crate) fn on_document_symbol_request( let (parsed_module, _errors) = noirc_frontend::parse_program(source); let mut collector = DocumentSymbolCollector::new(file_id, args.files); - let mut symbols = Vec::new(); - collector.collect_in_parsed_module(&parsed_module, &mut symbols); + let symbols = collector.collect(&parsed_module); DocumentSymbolResponse::Nested(symbols) }) }); @@ -52,67 +51,103 @@ pub(crate) fn on_document_symbol_request( struct DocumentSymbolCollector<'a> { file_id: FileId, files: &'a FileMap, + symbols: Vec, } impl<'a> DocumentSymbolCollector<'a> { fn new(file_id: FileId, files: &'a FileMap) -> Self { - Self { file_id, files } + Self { file_id, files, symbols: Vec::new() } } - fn collect_in_parsed_module( - &mut self, - parsed_module: &ParsedModule, - symbols: &mut Vec, - ) { - for item in &parsed_module.items { - self.collect_in_item(item, symbols); - } + fn collect(&mut self, parsed_module: &ParsedModule) -> Vec { + parsed_module.accept(self); + + std::mem::take(&mut self.symbols) } - fn collect_in_item(&mut self, item: &Item, symbols: &mut Vec) { - match &item.kind { - ItemKind::Function(noir_function) => { - self.collect_in_noir_function(noir_function, item.span, symbols); - } - ItemKind::Struct(noir_struct) => { - self.collect_in_noir_struct(noir_struct, item.span, symbols); - } - ItemKind::Trait(noir_trait) => { - self.collect_in_noir_trait(noir_trait, item.span, symbols); - } - ItemKind::TraitImpl(noir_trait_impl) => { - self.collect_in_noir_trait_impl(noir_trait_impl, item.span, symbols); - } - ItemKind::Impl(type_impl) => { - self.collect_in_type_impl(type_impl, item.span, symbols); - } - ItemKind::Submodules(parsed_sub_module) => { - self.collect_in_parsed_sub_module(parsed_sub_module, item.span, symbols); - } - ItemKind::Global(let_statement) => { - self.collect_in_global(let_statement, item.span, symbols); - } - ItemKind::Import(..) | ItemKind::TypeAlias(..) | ItemKind::ModuleDecl(..) => (), - } + fn collect_in_type(&mut self, name: &Ident, typ: Option<&UnresolvedType>) { + let Some(name_location) = self.to_lsp_location(name.span()) else { + return; + }; + + let span = if let Some(typ) = typ { + Span::from(name.span().start()..typ.span.end()) + } else { + name.span() + }; + + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + #[allow(deprecated)] + self.symbols.push(DocumentSymbol { + name: name.to_string(), + detail: None, + kind: SymbolKind::TYPE_PARAMETER, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: None, + }); } - fn collect_in_noir_function( + fn collect_in_constant( &mut self, - noir_function: &NoirFunction, - span: Span, - symbols: &mut Vec, + name: &Ident, + typ: &UnresolvedType, + default_value: Option<&Expression>, ) { + let Some(name_location) = self.to_lsp_location(name.span()) else { + return; + }; + + let mut span = name.span(); + + // If there's a type span, extend the span to include it + span = Span::from(span.start()..typ.span.end()); + + // If there's a default value, extend the span to include it + if let Some(default_value) = default_value { + span = Span::from(span.start()..default_value.span.end()); + } + let Some(location) = self.to_lsp_location(span) else { return; }; + #[allow(deprecated)] + self.symbols.push(DocumentSymbol { + name: name.to_string(), + detail: None, + kind: SymbolKind::CONSTANT, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: None, + }); + } + + fn to_lsp_location(&self, span: Span) -> Option { + super::to_lsp_location(self.files, self.file_id, span) + } +} + +impl<'a> Visitor for DocumentSymbolCollector<'a> { + fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { + let Some(location) = self.to_lsp_location(span) else { + return false; + }; + let Some(selection_location) = self.to_lsp_location(noir_function.name_ident().span()) else { - return; + return false; }; #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: noir_function.name().to_string(), detail: Some(noir_function.def.signature()), kind: SymbolKind::FUNCTION, @@ -122,20 +157,17 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: selection_location.range, children: None, }); + + false } - fn collect_in_noir_struct( - &mut self, - noir_struct: &NoirStruct, - span: Span, - symbols: &mut Vec, - ) { + fn visit_noir_struct(&mut self, noir_struct: &NoirStruct, span: Span) -> bool { let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; let Some(selection_location) = self.to_lsp_location(noir_struct.name.span()) else { - return; + return false; }; let mut children = Vec::new(); @@ -164,7 +196,7 @@ impl<'a> DocumentSymbolCollector<'a> { } #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: noir_struct.name.to_string(), detail: None, kind: SymbolKind::STRUCT, @@ -174,29 +206,31 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: selection_location.range, children: Some(children), }); + + false } - fn collect_in_noir_trait( - &mut self, - noir_trait: &NoirTrait, - span: Span, - symbols: &mut Vec, - ) { + fn visit_noir_trait(&mut self, noir_trait: &NoirTrait, span: Span) -> bool { let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; let Some(selection_location) = self.to_lsp_location(noir_trait.name.span()) else { - return; + return false; }; - let mut children = Vec::new(); + let old_symbols = std::mem::take(&mut self.symbols); + self.symbols = Vec::new(); + for item in &noir_trait.items { - self.collect_in_noir_trait_item(item, &mut children); + item.accept(self); } + let children = std::mem::take(&mut self.symbols); + self.symbols = old_symbols; + #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: noir_trait.name.to_string(), detail: None, kind: SymbolKind::INTERFACE, @@ -206,153 +240,87 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: selection_location.range, children: Some(children), }); - } - - fn collect_in_noir_trait_item( - &mut self, - trait_item: &TraitItem, - symbols: &mut Vec, - ) { - // Ideally `TraitItem` has a `span` for the entire definition, and we'd use that - // for the `range` property. For now we do our best to find a reasonable span. - match trait_item { - TraitItem::Function { name, parameters, return_type, body, .. } => { - let Some(name_location) = self.to_lsp_location(name.span()) else { - return; - }; - - let mut span = name.span(); - - // If there are parameters, extend the span to include the last parameter. - if let Some((param_name, _param_type)) = parameters.last() { - span = Span::from(span.start()..param_name.span().end()); - } - - // If there's a return type, extend the span to include it - match return_type { - FunctionReturnType::Default(return_type_span) => { - span = Span::from(span.start()..return_type_span.end()); - } - FunctionReturnType::Ty(typ) => { - span = Span::from(span.start()..typ.span.end()); - } - } - - // If there's a body, extend the span to include it - if let Some(body) = body { - if let Some(statement) = body.statements.last() { - span = Span::from(span.start()..statement.span.end()); - } - } - - let Some(location) = self.to_lsp_location(span) else { - return; - }; - #[allow(deprecated)] - symbols.push(DocumentSymbol { - name: name.to_string(), - detail: None, - kind: SymbolKind::METHOD, - tags: None, - deprecated: None, - range: location.range, - selection_range: name_location.range, - children: None, - }); - } - TraitItem::Constant { name, typ, default_value } => { - self.collect_in_constant(name, typ, default_value.as_ref(), symbols); - } - TraitItem::Type { name } => { - self.collect_in_type(name, None, symbols); - } - } + false } - fn collect_in_constant( + fn visit_trait_item_function( &mut self, name: &Ident, - typ: &UnresolvedType, - default_value: Option<&Expression>, - symbols: &mut Vec, - ) { + _generics: &noirc_frontend::ast::UnresolvedGenerics, + parameters: &[(Ident, UnresolvedType)], + return_type: &FunctionReturnType, + _where_clause: &[noirc_frontend::ast::UnresolvedTraitConstraint], + body: &Option, + ) -> bool { let Some(name_location) = self.to_lsp_location(name.span()) else { - return; + return false; }; let mut span = name.span(); - // If there's a type span, extend the span to include it - span = Span::from(span.start()..typ.span.end()); + // If there are parameters, extend the span to include the last parameter. + if let Some((param_name, _param_type)) = parameters.last() { + span = Span::from(span.start()..param_name.span().end()); + } - // If there's a default value, extend the span to include it - if let Some(default_value) = default_value { - span = Span::from(span.start()..default_value.span.end()); + // If there's a return type, extend the span to include it + match return_type { + FunctionReturnType::Default(return_type_span) => { + span = Span::from(span.start()..return_type_span.end()); + } + FunctionReturnType::Ty(typ) => { + span = Span::from(span.start()..typ.span.end()); + } + } + + // If there's a body, extend the span to include it + if let Some(body) = body { + if let Some(statement) = body.statements.last() { + span = Span::from(span.start()..statement.span.end()); + } } let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: name.to_string(), detail: None, - kind: SymbolKind::CONSTANT, + kind: SymbolKind::METHOD, tags: None, deprecated: None, range: location.range, selection_range: name_location.range, children: None, }); + + false } - fn collect_in_type( + fn visit_trait_item_constant( &mut self, name: &Ident, - typ: Option<&UnresolvedType>, - symbols: &mut Vec, - ) { - let Some(name_location) = self.to_lsp_location(name.span()) else { - return; - }; - - let span = if let Some(typ) = typ { - Span::from(name.span().start()..typ.span.end()) - } else { - name.span() - }; - - let Some(location) = self.to_lsp_location(span) else { - return; - }; + typ: &UnresolvedType, + default_value: &Option, + ) -> bool { + self.collect_in_constant(name, typ, default_value.as_ref()); + false + } - #[allow(deprecated)] - symbols.push(DocumentSymbol { - name: name.to_string(), - detail: None, - kind: SymbolKind::TYPE_PARAMETER, - tags: None, - deprecated: None, - range: location.range, - selection_range: name_location.range, - children: None, - }); + fn visit_trait_item_type(&mut self, name: &Ident) { + self.collect_in_type(name, None); } - fn collect_in_noir_trait_impl( - &mut self, - noir_trait_impl: &NoirTraitImpl, - span: Span, - symbols: &mut Vec, - ) { + fn visit_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl, span: Span) -> bool { let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; let Some(name_location) = self.to_lsp_location(noir_trait_impl.trait_name.span) else { - return; + return false; }; let mut trait_name = String::new(); @@ -378,13 +346,18 @@ impl<'a> DocumentSymbolCollector<'a> { trait_name.push('>'); } - let mut children = Vec::new(); + let old_symbols = std::mem::take(&mut self.symbols); + self.symbols = Vec::new(); + for trait_impl_item in &noir_trait_impl.items { - self.collect_in_trait_impl_item(trait_impl_item, &mut children); + trait_impl_item.accept(self); } + let children = std::mem::take(&mut self.symbols); + self.symbols = old_symbols; + #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: format!("impl {} for {}", trait_name, noir_trait_impl.object_type), detail: None, kind: SymbolKind::NAMESPACE, @@ -394,54 +367,52 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: name_location.range, children: Some(children), }); + + false } - fn collect_in_trait_impl_item( + fn visit_trait_impl_item_constant( &mut self, - trait_impl_item: &TraitImplItem, - symbols: &mut Vec, - ) { - match trait_impl_item { - TraitImplItem::Function(noir_function) => { - let span = Span::from( - noir_function.name_ident().span().start()..noir_function.span().end(), - ); - self.collect_in_noir_function(noir_function, span, symbols); - } - TraitImplItem::Constant(name, typ, default_value) => { - self.collect_in_constant(name, typ, Some(default_value), symbols); - } - TraitImplItem::Type { name, alias } => self.collect_in_type(name, Some(alias), symbols), - } + name: &Ident, + typ: &UnresolvedType, + default_value: &Expression, + ) -> bool { + self.collect_in_constant(name, typ, Some(default_value)); + false } - fn collect_in_type_impl( - &mut self, - type_impl: &TypeImpl, - span: Span, - symbols: &mut Vec, - ) { + fn visit_trait_impl_item_type(&mut self, name: &Ident, alias: &UnresolvedType) -> bool { + self.collect_in_type(name, Some(alias)); + false + } + + fn visit_type_impl(&mut self, type_impl: &TypeImpl, span: Span) -> bool { let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; let UnresolvedTypeData::Named(name_path, ..) = &type_impl.object_type.typ else { - return; + return false; }; let name = name_path.last_ident(); let Some(name_location) = self.to_lsp_location(name.span()) else { - return; + return false; }; - let mut children = Vec::new(); + let old_symbols = std::mem::take(&mut self.symbols); + self.symbols = Vec::new(); + for (noir_function, noir_function_span) in &type_impl.methods { - self.collect_in_noir_function(noir_function, *noir_function_span, &mut children); + noir_function.accept(*noir_function_span, self); } + let children = std::mem::take(&mut self.symbols); + self.symbols = old_symbols; + #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: name.to_string(), detail: None, kind: SymbolKind::NAMESPACE, @@ -451,29 +422,31 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: name_location.range, children: Some(children), }); + + false } - fn collect_in_parsed_sub_module( - &mut self, - parsed_sub_module: &ParsedSubModule, - span: Span, - symbols: &mut Vec, - ) { + fn visit_parsed_submodule(&mut self, parsed_sub_module: &ParsedSubModule, span: Span) -> bool { let Some(name_location) = self.to_lsp_location(parsed_sub_module.name.span()) else { - return; + return false; }; let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; - let mut children = Vec::new(); + let old_symbols = std::mem::take(&mut self.symbols); + self.symbols = Vec::new(); + for item in &parsed_sub_module.contents.items { - self.collect_in_item(item, &mut children); + item.accept(self); } + let children = std::mem::take(&mut self.symbols); + self.symbols = old_symbols; + #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: parsed_sub_module.name.to_string(), detail: None, kind: SymbolKind::MODULE, @@ -483,24 +456,21 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: name_location.range, children: Some(children), }); + + false } - fn collect_in_global( - &mut self, - global: &LetStatement, - span: Span, - symbols: &mut Vec, - ) { + fn visit_global(&mut self, global: &LetStatement, span: Span) -> bool { let Some(name_location) = self.to_lsp_location(global.pattern.span()) else { - return; + return false; }; let Some(location) = self.to_lsp_location(span) else { - return; + return false; }; #[allow(deprecated)] - symbols.push(DocumentSymbol { + self.symbols.push(DocumentSymbol { name: global.pattern.to_string(), detail: None, kind: SymbolKind::CONSTANT, @@ -510,10 +480,8 @@ impl<'a> DocumentSymbolCollector<'a> { selection_range: name_location.range, children: None, }); - } - fn to_lsp_location(&self, span: Span) -> Option { - super::to_lsp_location(self.files, self.file_id, span) + false } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs index a1e083187d3..43e0227fa26 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -10,14 +10,15 @@ use noirc_errors::{Location, Span}; use noirc_frontend::{ self, ast::{ - BlockExpression, Expression, ExpressionKind, Ident, LetStatement, NoirFunction, Pattern, - Statement, StatementKind, TraitImplItem, TraitItem, UnresolvedTypeData, + CallExpression, Expression, ExpressionKind, ForLoopStatement, Ident, Lambda, LetStatement, + MethodCallExpression, NoirFunction, NoirTraitImpl, Pattern, Statement, TypeImpl, + UnresolvedTypeData, Visitor, }, hir_def::stmt::HirPattern, macros_api::NodeInterner, node_interner::ReferenceId, - parser::{Item, ItemKind}, - ParsedModule, Type, TypeBinding, TypeVariable, TypeVariableKind, + parser::{Item, ParsedSubModule}, + Type, TypeBinding, TypeVariable, TypeVariableKind, }; use crate::{utils, LspState}; @@ -47,7 +48,7 @@ pub(crate) fn on_inlay_hint_request( let mut collector = InlayHintCollector::new(args.files, file_id, args.interner, span, options); - collector.collect_in_parsed_module(&parsed_moduled); + parsed_moduled.accept(&mut collector); collector.inlay_hints }) }); @@ -73,264 +74,6 @@ impl<'a> InlayHintCollector<'a> { ) -> InlayHintCollector<'a> { InlayHintCollector { files, file_id, interner, span, options, inlay_hints: Vec::new() } } - fn collect_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.collect_in_item(item); - } - } - - fn collect_in_item(&mut self, item: &Item) { - if !self.intersects_span(item.span) { - return; - } - - match &item.kind { - ItemKind::Function(noir_function) => { - self.collect_in_noir_function(noir_function, item.span); - } - ItemKind::Trait(noir_trait) => { - for item in &noir_trait.items { - self.collect_in_trait_item(item); - } - } - ItemKind::TraitImpl(noir_trait_impl) => { - for trait_impl_item in &noir_trait_impl.items { - self.collect_in_trait_impl_item(trait_impl_item, item.span); - } - - self.show_closing_brace_hint(item.span, || { - format!( - " impl {} for {}", - noir_trait_impl.trait_name, noir_trait_impl.object_type - ) - }); - } - ItemKind::Impl(type_impl) => { - for (noir_function, span) in &type_impl.methods { - self.collect_in_noir_function(noir_function, *span); - } - - self.show_closing_brace_hint(item.span, || { - format!(" impl {}", type_impl.object_type) - }); - } - ItemKind::Global(let_statement) => self.collect_in_let_statement(let_statement), - ItemKind::Submodules(parsed_submodule) => { - self.collect_in_parsed_module(&parsed_submodule.contents); - - self.show_closing_brace_hint(item.span, || { - if parsed_submodule.is_contract { - format!(" contract {}", parsed_submodule.name) - } else { - format!(" mod {}", parsed_submodule.name) - } - }); - } - ItemKind::ModuleDecl(_) => (), - ItemKind::Import(_) => (), - ItemKind::Struct(_) => (), - ItemKind::TypeAlias(_) => (), - } - } - - fn collect_in_trait_item(&mut self, item: &TraitItem) { - match item { - TraitItem::Function { body, .. } => { - if let Some(body) = body { - self.collect_in_block_expression(body); - } - } - TraitItem::Constant { name: _, typ: _, default_value } => { - if let Some(default_value) = default_value { - self.collect_in_expression(default_value); - } - } - TraitItem::Type { .. } => (), - } - } - - fn collect_in_trait_impl_item(&mut self, item: &TraitImplItem, span: Span) { - match item { - TraitImplItem::Function(noir_function) => { - self.collect_in_noir_function(noir_function, span); - } - TraitImplItem::Constant(_name, _typ, default_value) => { - self.collect_in_expression(default_value); - } - TraitImplItem::Type { .. } => (), - } - } - - fn collect_in_noir_function(&mut self, noir_function: &NoirFunction, span: Span) { - self.collect_in_block_expression(&noir_function.def.body); - - self.show_closing_brace_hint(span, || format!(" fn {}", noir_function.def.name)); - } - - fn collect_in_let_statement(&mut self, let_statement: &LetStatement) { - // Only show inlay hints for let variables that don't have an explicit type annotation - if let UnresolvedTypeData::Unspecified = let_statement.r#type.typ { - self.collect_in_pattern(&let_statement.pattern); - }; - - self.collect_in_expression(&let_statement.expression); - } - - fn collect_in_block_expression(&mut self, block_expression: &BlockExpression) { - for statement in &block_expression.statements { - self.collect_in_statement(statement); - } - } - - fn collect_in_statement(&mut self, statement: &Statement) { - if !self.intersects_span(statement.span) { - return; - } - - match &statement.kind { - StatementKind::Let(let_statement) => self.collect_in_let_statement(let_statement), - StatementKind::Constrain(constrain_statement) => { - self.collect_in_expression(&constrain_statement.0); - } - StatementKind::Expression(expression) => self.collect_in_expression(expression), - StatementKind::Assign(assign_statement) => { - self.collect_in_expression(&assign_statement.expression); - } - StatementKind::For(for_loop_statement) => { - self.collect_in_ident(&for_loop_statement.identifier, false); - self.collect_in_expression(&for_loop_statement.block); - } - StatementKind::Comptime(statement) => self.collect_in_statement(statement), - StatementKind::Semi(expression) => self.collect_in_expression(expression), - StatementKind::Break => (), - StatementKind::Continue => (), - StatementKind::Error => (), - } - } - - fn collect_in_expression(&mut self, expression: &Expression) { - if !self.intersects_span(expression.span) { - return; - } - - match &expression.kind { - ExpressionKind::Block(block_expression) => { - self.collect_in_block_expression(block_expression); - } - ExpressionKind::Prefix(prefix_expression) => { - self.collect_in_expression(&prefix_expression.rhs); - } - ExpressionKind::Index(index_expression) => { - self.collect_in_expression(&index_expression.collection); - self.collect_in_expression(&index_expression.index); - } - ExpressionKind::Call(call_expression) => { - self.collect_call_parameter_names( - get_expression_name(&call_expression.func), - call_expression.func.span, - &call_expression.arguments, - ); - - self.collect_in_expression(&call_expression.func); - for arg in &call_expression.arguments { - self.collect_in_expression(arg); - } - } - ExpressionKind::MethodCall(method_call_expression) => { - self.collect_call_parameter_names( - Some(method_call_expression.method_name.to_string()), - method_call_expression.method_name.span(), - &method_call_expression.arguments, - ); - - self.collect_in_expression(&method_call_expression.object); - for arg in &method_call_expression.arguments { - self.collect_in_expression(arg); - } - } - ExpressionKind::Constructor(constructor_expression) => { - for (_name, expr) in &constructor_expression.fields { - self.collect_in_expression(expr); - } - } - ExpressionKind::MemberAccess(member_access_expression) => { - self.collect_in_expression(&member_access_expression.lhs); - } - ExpressionKind::Cast(cast_expression) => { - self.collect_in_expression(&cast_expression.lhs); - } - ExpressionKind::Infix(infix_expression) => { - self.collect_in_expression(&infix_expression.lhs); - self.collect_in_expression(&infix_expression.rhs); - } - ExpressionKind::If(if_expression) => { - self.collect_in_expression(&if_expression.condition); - self.collect_in_expression(&if_expression.consequence); - if let Some(alternative) = &if_expression.alternative { - self.collect_in_expression(alternative); - } - } - ExpressionKind::Tuple(expressions) => { - for expression in expressions { - self.collect_in_expression(expression); - } - } - ExpressionKind::Lambda(lambda) => { - for (pattern, typ) in &lambda.parameters { - if matches!(typ.typ, UnresolvedTypeData::Unspecified) { - self.collect_in_pattern(pattern); - } - } - - self.collect_in_expression(&lambda.body); - } - ExpressionKind::Parenthesized(parenthesized) => { - self.collect_in_expression(parenthesized); - } - ExpressionKind::Unquote(expression) => { - self.collect_in_expression(expression); - } - ExpressionKind::Comptime(block_expression, _span) => { - self.collect_in_block_expression(block_expression); - } - ExpressionKind::Unsafe(block_expression, _span) => { - self.collect_in_block_expression(block_expression); - } - ExpressionKind::AsTraitPath(path) => { - self.collect_in_ident(&path.impl_item, true); - } - ExpressionKind::Literal(..) - | ExpressionKind::Variable(..) - | ExpressionKind::Quote(..) - | ExpressionKind::Resolved(..) - | ExpressionKind::Error => (), - } - } - - fn collect_in_pattern(&mut self, pattern: &Pattern) { - if !self.options.type_hints.enabled { - return; - } - - match pattern { - Pattern::Identifier(ident) => { - self.collect_in_ident(ident, true); - } - Pattern::Mutable(pattern, _span, _is_synthesized) => { - self.collect_in_pattern(pattern); - } - Pattern::Tuple(patterns, _span) => { - for pattern in patterns { - self.collect_in_pattern(pattern); - } - } - Pattern::Struct(_path, patterns, _span) => { - for (_ident, pattern) in patterns { - self.collect_in_pattern(pattern); - } - } - } - } fn collect_in_ident(&mut self, ident: &Ident, editable: bool) { if !self.options.type_hints.enabled { @@ -525,6 +268,112 @@ impl<'a> InlayHintCollector<'a> { } } +impl<'a> Visitor for InlayHintCollector<'a> { + fn visit_item(&mut self, item: &Item) -> bool { + self.intersects_span(item.span) + } + + fn visit_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl, span: Span) -> bool { + self.show_closing_brace_hint(span, || { + format!(" impl {} for {}", noir_trait_impl.trait_name, noir_trait_impl.object_type) + }); + + true + } + + fn visit_type_impl(&mut self, type_impl: &TypeImpl, span: Span) -> bool { + self.show_closing_brace_hint(span, || format!(" impl {}", type_impl.object_type)); + + true + } + + fn visit_parsed_submodule(&mut self, parsed_submodule: &ParsedSubModule, span: Span) -> bool { + self.show_closing_brace_hint(span, || { + if parsed_submodule.is_contract { + format!(" contract {}", parsed_submodule.name) + } else { + format!(" mod {}", parsed_submodule.name) + } + }); + + true + } + + fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { + self.show_closing_brace_hint(span, || format!(" fn {}", noir_function.def.name)); + + true + } + + fn visit_statement(&mut self, statement: &Statement) -> bool { + self.intersects_span(statement.span) + } + + fn visit_let_statement(&mut self, let_statement: &LetStatement) -> bool { + // Only show inlay hints for let variables that don't have an explicit type annotation + if let UnresolvedTypeData::Unspecified = let_statement.r#type.typ { + let_statement.pattern.accept(self); + }; + + let_statement.expression.accept(self); + + false + } + + fn visit_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) -> bool { + self.collect_in_ident(&for_loop_statement.identifier, false); + true + } + + fn visit_expression(&mut self, expression: &Expression) -> bool { + self.intersects_span(expression.span) + } + + fn visit_call_expression(&mut self, call_expression: &CallExpression, _: Span) -> bool { + self.collect_call_parameter_names( + get_expression_name(&call_expression.func), + call_expression.func.span, + &call_expression.arguments, + ); + + true + } + + fn visit_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + _: Span, + ) -> bool { + self.collect_call_parameter_names( + Some(method_call_expression.method_name.to_string()), + method_call_expression.method_name.span(), + &method_call_expression.arguments, + ); + + true + } + + fn visit_lambda(&mut self, lambda: &Lambda, _: Span) -> bool { + for (pattern, typ) in &lambda.parameters { + if matches!(typ.typ, UnresolvedTypeData::Unspecified) { + pattern.accept(self); + } + } + + lambda.body.accept(self); + + false + } + + fn visit_pattern(&mut self, _: &Pattern) -> bool { + self.options.type_hints.enabled + } + + fn visit_identifier_pattern(&mut self, ident: &Ident) { + self.collect_in_ident(ident, true); + } +} + fn string_part(str: impl Into) -> InlayHintLabelPart { InlayHintLabelPart { value: str.into(), location: None, tooltip: None, command: None } } @@ -692,6 +541,7 @@ fn get_expression_name(expression: &Expression) -> Option { | ExpressionKind::Unquote(..) | ExpressionKind::Comptime(..) | ExpressionKind::Resolved(..) + | ExpressionKind::Interned(..) | ExpressionKind::Literal(..) | ExpressionKind::Unsafe(..) | ExpressionKind::Error => None, diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs index 8aa74fe9900..8f1bbb1570f 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help.rs @@ -7,10 +7,14 @@ use lsp_types::{ }; use noirc_errors::{Location, Span}; use noirc_frontend::{ - ast::{CallExpression, Expression, FunctionReturnType, MethodCallExpression}, + ast::{ + CallExpression, ConstrainKind, ConstrainStatement, Expression, ExpressionKind, + FunctionReturnType, MethodCallExpression, Statement, Visitor, + }, hir_def::{function::FuncMeta, stmt::HirPattern}, macros_api::NodeInterner, node_interner::ReferenceId, + parser::Item, ParsedModule, Type, }; @@ -19,7 +23,6 @@ use crate::{utils, LspState}; use super::process_request; mod tests; -mod traversal; pub(crate) fn on_signature_help_request( state: &mut LspState, @@ -61,49 +64,11 @@ impl<'a> SignatureFinder<'a> { } fn find(&mut self, parsed_module: &ParsedModule) -> Option { - self.find_in_parsed_module(parsed_module); + parsed_module.accept(self); self.signature_help.clone() } - fn find_in_call_expression(&mut self, call_expression: &CallExpression, span: Span) { - self.find_in_expression(&call_expression.func); - self.find_in_expressions(&call_expression.arguments); - - let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); - let span = call_expression.func.span; - let name_span = Span::from(span.end() - 1..span.end()); - let has_self = false; - - self.try_compute_signature_help( - &call_expression.arguments, - arguments_span, - name_span, - has_self, - ); - } - - fn find_in_method_call_expression( - &mut self, - method_call_expression: &MethodCallExpression, - span: Span, - ) { - self.find_in_expression(&method_call_expression.object); - self.find_in_expressions(&method_call_expression.arguments); - - let arguments_span = - Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); - let name_span = method_call_expression.method_name.span(); - let has_self = true; - - self.try_compute_signature_help( - &method_call_expression.arguments, - arguments_span, - name_span, - has_self, - ); - } - fn try_compute_signature_help( &mut self, arguments: &[Expression], @@ -119,18 +84,7 @@ impl<'a> SignatureFinder<'a> { return; } - let mut active_parameter = None; - for (index, arg) in arguments.iter().enumerate() { - if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { - active_parameter = Some(index as u32); - break; - } - } - - if active_parameter.is_none() { - active_parameter = Some(arguments.len() as u32); - } - + let active_parameter = self.compute_active_parameter(arguments); let location = Location::new(name_span, self.file); // Check if the call references a named function @@ -267,6 +221,60 @@ impl<'a> SignatureFinder<'a> { } } + fn assert_signature_information(&self, active_parameter: Option) -> SignatureInformation { + self.hardcoded_signature_information( + active_parameter, + "assert", + &["predicate: bool", "[failure_message: str]"], + ) + } + + fn assert_eq_signature_information( + &self, + active_parameter: Option, + ) -> SignatureInformation { + self.hardcoded_signature_information( + active_parameter, + "assert_eq", + &["lhs: T", "rhs: T", "[failure_message: str]"], + ) + } + + fn hardcoded_signature_information( + &self, + active_parameter: Option, + name: &str, + arguments: &[&str], + ) -> SignatureInformation { + let mut label = String::new(); + let mut parameters = Vec::new(); + + label.push_str(name); + label.push('('); + for (index, typ) in arguments.iter().enumerate() { + if index > 0 { + label.push_str(", "); + } + + let parameter_start = label.chars().count(); + label.push_str(typ); + let parameter_end = label.chars().count(); + + parameters.push(ParameterInformation { + label: ParameterLabel::LabelOffsets([parameter_start as u32, parameter_end as u32]), + documentation: None, + }); + } + label.push(')'); + + SignatureInformation { + label, + documentation: None, + parameters: Some(parameters), + active_parameter, + } + } + fn hir_pattern_to_argument(&self, pattern: &HirPattern, text: &mut String) { match pattern { HirPattern::Identifier(hir_ident) => { @@ -286,7 +294,124 @@ impl<'a> SignatureFinder<'a> { self.signature_help = Some(signature_help); } + fn compute_active_parameter(&self, arguments: &[Expression]) -> Option { + let mut active_parameter = None; + for (index, arg) in arguments.iter().enumerate() { + if self.includes_span(arg.span) || arg.span.start() as usize >= self.byte_index { + active_parameter = Some(index as u32); + break; + } + } + + if active_parameter.is_none() { + active_parameter = Some(arguments.len() as u32); + } + + active_parameter + } + fn includes_span(&self, span: Span) -> bool { span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize } } + +impl<'a> Visitor for SignatureFinder<'a> { + fn visit_item(&mut self, item: &Item) -> bool { + self.includes_span(item.span) + } + + fn visit_statement(&mut self, statement: &Statement) -> bool { + self.includes_span(statement.span) + } + + fn visit_expression(&mut self, expression: &Expression) -> bool { + self.includes_span(expression.span) + } + + fn visit_call_expression(&mut self, call_expression: &CallExpression, span: Span) -> bool { + call_expression.accept_children(self); + + let arguments_span = Span::from(call_expression.func.span.end() + 1..span.end() - 1); + let span = call_expression.func.span; + let name_span = Span::from(span.end() - 1..span.end()); + let has_self = false; + + self.try_compute_signature_help( + &call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + + false + } + + fn visit_method_call_expression( + &mut self, + method_call_expression: &MethodCallExpression, + span: Span, + ) -> bool { + method_call_expression.accept_children(self); + + let arguments_span = + Span::from(method_call_expression.method_name.span().end() + 1..span.end() - 1); + let name_span = method_call_expression.method_name.span(); + let has_self = true; + + self.try_compute_signature_help( + &method_call_expression.arguments, + arguments_span, + name_span, + has_self, + ); + + false + } + + fn visit_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) -> bool { + constrain_statement.accept_children(self); + + if self.signature_help.is_some() { + return false; + } + + let arguments_span = if let Some(expr) = &constrain_statement.1 { + Span::from(constrain_statement.0.span.start()..expr.span.end()) + } else { + constrain_statement.0.span + }; + + if !self.includes_span(arguments_span) { + return false; + } + + match constrain_statement.2 { + ConstrainKind::Assert => { + let mut arguments = vec![constrain_statement.0.clone()]; + if let Some(expr) = &constrain_statement.1 { + arguments.push(expr.clone()); + } + + let active_parameter = self.compute_active_parameter(&arguments); + let signature_information = self.assert_signature_information(active_parameter); + self.set_signature_help(signature_information); + } + ConstrainKind::AssertEq => { + if let ExpressionKind::Infix(infix) = &constrain_statement.0.kind { + let mut arguments = vec![infix.lhs.clone(), infix.rhs.clone()]; + if let Some(expr) = &constrain_statement.1 { + arguments.push(expr.clone()); + } + + let active_parameter = self.compute_active_parameter(&arguments); + let signature_information = + self.assert_eq_signature_information(active_parameter); + self.set_signature_help(signature_information); + } + } + ConstrainKind::Constrain => (), + } + + false + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs index c48ee159084..4b3f3c38156 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/signature_help/tests.rs @@ -193,4 +193,51 @@ mod signature_help_tests { assert_eq!(signature.active_parameter, Some(0)); } + + #[test] + async fn test_signature_help_for_assert() { + let src = r#" + fn bar() { + assert(>|<1, "hello"); + } + "#; + + let signature_help = get_signature_help(src).await; + assert_eq!(signature_help.signatures.len(), 1); + + let signature = &signature_help.signatures[0]; + assert_eq!(signature.label, "assert(predicate: bool, [failure_message: str])"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 2); + + check_label(&signature.label, ¶ms[0].label, "predicate: bool"); + check_label(&signature.label, ¶ms[1].label, "[failure_message: str]"); + + assert_eq!(signature.active_parameter, Some(0)); + } + + #[test] + async fn test_signature_help_for_assert_eq() { + let src = r#" + fn bar() { + assert_eq(>|])"); + + let params = signature.parameters.as_ref().unwrap(); + assert_eq!(params.len(), 3); + + check_label(&signature.label, ¶ms[0].label, "lhs: T"); + check_label(&signature.label, ¶ms[1].label, "rhs: T"); + check_label(&signature.label, ¶ms[2].label, "[failure_message: str]"); + + assert_eq!(signature.active_parameter, Some(0)); + } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs b/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs deleted file mode 100644 index 22f92a86124..00000000000 --- a/noir/noir-repo/tooling/lsp/src/requests/signature_help/traversal.rs +++ /dev/null @@ -1,304 +0,0 @@ -/// This file includes the signature help logic that's just about -/// traversing the AST without any additional logic. -use super::SignatureFinder; - -use noirc_frontend::{ - ast::{ - ArrayLiteral, AssignStatement, BlockExpression, CastExpression, ConstrainStatement, - ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, ForRange, - IfExpression, IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, - MemberAccessExpression, NoirFunction, NoirTrait, NoirTraitImpl, Statement, StatementKind, - TraitImplItem, TraitItem, TypeImpl, - }, - parser::{Item, ItemKind}, - ParsedModule, -}; - -impl<'a> SignatureFinder<'a> { - pub(super) fn find_in_parsed_module(&mut self, parsed_module: &ParsedModule) { - for item in &parsed_module.items { - self.find_in_item(item); - } - } - - pub(super) fn find_in_item(&mut self, item: &Item) { - if !self.includes_span(item.span) { - return; - } - - match &item.kind { - ItemKind::Submodules(parsed_sub_module) => { - self.find_in_parsed_module(&parsed_sub_module.contents); - } - ItemKind::Function(noir_function) => self.find_in_noir_function(noir_function), - ItemKind::TraitImpl(noir_trait_impl) => self.find_in_noir_trait_impl(noir_trait_impl), - ItemKind::Impl(type_impl) => self.find_in_type_impl(type_impl), - ItemKind::Global(let_statement) => self.find_in_let_statement(let_statement), - ItemKind::Trait(noir_trait) => self.find_in_noir_trait(noir_trait), - ItemKind::Import(..) - | ItemKind::TypeAlias(_) - | ItemKind::Struct(_) - | ItemKind::ModuleDecl(_) => (), - } - } - - pub(super) fn find_in_noir_function(&mut self, noir_function: &NoirFunction) { - self.find_in_block_expression(&noir_function.def.body); - } - - pub(super) fn find_in_noir_trait_impl(&mut self, noir_trait_impl: &NoirTraitImpl) { - for item in &noir_trait_impl.items { - self.find_in_trait_impl_item(item); - } - } - - pub(super) fn find_in_trait_impl_item(&mut self, item: &TraitImplItem) { - match item { - TraitImplItem::Function(noir_function) => self.find_in_noir_function(noir_function), - TraitImplItem::Constant(_, _, _) => (), - TraitImplItem::Type { .. } => (), - } - } - - pub(super) fn find_in_type_impl(&mut self, type_impl: &TypeImpl) { - for (method, span) in &type_impl.methods { - if self.includes_span(*span) { - self.find_in_noir_function(method); - } - } - } - - pub(super) fn find_in_noir_trait(&mut self, noir_trait: &NoirTrait) { - for item in &noir_trait.items { - self.find_in_trait_item(item); - } - } - - pub(super) fn find_in_trait_item(&mut self, trait_item: &TraitItem) { - match trait_item { - TraitItem::Function { body, .. } => { - if let Some(body) = body { - self.find_in_block_expression(body); - }; - } - TraitItem::Constant { default_value, .. } => { - if let Some(default_value) = default_value { - self.find_in_expression(default_value); - } - } - TraitItem::Type { .. } => (), - } - } - - pub(super) fn find_in_block_expression(&mut self, block_expression: &BlockExpression) { - for statement in &block_expression.statements { - if self.includes_span(statement.span) { - self.find_in_statement(statement); - } - } - } - - pub(super) fn find_in_statement(&mut self, statement: &Statement) { - if !self.includes_span(statement.span) { - return; - } - - match &statement.kind { - StatementKind::Let(let_statement) => { - self.find_in_let_statement(let_statement); - } - StatementKind::Constrain(constrain_statement) => { - self.find_in_constrain_statement(constrain_statement); - } - StatementKind::Expression(expression) => { - self.find_in_expression(expression); - } - StatementKind::Assign(assign_statement) => { - self.find_in_assign_statement(assign_statement); - } - StatementKind::For(for_loop_statement) => { - self.find_in_for_loop_statement(for_loop_statement); - } - StatementKind::Comptime(statement) => { - self.find_in_statement(statement); - } - StatementKind::Semi(expression) => { - self.find_in_expression(expression); - } - StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), - } - } - - pub(super) fn find_in_let_statement(&mut self, let_statement: &LetStatement) { - self.find_in_expression(&let_statement.expression); - } - - pub(super) fn find_in_constrain_statement(&mut self, constrain_statement: &ConstrainStatement) { - self.find_in_expression(&constrain_statement.0); - - if let Some(exp) = &constrain_statement.1 { - self.find_in_expression(exp); - } - } - - pub(super) fn find_in_assign_statement(&mut self, assign_statement: &AssignStatement) { - self.find_in_lvalue(&assign_statement.lvalue); - self.find_in_expression(&assign_statement.expression); - } - - pub(super) fn find_in_for_loop_statement(&mut self, for_loop_statement: &ForLoopStatement) { - self.find_in_for_range(&for_loop_statement.range); - self.find_in_expression(&for_loop_statement.block); - } - - pub(super) fn find_in_lvalue(&mut self, lvalue: &LValue) { - match lvalue { - LValue::Ident(_) => (), - LValue::MemberAccess { object, field_name: _, span: _ } => self.find_in_lvalue(object), - LValue::Index { array, index, span: _ } => { - self.find_in_lvalue(array); - self.find_in_expression(index); - } - LValue::Dereference(lvalue, _) => self.find_in_lvalue(lvalue), - } - } - - pub(super) fn find_in_for_range(&mut self, for_range: &ForRange) { - match for_range { - ForRange::Range(start, end) => { - self.find_in_expression(start); - self.find_in_expression(end); - } - ForRange::Array(expression) => self.find_in_expression(expression), - } - } - - pub(super) fn find_in_expressions(&mut self, expressions: &[Expression]) { - for expression in expressions { - self.find_in_expression(expression); - } - } - - pub(super) fn find_in_expression(&mut self, expression: &Expression) { - match &expression.kind { - ExpressionKind::Literal(literal) => self.find_in_literal(literal), - ExpressionKind::Block(block_expression) => { - self.find_in_block_expression(block_expression); - } - ExpressionKind::Prefix(prefix_expression) => { - self.find_in_expression(&prefix_expression.rhs); - } - ExpressionKind::Index(index_expression) => { - self.find_in_index_expression(index_expression); - } - ExpressionKind::Call(call_expression) => { - self.find_in_call_expression(call_expression, expression.span); - } - ExpressionKind::MethodCall(method_call_expression) => { - self.find_in_method_call_expression(method_call_expression, expression.span); - } - ExpressionKind::Constructor(constructor_expression) => { - self.find_in_constructor_expression(constructor_expression); - } - ExpressionKind::MemberAccess(member_access_expression) => { - self.find_in_member_access_expression(member_access_expression); - } - ExpressionKind::Cast(cast_expression) => { - self.find_in_cast_expression(cast_expression); - } - ExpressionKind::Infix(infix_expression) => { - self.find_in_infix_expression(infix_expression); - } - ExpressionKind::If(if_expression) => { - self.find_in_if_expression(if_expression); - } - ExpressionKind::Tuple(expressions) => { - self.find_in_expressions(expressions); - } - ExpressionKind::Lambda(lambda) => self.find_in_lambda(lambda), - ExpressionKind::Parenthesized(expression) => { - self.find_in_expression(expression); - } - ExpressionKind::Unquote(expression) => { - self.find_in_expression(expression); - } - ExpressionKind::Comptime(block_expression, _) => { - self.find_in_block_expression(block_expression); - } - ExpressionKind::Unsafe(block_expression, _) => { - self.find_in_block_expression(block_expression); - } - ExpressionKind::Variable(_) - | ExpressionKind::AsTraitPath(_) - | ExpressionKind::Quote(_) - | ExpressionKind::Resolved(_) - | ExpressionKind::Error => (), - } - } - - pub(super) fn find_in_literal(&mut self, literal: &Literal) { - match literal { - Literal::Array(array_literal) => self.find_in_array_literal(array_literal), - Literal::Slice(array_literal) => self.find_in_array_literal(array_literal), - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => (), - } - } - - pub(super) fn find_in_array_literal(&mut self, array_literal: &ArrayLiteral) { - match array_literal { - ArrayLiteral::Standard(expressions) => self.find_in_expressions(expressions), - ArrayLiteral::Repeated { repeated_element, length } => { - self.find_in_expression(repeated_element); - self.find_in_expression(length); - } - } - } - - pub(super) fn find_in_index_expression(&mut self, index_expression: &IndexExpression) { - self.find_in_expression(&index_expression.collection); - self.find_in_expression(&index_expression.index); - } - - pub(super) fn find_in_constructor_expression( - &mut self, - constructor_expression: &ConstructorExpression, - ) { - for (_field_name, expression) in &constructor_expression.fields { - self.find_in_expression(expression); - } - } - - pub(super) fn find_in_member_access_expression( - &mut self, - member_access_expression: &MemberAccessExpression, - ) { - self.find_in_expression(&member_access_expression.lhs); - } - - pub(super) fn find_in_cast_expression(&mut self, cast_expression: &CastExpression) { - self.find_in_expression(&cast_expression.lhs); - } - - pub(super) fn find_in_infix_expression(&mut self, infix_expression: &InfixExpression) { - self.find_in_expression(&infix_expression.lhs); - self.find_in_expression(&infix_expression.rhs); - } - - pub(super) fn find_in_if_expression(&mut self, if_expression: &IfExpression) { - self.find_in_expression(&if_expression.condition); - self.find_in_expression(&if_expression.consequence); - - if let Some(alternative) = &if_expression.alternative { - self.find_in_expression(alternative); - } - } - - pub(super) fn find_in_lambda(&mut self, lambda: &Lambda) { - self.find_in_expression(&lambda.body); - } -} diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index 046eca88099..c5d4bbc9788 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -23,7 +23,7 @@ noirc_printable_type.workspace = true iter-extended.workspace = true thiserror.workspace = true tracing.workspace = true -rayon = "1.8.0" +rayon.workspace = true jsonrpc.workspace = true rand.workspace = true serde.workspace = true diff --git a/noir/noir-repo/tooling/nargo/src/errors.rs b/noir/noir-repo/tooling/nargo/src/errors.rs index b5571ff7758..0b8378702cb 100644 --- a/noir/noir-repo/tooling/nargo/src/errors.rs +++ b/noir/noir-repo/tooling/nargo/src/errors.rs @@ -2,8 +2,8 @@ use std::collections::BTreeMap; use acvm::{ acir::circuit::{ - ErrorSelector, OpcodeLocation, RawAssertionPayload, ResolvedAssertionPayload, - ResolvedOpcodeLocation, + brillig::BrilligFunctionId, ErrorSelector, OpcodeLocation, RawAssertionPayload, + ResolvedAssertionPayload, ResolvedOpcodeLocation, }, pwg::{ErrorLocation, OpcodeResolutionError}, AcirField, FieldElement, @@ -66,7 +66,7 @@ impl NargoError { ) -> Option { match self { NargoError::ExecutionError(error) => match error { - ExecutionError::AssertionFailed(payload, _) => match payload { + ExecutionError::AssertionFailed(payload, _, _) => match payload { ResolvedAssertionPayload::String(message) => Some(message.to_string()), ResolvedAssertionPayload::Raw(raw) => { let abi_type = error_types.get(&raw.selector)?; @@ -90,7 +90,11 @@ impl NargoError { #[derive(Debug, Error)] pub enum ExecutionError { #[error("Failed assertion")] - AssertionFailed(ResolvedAssertionPayload, Vec), + AssertionFailed( + ResolvedAssertionPayload, + Vec, + Option, + ), #[error("Failed to solve program: '{}'", .0)] SolvingError(OpcodeResolutionError, Option>), @@ -106,7 +110,7 @@ fn extract_locations_from_error( OpcodeResolutionError::BrilligFunctionFailed { .. }, acir_call_stack, ) => acir_call_stack.clone(), - ExecutionError::AssertionFailed(_, call_stack) => Some(call_stack.clone()), + ExecutionError::AssertionFailed(_, call_stack, _) => Some(call_stack.clone()), ExecutionError::SolvingError( OpcodeResolutionError::IndexOutOfBounds { opcode_location: error_location, .. }, acir_call_stack, @@ -148,6 +152,7 @@ fn extract_locations_from_error( OpcodeResolutionError::BrilligFunctionFailed { function_id, .. }, _, ) => Some(*function_id), + ExecutionError::AssertionFailed(_, _, function_id) => *function_id, _ => None, }; @@ -187,6 +192,7 @@ fn extract_message_from_error( NargoError::ExecutionError(ExecutionError::AssertionFailed( ResolvedAssertionPayload::String(message), _, + _, )) => { format!("Assertion failed: '{message}'") } diff --git a/noir/noir-repo/tooling/nargo/src/ops/execute.rs b/noir/noir-repo/tooling/nargo/src/ops/execute.rs index 5a43b1c2f9c..59d554d7ca5 100644 --- a/noir/noir-repo/tooling/nargo/src/ops/execute.rs +++ b/noir/noir-repo/tooling/nargo/src/ops/execute.rs @@ -117,10 +117,18 @@ impl<'a, F: AcirField, B: BlackBoxFunctionSolver, E: ForeignCallExecutor> _ => None, }; + let brillig_function_id = match &error { + OpcodeResolutionError::BrilligFunctionFailed { function_id, .. } => { + Some(*function_id) + } + _ => None, + }; + return Err(NargoError::ExecutionError(match assertion_payload { Some(payload) => ExecutionError::AssertionFailed( payload, call_stack.expect("Should have call stack for an assertion failure"), + brillig_function_id, ), None => ExecutionError::SolvingError(error, call_stack), })); diff --git a/noir/noir-repo/tooling/nargo/src/package.rs b/noir/noir-repo/tooling/nargo/src/package.rs index f55ca5550a3..cde616a9e32 100644 --- a/noir/noir-repo/tooling/nargo/src/package.rs +++ b/noir/noir-repo/tooling/nargo/src/package.rs @@ -73,4 +73,11 @@ impl Package { pub fn is_library(&self) -> bool { self.package_type == PackageType::Library } + + pub fn error_on_unused_imports(&self) -> bool { + match self.package_type { + PackageType::Library => false, + PackageType::Binary | PackageType::Contract => true, + } + } } diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index f3d9f92caaa..284be56d247 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -31,7 +31,7 @@ nargo_fmt.workspace = true nargo_toml.workspace = true noir_lsp.workspace = true noir_debugger.workspace = true -noirc_driver.workspace = true +noirc_driver = { workspace = true, features = ["bn254"] } noirc_frontend = { workspace = true, features = ["bn254"] } noirc_abi.workspace = true noirc_errors.workspace = true @@ -42,7 +42,7 @@ toml.workspace = true serde.workspace = true serde_json.workspace = true prettytable-rs = "0.10" -rayon = "1.8.0" +rayon.workspace = true thiserror.workspace = true tower.workspace = true async-lsp = { workspace = true, features = ["client-monitor", "stdio", "tracing", "tokio"] } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index 5239070b4d2..1130a82fdfc 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs @@ -10,7 +10,7 @@ use nargo::{ use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; use noirc_driver::{ - check_crate, compute_function_abi, file_manager_with_stdlib, CompileOptions, + check_crate, compute_function_abi, file_manager_with_stdlib, CheckOptions, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::{ @@ -81,7 +81,9 @@ fn check_package( allow_overwrite: bool, ) -> Result { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate_and_report_errors(&mut context, crate_id, compile_options)?; + let error_on_unused_imports = package.error_on_unused_imports(); + let check_options = CheckOptions::new(compile_options, error_on_unused_imports); + check_crate_and_report_errors(&mut context, crate_id, &check_options)?; if package.is_library() || package.is_contract() { // Libraries do not have ABIs while contracts have many, so we cannot generate a `Prover.toml` file. @@ -150,9 +152,10 @@ fn create_input_toml_template( pub(crate) fn check_crate_and_report_errors( context: &mut Context, crate_id: CrateId, - options: &CompileOptions, + check_options: &CheckOptions, ) -> Result<(), CompileError> { - let result = check_crate(context, crate_id, options); + let options = &check_options.compile_options; + let result = check_crate(context, crate_id, check_options); report_errors(result, &context.file_manager, options.deny_warnings, options.silence_warnings) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs index 325641d0c5b..85faf574a0a 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -200,14 +200,11 @@ fn compile_programs( Ok(((), warnings)) }; - let program_results: Vec> = if compile_options.sequential { - binary_packages.iter().map(compile_package).collect() - } else { - // Configure a thread pool with a larger stack size to prevent overflowing stack in large programs. - // Default is 2MB. - let pool = rayon::ThreadPoolBuilder::new().stack_size(4 * 1024 * 1024).build().unwrap(); - pool.install(|| binary_packages.par_iter().map(compile_package).collect()) - }; + // Configure a thread pool with a larger stack size to prevent overflowing stack in large programs. + // Default is 2MB. + let pool = rayon::ThreadPoolBuilder::new().stack_size(4 * 1024 * 1024).build().unwrap(); + let program_results: Vec> = + pool.install(|| binary_packages.par_iter().map(compile_package).collect()); // Collate any warnings/errors which were encountered during compilation. collect_errors(program_results).map(|(_, warnings)| ((), warnings)) diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs index 19add7f30dc..5721dd33e27 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs @@ -12,7 +12,7 @@ use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ - compile_no_check, file_manager_with_stdlib, CompileOptions, CompiledProgram, + compile_no_check, file_manager_with_stdlib, CheckOptions, CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; @@ -83,7 +83,9 @@ fn compile_exported_functions( compile_options: &CompileOptions, ) -> Result<(), CliError> { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate_and_report_errors(&mut context, crate_id, compile_options)?; + let error_on_unused_imports = package.error_on_unused_imports(); + let check_options = CheckOptions::new(compile_options, error_on_unused_imports); + check_crate_and_report_errors(&mut context, crate_id, &check_options)?; let exported_functions = context.get_all_exported_functions_in_crate(&crate_id); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs index dee9a00507c..4a7a81431bb 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/fs/inputs.rs @@ -25,7 +25,13 @@ pub(crate) fn read_inputs_from_file>( let file_path = path.as_ref().join(file_name).with_extension(format.ext()); if !file_path.exists() { - return Err(FilesystemError::MissingTomlFile(file_name.to_owned(), file_path)); + if abi.parameters.is_empty() { + // Reading a return value from the `Prover.toml` is optional, + // so if the ABI has no parameters we can skip reading the file if it doesn't exist. + return Ok((BTreeMap::new(), None)); + } else { + return Err(FilesystemError::MissingTomlFile(file_name.to_owned(), file_path)); + } } let input_string = std::fs::read_to_string(file_path).unwrap(); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs index 92568c10f81..751d49e6427 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs @@ -10,7 +10,8 @@ use nargo::{ }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ - check_crate, file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, + check_crate, file_manager_with_stdlib, CheckOptions, CompileOptions, + NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::{ graph::CrateName, @@ -185,7 +186,9 @@ fn run_test + Default>( // We then need to construct a separate copy for each test. let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate(&mut context, crate_id, compile_options) + let error_on_unused_imports = package.error_on_unused_imports(); + let check_options = CheckOptions::new(compile_options, error_on_unused_imports); + check_crate(&mut context, crate_id, &check_options) .expect("Any errors should have occurred when collecting test functions"); let test_functions = context @@ -211,10 +214,12 @@ fn get_tests_in_package( parsed_files: &ParsedFiles, package: &Package, fn_name: FunctionNameMatch, - compile_options: &CompileOptions, + options: &CompileOptions, ) -> Result, CliError> { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate_and_report_errors(&mut context, crate_id, compile_options)?; + let error_on_unused_imports = package.error_on_unused_imports(); + let check_options = CheckOptions::new(options, error_on_unused_imports); + check_crate_and_report_errors(&mut context, crate_id, &check_options)?; Ok(context .get_all_test_functions_in_crate_matching(&crate_id, fn_name) diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs index 4fee7d3e197..caa60b17cc2 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs @@ -175,6 +175,9 @@ pub(crate) fn rewrite( ExpressionKind::Resolved(_) => { unreachable!("ExpressionKind::Resolved should only emitted by the comptime interpreter") } + ExpressionKind::Interned(_) => { + unreachable!("ExpressionKind::Interned should only emitted by the comptime interpreter") + } ExpressionKind::Unquote(expr) => { if matches!(&expr.kind, ExpressionKind::Variable(..)) { format!("${expr}") diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs index 8d1e27078a8..6121f8debf6 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs @@ -73,6 +73,6 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) | UnresolvedTypeData::FormatString(_, _) | UnresolvedTypeData::Quoted(_) | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span).into(), - UnresolvedTypeData::Error => unreachable!(), + UnresolvedTypeData::Interned(_) | UnresolvedTypeData::Error => unreachable!(), } } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs index 8e05fe3f5c5..b5ac14a33b3 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs @@ -104,6 +104,9 @@ impl super::FmtVisitor<'_> { StatementKind::Break => self.push_rewrite("break;".into(), span), StatementKind::Continue => self.push_rewrite("continue;".into(), span), StatementKind::Comptime(statement) => self.visit_stmt(statement.kind, span, is_last), + StatementKind::Interned(_) => unreachable!( + "StatementKind::Resolved should only emitted by the comptime interpreter" + ), } } } diff --git a/noir/verify_honk_proof/Prover.toml b/noir/verify_honk_proof/Prover.toml index 6e5c36559bd..5bdebb476be 100644 --- a/noir/verify_honk_proof/Prover.toml +++ b/noir/verify_honk_proof/Prover.toml @@ -1,4 +1,575 @@ -key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" -proof = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632","0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc","0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62","0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c","0x000000000000000000000000000000b0804efd6573805f991458295f510a2004","0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e","0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47","0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15","0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd","0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383","0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4","0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x000000000000000000000000000000f968b227a358a305607f3efc933823d288","0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08","0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f","0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1","0x0000000000000000000000000000005b739ed2075f2b046062b8fc6a2d1e9863","0x00000000000000000000000000000000001285cd1030d338c0e1603b4da2c838","0x00000000000000000000000000000027447d6c281eb38b2b937af4a516d60c04","0x000000000000000000000000000000000019bc3d980465fbb4a656a74296fc58","0x000000000000000000000000000000b484788ace8f7df86dd5e325d2e9b12599","0x00000000000000000000000000000000000a2ca0d10eb7b767114ae230b728d3","0x000000000000000000000000000000c6dfc7092f16f95795e437664498b88d53","0x0000000000000000000000000000000000131067b4e4d95a4f6f8cf5c9b5450a","0x0f413f22eec51f2a02800e0cafaeec1d92d744fbbaef213c687b9edabd6985f5","0x21230f4ff26c80ffb5d037a9d1d26c3f955ca34cbeca4f54db6656b932967a0c","0x0521f877fe35535767f99597cc50effbd283dcae6812ee0a7620d796ccbfd642","0x202b01350a9cc5c20ec0f3eaada338c0a3b793811bd539418ffa3cc4302615e2","0x2d1214d9b0d41058ad4a172d9c0aecc5bdabe95e687c3465050c6b5396509be4","0x1113b344a151b0af091cb28d728b752ebb4865da6cd7ee68471b961ca5cf69b9","0x2aa66d0954bb83e17bd5c9928d3aa7a7df75d741d409f7c15ba596804ba643fb","0x2e26bc7a530771ef7a95d5360d537e41cf94d8a0942764ff09881c107f91a106","0x0f14f32b921bb63ad1df00adab7c82af58ea8aa7f353f14b281208d8c5fab504","0x13429515c0c53b6502bbcdf545defb3cb69a986c9263e070fcbb397391aae1a3","0x1f21cac5e2f262afc1006a21454cc6bcb018c44e53ad8ab61cebbac99e539176","0x2a9886a6ddc8a61b097c668cd362fc8acdee8dde74f7b1af192c3e060bb2948f","0x2d718181e408ead2e9bcd30a84ad1fccbaf8d48ab6d1820bad4933d284b503c4","0x2634c1aafc902f14508f34d3d7e9d485f42d1a4c95b5a1ef73711ed0d3c68d77","0x092ede9777e6472ce5ffd8c963d466006189e960e2c591d338dc8d4af1a057fb","0x1cba45b17fd24f1cb1b4ab7b83eee741f6c77ba70a497dc4de259eceb7d5ea26","0x246e887c7bf2e17f919b2393b6e9b00b33e8822d862544a775aac05cb7bff710","0x04c3f539fe8689971948afcb437f1ecbd444a5bddaca1c8a450348dcd8480047","0x20c6a423ae4fd58e8951aa378d02d77baf90508ceb48856db2319d70938b186e","0x1bcf8786b554b3316d8ebdbc9d006a4e5d4865aad512ffd404b7f83550d3d030","0x09ab038260518f0970564afcd6bf22e2abf6b1fa5e12a327bbf195b6ca5edd78","0x1024e32554746f89c195286ba6ccfc9765e5d14bbe8064bc6fdf22d16ec6b495","0x17706656f8dbd7e47bb257a6428f0cb7278ea02fa9e6ce431d7bcc9133fba9c7","0x25a3e8a33c15ef2a4dd16313a6049bf1d468b4cdc141f238f2d51a1e8e1c22b3","0x1198863f08006edb27aee23164fb117a4ddec1bf1ed89807aa907e5cd24bf068","0x1862b4856b5b4d4a064f873e221703e4e2cd1ebfca1337dedca56485c38ed5a0","0x062214af1ea6dd6bf8895b92d394571c43970b6f967e1c794624d96071b25ad3","0x1e5be9428ddcf1f9b0cbafc28101e792ec5cf73852b0cd0b84fbff71b4490e09","0x2d4189bea5b1e30f63c64bd26df82f18bcaf885ec8887b54634b2557869ce87f","0x0f2e5d9a908850e9d44925e17d8b12d1adb1ed029799c9b5858598504242bbc0","0x3050dc85746a57931d99f3f35e77c2ba561fba0baa018b79ff1fd544026833ae","0x2a591a32437e5e0b875a137fd868bd1b6dbc003ff1b661f26e00627cc7c5cf47","0x27946841e1670ad9c65717016d0cedf524724217236e81b9fd0a264a36ebfb0e","0x0fc396e9d19d6e68e289602e292ee345542d0d28bf6de34fa62cc577cbdfb1df","0x08e7433a07a44c0c9c4dd4b273a2685bbd1a91fd5cf2b43409458fab42a23e1b","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x12bd9bfb029c3503a5c6deea87b0a0f11bb9f7ea584af2d48f3e48d7e09247ae","0x2ccc4810748c0a82dfc0f063d0b8c7999ffe9474653080e6ef92b3cb7a428784","0x08eb574d7fecadadb508c8bd35fdad06b99110609d679763c2e3645229b1b95a","0x0f1a65e747c8021ed7c454a4be1e89b1bce66ead9ed980fa98a7a050eafe98a1","0x1c8ff9e36684ec71614dee4c17859b06c742089f6029d3694a16e00dac9b57f1","0x0303101a8ba712aeca4da85b767ab8d3ecf489ec7d746f8ee20041717cc000e9","0x0aaf64c65e7088e5596108c9601467911fea809ca6540d79af77e6e66e36cd99","0x17caf164ce74ea7edfb1390e07763d2197797ec26661b92cde18a98d61d2fddc","0x18cb055c7ad6d01437725bb457681d81f3ecadc4f35d838a3c13daf25a44456a","0x2d78602b8bbcd32b36a99a6e2d248e7fe044ef1b50813133370412f9ef5299f0","0x2b139276ea86d426a115479e4154f72a6bd83a6253bf13e9670dc6b4664378f0","0x127c7837b384902c39a104036c09546728571c46c8166b1b9b13b3a615ebb781","0x05faa4816f83cf0189a482ad943c94b9ec6474002f2b327f8698763ad0ea0985","0x2f90359cc30ee693fb3aced96523cf7aebd152c22329eee56a398d9a4ac0628e","0x0a71beaf17a59c5a238f04c1f203848d87502c5057a78c13f0cfb0f9876e7714","0x2696c1e6d089556adaeb95c8a5e3065b00a393a38c2d69e9bd6ce8cdc49d87da","0x1f3d165a7dc6564a036e451eb9cb7f1e1cb1e6d29daa75e3f135ea3e58a79ccd","0x1473a660819bdd838d56122b72b32b267211e9f1103239480ec50fa85c9e1035","0x0a8ccaeb22451f391b3fc3467c8e6e900270a7afb7b510e8acf5a4f06f1c0888","0x03b3080afc0658cc87e307758cebc171921f43eca159b9dedf7f72aa8dd926bd","0x2dd7d6663fa0e1755dfafac352c361fcd64c7f4d53627e3646870ac169cc4a07","0x1ec54b883f5f35ccad0e75695af20790d9860104095bab34c9bf01628dd40cb9","0x193dff50f83c241f7a9e087a29ce72ecf3f6d8563593f786dcd04c32bcfd4ced","0x135122c0dae26cda8ca1c09de8225064ad86d10423ab0aaa53b481aa4626e1d6","0x08d5a56cbfab5aeed56d3cdd7fb6b30fc26b0c1a5b63fccd7fa44c53ba6fd35a","0x0d12f126dfa2daad3726d00ca339284cc22e36c6d81bb7a4b95c6f9598b60e7c","0x2e8b24bbdf2fd839d3c7cae1f0eeb96bfcfaeef30b27476f2fafcb17da78cd5e","0x2364acfe0cea39b7f749c5f303b99504977357925f810f684c60f35d16315211","0x06ca062eb70b8c51cfac35345e7b6b51f33a8ec9ebe204fb9b4911200bf508b7","0x266c0aa1ccb97186815bf69084f600d06ddd934e59a38dfe602ee5d6b9487f22","0x1d817537a49c6d0e3b4b65c6665334b91d7593142e60065048be9e55ceb5e7ab","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x25b77026673a1e613e50df0e88fb510973739d5f9064bd364079a9f884209632","0x25c9bc7a3f6aae3d43ff68b5614b34b5eaceff37157b37347995d231784ac1fd","0x085f69baef22680ae15f4801ef4361ebe9c7fc24a94b5bc2527dce8fb705439e","0x0d7c6b9ce31bfc32238a205455baf5ffe99cd30eb0f7bb5b504e1d4501e01382","0x1001a8cc4bc1221c814fba0eddcf3c40619b133373640c600de5bed0a0a05b10","0x20f5894be90e52977cb70f4f4cbd5101693db0360848939750db7e91109d54b6","0x22c09cb26db43f0599408b4daed0f4f496c66424e6affa41c14387d8e0af851b","0x24e5f41357798432426a9549d71e8cc681eaebacbe87f6e3bf38e85de5aa2f3d","0x06eb90100c736fbf2b87432d7821ecdc0b365024739bc36363d48b905973f5b9","0x000000000000000000000000000000ece6d09ed58e9f5661c01140b10558a8c2","0x000000000000000000000000000000000012b6e4f37adcb34b8e88ff8b6eebce","0x000000000000000000000000000000b226a2bb93593fa1fab19a44767828a3f5","0x00000000000000000000000000000000002b5b518342030543092e1428a7e33c","0x00000000000000000000000000000022ba33857034a0574c216eb3c1ddff3025","0x00000000000000000000000000000000001918e58df857985a7cf9eae7802165","0x00000000000000000000000000000045c2d840b96fb6106cc14dcad89dd5f675","0x00000000000000000000000000000000000afdfac1e3a1febdd0208867d44f98","0x00000000000000000000000000000042ebed6c5ec45d794f119aef24c192af0f","0x00000000000000000000000000000000002d05ef250900bbcc5751bbeb210d6a","0x00000000000000000000000000000060d604bdda48eecc90ed065bd9770e1323","0x00000000000000000000000000000000001fed91c63d0041660c1cbc84c2ffbb","0x00000000000000000000000000000054196b549cde36092e8184c7f4f7d878de","0x00000000000000000000000000000000000153f26a01294329922b492485cc31","0x00000000000000000000000000000056ebea579d10dbb440f0222931df2c0059","0x00000000000000000000000000000000000d2cbc61ce5b7cdd7fce398da4637b","0x000000000000000000000000000000e2b9512360b9797d96675d8a2fd2f7aa5d","0x000000000000000000000000000000000025742905f105ff895f74e7c3daa34a","0x000000000000000000000000000000a2dd7df55db59bd41b83518d4403fbc382","0x00000000000000000000000000000000002c1d9c3cbb9371d4cc4e9f900b9a46","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000bcf12ae40c9425c3e67654b84181f90502","0x00000000000000000000000000000000000b6d3faa8a71ff6ef1aa887b7307cf","0x0000000000000000000000000000001f6f719acc23b8f84808c0275d61cfb456","0x0000000000000000000000000000000000296030933ed0c134457ae71c393dfe","0x000000000000000000000000000000ebe1a57cdd7d3d763289b40ef5ed9a7ae0","0x000000000000000000000000000000000010f30483e7df51fca2316d3367603c","0x0000000000000000000000000000000149b7b283ab18060618c8e051864c03cd","0x00000000000000000000000000000000001ef7763235a3a25e241a5f06704dc3"] -public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] -verification_key = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000000000000000000000000000000000000004","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000006","0x0000000000000000000000000000000000000000000000000000000000000007","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000009","0x000000000000000000000000000000000000000000000000000000000000000a","0x000000000000000000000000000000000000000000000000000000000000000b","0x000000000000000000000000000000000000000000000000000000000000000c","0x000000000000000000000000000000000000000000000000000000000000000d","0x000000000000000000000000000000000000000000000000000000000000000e","0x000000000000000000000000000000000000000000000000000000000000000f","0x0000000000000000000000000000000000000000000000000000000000000010","0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84","0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae","0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16","0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1","0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c","0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7","0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8","0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c","0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5","0x00000000000000000000000000000000002002681bb417184b2df070a16a3858","0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511","0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223","0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7","0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c","0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130","0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f","0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3","0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592","0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3","0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1","0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0","0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c","0x0000000000000000000000000000009f825dde88092070747180d581c342444a","0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01","0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff","0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9","0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1","0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b","0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2","0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f","0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0","0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349","0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8","0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2","0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556","0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d","0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb","0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d","0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8","0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862","0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e","0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830","0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f","0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe","0x000000000000000000000000000000231147211b3c75e1f47d150e4bbd2fb22e","0x00000000000000000000000000000000000d19ee104a10d3c701cfd87473cbbe","0x0000000000000000000000000000006705f3f382637d00f698e2c5c94ed05ae9","0x00000000000000000000000000000000000b9c792da28bb60601dd7ce4b74e68","0x000000000000000000000000000000ac5acc8cc21e4ddb225c510670f80c80b3","0x00000000000000000000000000000000002da9d3fa57343e6998aba19429b9fa","0x0000000000000000000000000000004bacbf54b7c17a560df0af18b6d0d527be","0x00000000000000000000000000000000000faea33aeca2025b22c288964b21eb","0x000000000000000000000000000000492e756298d68d6e95de096055cc0336c3","0x00000000000000000000000000000000001a12a12f004859e5a3675c7315121b","0x000000000000000000000000000000893d521d512f30e6d32afbbc0cecd8ee00","0x00000000000000000000000000000000001674b3c1ef12c6da690631e0d86c04","0x000000000000000000000000000000aa6cb02a52e7a613873d4ac9b411349945","0x00000000000000000000000000000000001ecb1fe9c493add46751f9940f73e1","0x00000000000000000000000000000045b3d362ca82cba69fb2b9c733a5b8c351","0x000000000000000000000000000000000019a683586af466e331945b732d2f8c","0x000000000000000000000000000000fc79b052dfdfe67c0ecfc06b4267ffd694","0x00000000000000000000000000000000001336a70c396393038d5e9913744ac2","0x0000000000000000000000000000005450d29af1e9438e91cd33ddeb2548226e","0x000000000000000000000000000000000000993a602891cfd0e6f6ecf7404933","0x000000000000000000000000000000498efddab90a32e9b2db729ed6e9b40192","0x00000000000000000000000000000000002425efebe9628c63ca6fc28bdb5901","0x000000000000000000000000000000d8488157f875a21ab5f93f1c2b641f3de9","0x0000000000000000000000000000000000290f95ada3936604dc4b14df7504e3","0x0000000000000000000000000000005d6902187f3ed60dcce06fca211b40329a","0x00000000000000000000000000000000002b5870a6ba0b20aaa0178e5adfbc36","0x000000000000000000000000000000e5c2519171fa0e548fc3c4966ffc1ce570","0x00000000000000000000000000000000001cb8d8f4793b7debbdc429389dbf2d","0x000000000000000000000000000000a3ee22dd60456277b86c32a18982dcb185","0x00000000000000000000000000000000002493c99a3d068b03f8f2b8d28b57ce","0x000000000000000000000000000000f6c3731486320082c20ec71bbdc92196c1","0x00000000000000000000000000000000001ded39c4c8366469843cd63f09ecac","0x000000000000000000000000000000494997477ab161763e46601d95844837ef","0x00000000000000000000000000000000002e0cddbc5712d79b59cb3b41ebbcdd","0x000000000000000000000000000000426db4c64531d350750df62dbbc41a1bd9","0x0000000000000000000000000000000000303126892f664d8d505964d14315ec","0x00000000000000000000000000000076a6b2c6040c0c62bd59acfe3e3e125672","0x000000000000000000000000000000000000874a5ad262eecc6b565e0b085074","0x000000000000000000000000000000ef082fb517183c9c6841c2b8ef2ca1df04","0x0000000000000000000000000000000000127b2a745a1b74968c3edc18982b9b","0x000000000000000000000000000000c9efd4f8c3d56e1eb23d789a8f710d5be6","0x000000000000000000000000000000000015a18748490ff4c2b1871081954e86","0x000000000000000000000000000000a0011ef987dc016ab110eacd554a1d8bbf","0x00000000000000000000000000000000002097c84955059442a95df075833071","0x000000000000000000000000000000d38e9426ad3085b68b00a93c17897c2877","0x00000000000000000000000000000000002aecd48089890ea0798eb952c66824","0x00000000000000000000000000000078d8a9ce405ce559f441f2e71477ff3ddb","0x00000000000000000000000000000000001216bdb2f0d961bb8a7a23331d2150","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb","0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56","0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc","0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4"] +key_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" +proof = [ + "0x0000000000000000000000000000000000000000000000000000000000000040", + "0x0000000000000000000000000000000000000000000000000000000000000011", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf", + "0x00000000000000000000000000000000000000000000000b75c020998797da78", + "0x0000000000000000000000000000000000000000000000005a107acb64952eca", + "0x000000000000000000000000000000000000000000000000000031e97a575e9d", + "0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4", + "0x00000000000000000000000000000000000000000000000c410db10a01750aeb", + "0x00000000000000000000000000000000000000000000000d722669117f9758a4", + "0x000000000000000000000000000000000000000000000000000178cbf4206471", + "0x000000000000000000000000000000000000000000000000e91b8a11e7842c38", + "0x000000000000000000000000000000000000000000000007fd51009034b3357f", + "0x000000000000000000000000000000000000000000000009889939f81e9c7402", + "0x0000000000000000000000000000000000000000000000000000f94656a2ca48", + "0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f", + "0x0000000000000000000000000000000000000000000000093fe27776f50224bd", + "0x000000000000000000000000000000000000000000000004a0c80c0da527a081", + "0x0000000000000000000000000000000000000000000000000001b52c2020d746", + "0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632", + "0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc", + "0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62", + "0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c", + "0x000000000000000000000000000000b0804efd6573805f991458295f510a2004", + "0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e", + "0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47", + "0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15", + "0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd", + "0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383", + "0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4", + "0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98", + "0x0000000000000000000000000000006cf7dd96d7636fda5953191b1ad776d491", + "0x00000000000000000000000000000000001633d881a08d136e834cb13a28fcc6", + "0x00000000000000000000000000000001254956cff6908b069fca0e6cf1c47eb1", + "0x000000000000000000000000000000000006f4d4dd3890e997e75e75886bf8f7", + "0x0000000000000000000000000000006cf7dd96d7636fda5953191b1ad776d491", + "0x00000000000000000000000000000000001633d881a08d136e834cb13a28fcc6", + "0x00000000000000000000000000000001254956cff6908b069fca0e6cf1c47eb1", + "0x000000000000000000000000000000000006f4d4dd3890e997e75e75886bf8f7", + "0x000000000000000000000000000000f968b227a358a305607f3efc933823d288", + "0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08", + "0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f", + "0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1", + "0x000000000000000000000000000000286fcda0e28617c86e195005b9f2efc555", + "0x00000000000000000000000000000000000dc409eb684b23f6a97175bcb9b486", + "0x000000000000000000000000000000e8de6a193cd36414f598bc7c48d67c3b59", + "0x00000000000000000000000000000000002a8a791544cad8c712de871e3de50a", + "0x000000000000000000000000000000d6f1e64b562df0f17ecc6aa46392f8d5a3", + "0x00000000000000000000000000000000000aac977763f33fd6a360ccc50a827a", + "0x000000000000000000000000000000899fa957f5597c6419e3ead9843d21d917", + "0x000000000000000000000000000000000016c4611846952bd6833c35fb11c0da", + "0x013dbfbfbfb2ae7d524edb15343e551d9510b3116223baaa67312d17652f2fb1", + "0x2f268eb3217ef1ac66016aa14d43033f932335371795b5e6dcb0c87c8ad0d050", + "0x2d5dbd52e00ae837e9868289fbe9057f16ea5b76c7e362603e8883f0de4b3e94", + "0x0e357b6a266c20d5e546c2931475eb044d7e75e08ec31b5e8623aec30f964323", + "0x0a9ace4dea44d0a2e8d12d495a683f508714356656aea3882436b729ead24165", + "0x0c17102a98ccb76faf0f78d669ee9cfb694849896787c985225d92e1af3cab35", + "0x09cc7cb719deb139c84fd9fa273e862a1b5d1cec2501c6cd8ba3c37ca06ac07f", + "0x15a0369f3f95d53687dfe79483baf75597d8b281fe0595caf1f7c9ccf99d985e", + "0x17fb53a42b3d1fa5d26ab19dfcc0d74d1781cee0be98dcc492c22e8f3442c4db", + "0x291d6810fc6afc5c2254fd283843a74c85a77275eee3049ea8ed9c88e02a99b8", + "0x0ad40d1627c31247dfb894584a71f8599cfcb85afe84b20186fc07fccae1aa4a", + "0x251cd908fb4e9fe88660f2303f8d7e4d7886da32fddc0319a842b99543659c0b", + "0x1885bdea3dd82085ca67502ebec8ad87213493e18a06cfa27e2c69810481b4a7", + "0x239ab5ba86866bc6705091f82a6a29444dc76b0e7d94cede7eb745cce36ab2cf", + "0x088d29a03baa491845d152124189dfb8bf70ba9bf1fb00c379199dbb0195c663", + "0x18c9fbe3227988d2da599eba82d60f4de25b442b663585fdc611e37305fa77fc", + "0x010242ae641a8cc4d06b5d24e38d9fa6254f981e28f238ccf6aad580f780d3f5", + "0x00128d34b122e84d7e23276b1f13f5789a562e82c727e9ffcfd7bbaccbe69e04", + "0x0776defaf478bfea4db2698542314e27213f63c96e41f98d4d82a47ed6fab55d", + "0x273014a360eaaa493e398df82f18d9cae37f4b6c0ead20100cad3f5491805298", + "0x2b13528eb9ab6fa705f2b48c9ec6ce054ac984e3adf17d4d73431e8456bf4a3c", + "0x22dafe1d63e39cd2effb236da2e131ee1c8cf4049ce504431dcaf98f75c47ad8", + "0x1afb5bc7eb8d30d807101357bb290f9c3113523f4aacc1154a27b075e46a4fa4", + "0x0782dd7df679163e5f0c126abc901d00f3d7d0856b4c02a199ab691ecd7566e6", + "0x2e556c722c99a84a09ffdcc719178277f8e6c9e31a4769270e3b522b944b8ea2", + "0x1be933a48dca8ef26202d3f135998ac8bed6947020b7447ffb6033b0e37f2065", + "0x2d8ebae210848de2464f5435f1fd4b5467ee938910d7779002614943060bbb32", + "0x2da854bbee38a94a6a9c2c85dd05bb4c879173720b67f93f78b9de93cdb427b0", + "0x0fa2649472af2e79489c466b58002f8f284f953085ac0a98dfabee85b78f63cf", + "0x304a09437636026ef0746c4b8ac1ca0ff250c5630fb5bd03ddafddd7cbde850e", + "0x0c83bb3c6ee0faa1646ee4d8dd83f67ec98e5d63ac802f7bdebfcdf21dee62f1", + "0x229d7e4524b30c18a6b94f0054e6d2ea8eb2396f58f9c808d2c9f991e2be2399", + "0x1265bf5e1aaddeae09242b1435e2f8f9e7487bf76a0461752777f6ea1ff75ad6", + "0x2f32f53281b7a363d6bec84ca21c71c3206d906b036e8b36b0702780e3b1b870", + "0x017fb18c9aef4d6d2bc99f5d7f9a002c8921fcd7c7ba69bf05930b55c2829cb7", + "0x2ec761c02ef6f2eefb7c9b2d6df71795d0ce0820f86797e2e11415cb5b122f22", + "0x2b1722960f42a1b40ffae3e4b9419fc8ff5cb8139a2c7e89af332ba2e95c1b5f", + "0x2dafa15594da2318245475c77eae3712429226b3005852e70f567efff0a7d79a", + "0x2ed44d7e3d5f44ac8f7c144ee0ba9d369c82428827c19b010384708bbc52a3f9", + "0x2777eedda697c7f90aee44fa97cfe62596d16c43fa3545b48d622023ca7a446a", + "0x1a47a5c1b0f41905aa0bad6248be8c7887ddea3ad9dfc8462b23a95b073c8a49", + "0x093656d571e84ac676a265dd509c98513039552b7a24e001b003ca618cc4ea5c", + "0x15c901e8a7ff0f1ae1989b5cfb687975c16716a8014a4052d527d4db4ecbaeb4", + "0x08bfa20e83096b0be58e4c96232510c8ef9824c0a62b91ffcc4592b217753a72", + "0x021913efbdfbc73aa5f4a97c79f352ac61f71248947f5eb5713c1b107c632703", + "0x00df89625aef270fab2a8c33ba742e1375423f4cfb3f63514ae748e004bb8cf4", + "0x2455f76c8ee59e93cbe7fe192cf0f766e1399617cabfa230cf27ca2a18cd58d5", + "0x150c3e56ea4f6442ed6b11030c98682a8f5e3c9cd6fd18949254a7c79b3cb5b6", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x01e89c6fe644aea7f63301278dbdb4ea29cf4d33f8b0cdcd79cb106e0bf0a753", + "0x2d49d23421e253903b8a5d0911642b9ce218cef4e350cf8689704eb1f3ae38d4", + "0x072956ca447343d788791fee1ef222f280048ad4aefb6cb7bc96b538f482f525", + "0x168176bf15c8ca63457acf05efbe54af452ea41f935ab82c2a96fedde10ba52f", + "0x20a13690f13491f7f3b756a1dc3b69a3f96d78355c70289583032a593bfc87bc", + "0x273e0a32ab3ef0d3f179b62520b31015ccfc8b53c76a1bb323b41e40ff954596", + "0x28019d4b05546b44e35d5dc74375b75dabb6fae49a07381605c60423c6163d26", + "0x10beda0b8dd484c63f0937820e2c7e9be832a0031efe3557631608895255ca5f", + "0x095a8f04a901526e4d70b1560bfff29b5a3c30347725d1e420c1b30ee2bf8a1c", + "0x1fb742e863a5c76262ffec93b3351405b0840b326fa5fffd73f40abcd5f05f05", + "0x11fa63cfcb2e603fe4e4668d75f05a2cf22650b84a91d1753e83f0e7ae83b4ad", + "0x2872e3d3c431a8b7ee4cec1c2a999a42c40ae33382fbba80a6d4c1a39b2d57a3", + "0x17e8c2a5f809f9935d7c6d7cb2f8859a513864b53f53de3d2a14c74cd690bd1a", + "0x20a552298d691393ae401382b3015689231ad988d3eb0521d414dcd2e8781053", + "0x183eb6bca59a141b4e8136179a258272ec9c25ec80bdb0458b6880c711707a28", + "0x03cd147a2a4c8dc272f3e240b8b0090d45e994e5fd40e07a54f6765795cd5ef8", + "0x082b135b3a20da4c766242b4258e27dbc050e4b8958bb15431626f2eeed9bd2b", + "0x28c894a6a719a32fe8d78ded46bc685ba035e5579c88fbc5bcbc0f09d8c5268b", + "0x06418cceff50837f923e63a37c2c534d13d9f59793c3aa6274813baa64d1899e", + "0x2b4a27b672f85c4fc697605da213de8b950a629602c5b8c6403e6c1c1065388a", + "0x0e2b817c6a79d6d1027f0376fb26ec81a140a4402e2dcdff6152cf01f2f4dbf9", + "0x2ae0fbce87dc53f0ff5473117e1c49a8197a14f8eaaec00cb5b10f94e844111f", + "0x2368004a1dee06f505e75ada3e9f8cc4c801f6a2068620da51ba11f537453835", + "0x2009df8e6f49f67dcaecb93e4a9ef81aaff096136d26f0fe691e14cd580c47da", + "0x2e512617136e8da2817856e57f13087a75fcc512faefc6d4b2eedd73c58a9b35", + "0x2848fcd535bd7c8017ca331a14919aa492ed05b04e9d0745480d291205eac8dc", + "0x19bb0990cb37f3a8f6c3db78219b07d6accd08e889586660e92dd6000755f09a", + "0x15520c8158b2e36c40c5fa46d5281c45d3df2c7f5d974a1f9549bfca6cbceaea", + "0x0e285f4df658d99922c286c5a253d6f6f37aa6c52d7a0fc1a20f3e6da9df23e1", + "0x0f9cd4667f4c1e86f83eda9e752a05c0cc630b0827a93a68322fa258dffb0f24", + "0x12d8b0dbbea3dccfe5d2dd090daf8ab4d2fac74fada9c49875b0c9122663a8ad", + "0x2e8c814d93f027ecff08c4e58555aadfc0f9ec3889eff2150f2b5bb6c557add0", + "0x013516a1456c5831aba87e4057878f6f3f18471e0674fd1e89be3e18351ec394", + "0x14418aa79dc84fd791d5638bdc103786ef8181a714ee8e022d3a1e792cbc7959", + "0x14418aa79dc84fd791d5638bdc103786ef8181a714ee8e022d3a1e792cbc7959", + "0x25c5e6c96a39bb36e19106d4049b675f0279084cc757c4e2acf6e497c61056a2", + "0x231aaafcf2a4c6fd8da18ce5ae5b33790f2c306a2692c6383c9a0787c50ac269", + "0x0a5f7665f0997081f9b38ec64e9a18542ac3a9648060f8cc720fc04669224730", + "0x0f1c9d9d1ac6f62825c6038117ed30540be434e8fd2d88150dcd4fece39b335a", + "0x1308871c8fcb09f07e5257f5cc5678d98842a8d18b2af09b5132d9af3cb1893e", + "0x28801985290dac4eba72ed01ee06fe88f6fc533dc1a46bd86e2d35be8021b042", + "0x14407f38cfba3cc61fca173b41133ab05a1c176caf8bb597588b01817e9eeaa3", + "0x0ea1a9f6f95f6193e512a7bd3db0c147f66687662934aed53cb657935b1e4eb9", + "0x1bc4ab6eacd61b5fd9e414b0186ef5deaadaf59aa9e53cb8d8812255baa28109", + "0x00000000000000000000000000000093a4da68a2fac0ee94841efdfc57eb748c", + "0x00000000000000000000000000000000001c22f1f5f927bee6adb649cc132391", + "0x0000000000000000000000000000003d0c2acea76c551f58876b3c35f19f345a", + "0x00000000000000000000000000000000002e94fded0a0b7f4fd1c882fd2a4e52", + "0x00000000000000000000000000000022e23b6fa0f72844bf8f60ea140cca5663", + "0x000000000000000000000000000000000013380f284bf3cb98b9a7cbae7d702b", + "0x000000000000000000000000000000942a13cf93056815c3f7439c9eed0a103e", + "0x00000000000000000000000000000000002be14bec02c6dae4625d32866de4fc", + "0x000000000000000000000000000000e2a2c75dc664c12695b4f7795c61f92669", + "0x000000000000000000000000000000000000725da448f376bde6cf63bcf79463", + "0x000000000000000000000000000000f54eee585f8ab367dc66a587e1d4cdbd8c", + "0x0000000000000000000000000000000000071106624ae5623a070f0addc18433", + "0x000000000000000000000000000000d60352bea3b2adb311b1a3beb25acb8aed", + "0x00000000000000000000000000000000001965b7c781e33f94e90c743c7881ed", + "0x0000000000000000000000000000006458a2aa57539e2b192f9c3ed69f9fb674", + "0x00000000000000000000000000000000001fc9c667723a4e66d752c6b426d444", + "0x0000000000000000000000000000008d1ff1c5d59a463c5b46bcf52f41ad3c63", + "0x00000000000000000000000000000000001b3e73df070a35c49a03fab1c76e9b", + "0x0000000000000000000000000000001c17a62b6c0a7ab14de83391e06f780adb", + "0x000000000000000000000000000000000012c7fbe2591b9ae72dd526e4ed1d7f", + "0x000000000000000000000000000000a758fa0c72d6a93155cb18b3fcc7defd34", + "0x00000000000000000000000000000000000cea12961770ce7cb6f2a4aed009fe", + "0x000000000000000000000000000000ef6e9647803aac315fa6d287e0e66f4767", + "0x0000000000000000000000000000000000259a82b8d6c6015cc51d2681f26ad4", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000008152b373c87004bef7d2c55ec8c540b67f", + "0x00000000000000000000000000000000000a55be5fdcb0a0dce4976d7bb78b0c", + "0x000000000000000000000000000000f749ea03f04ac964706139b9d1db595ecb", + "0x000000000000000000000000000000000013218e14dae80c066b4e46e9309fb2", + "0x0000000000000000000000000000004bbd7f950c36ce69db39e2b234a9e3f9b0", + "0x00000000000000000000000000000000002a0c3994d892ca5ea26984abbb30fb", + "0x0000000000000000000000000000006c1b39306846620bd546ac2c897834f259", + "0x000000000000000000000000000000000020350b9f507d6e25961a11be3e494b", +] +public_inputs = [ + "0x0000000000000000000000000000000000000000000000000000000000000003", +] +verification_key = [ + "0x0000000000000000000000000000000000000000000000000000000000000040", + "0x0000000000000000000000000000000000000000000000000000000000000011", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x0000000000000000000000000000000000000000000000000000000000000005", + "0x0000000000000000000000000000000000000000000000000000000000000006", + "0x0000000000000000000000000000000000000000000000000000000000000007", + "0x0000000000000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000009", + "0x000000000000000000000000000000000000000000000000000000000000000a", + "0x000000000000000000000000000000000000000000000000000000000000000b", + "0x000000000000000000000000000000000000000000000000000000000000000c", + "0x000000000000000000000000000000000000000000000000000000000000000d", + "0x000000000000000000000000000000000000000000000000000000000000000e", + "0x000000000000000000000000000000000000000000000000000000000000000f", + "0x0000000000000000000000000000000000000000000000000000000000000010", + "0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84", + "0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae", + "0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16", + "0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1", + "0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c", + "0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7", + "0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8", + "0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c", + "0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5", + "0x00000000000000000000000000000000002002681bb417184b2df070a16a3858", + "0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511", + "0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223", + "0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7", + "0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c", + "0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130", + "0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f", + "0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3", + "0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592", + "0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3", + "0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1", + "0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0", + "0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c", + "0x0000000000000000000000000000009f825dde88092070747180d581c342444a", + "0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01", + "0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff", + "0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9", + "0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1", + "0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b", + "0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2", + "0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f", + "0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0", + "0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349", + "0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8", + "0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2", + "0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556", + "0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d", + "0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb", + "0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d", + "0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8", + "0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862", + "0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e", + "0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830", + "0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f", + "0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe", + "0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb", + "0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56", + "0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc", + "0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4", + "0x00000000000000000000000000000029a17181c7934fc3fdbd352eac5cb521b9", + "0x00000000000000000000000000000000001f497cbf5284ff29a2d336e5991999", + "0x000000000000000000000000000000072bd9c0c6beda1fdee6d4ff0432ba9e1b", + "0x000000000000000000000000000000000013ea38a0bd2aa751a490a724fac818", + "0x000000000000000000000000000000c599f63dcd3edd49f08ae5c3141c1e3493", + "0x00000000000000000000000000000000002bdb36be0bea09950dd32a8ccf6fbc", + "0x00000000000000000000000000000047f27f29724e7f19eba0340256a0bd4b7d", + "0x00000000000000000000000000000000001c1c5ccf87a962129ca785f8f35120", + "0x000000000000000000000000000000c5c71efdae00679bbe4a95096e012b1817", + "0x000000000000000000000000000000000017a365de041e317817d0135f2b48e0", + "0x0000000000000000000000000000008ae711ac402f7848d719c93a89ba8d39f1", + "0x00000000000000000000000000000000002b6fb40ed8a1935226f4f9786a0499", + "0x0000000000000000000000000000002f03a71501d83de1da5715a4e9462d6198", + "0x00000000000000000000000000000000001644064443b8546f48eae693af47b8", + "0x00000000000000000000000000000083763ab1b6e8fe269b2fe4c7b9c448c08d", + "0x000000000000000000000000000000000021d7cc18c59676a8eeb47c0111c251", + "0x000000000000000000000000000000b5f937153073e03ea7d51a996e0ebc2e6b", + "0x000000000000000000000000000000000011ddd0e26457373eb06e0493177672", + "0x000000000000000000000000000000c5f6eb9f6fc8fa99811a4a88c74a6d018b", + "0x000000000000000000000000000000000025bcd07a0732c123567834f5109558", + "0x000000000000000000000000000000aeb08a0b1a4442189448b4e97490568146", + "0x000000000000000000000000000000000002a1744e4771705536a88f07e0f90f", + "0x000000000000000000000000000000b938568293bd0724b0ea76c2ec34c4a829", + "0x0000000000000000000000000000000000053296e8f3b9ad3af877dfa9c7c2a7", + "0x000000000000000000000000000000f0ca1db6323996eba26bdc86dafef9d10b", + "0x00000000000000000000000000000000001441a46c58af03d5645d52721d956a", + "0x0000000000000000000000000000008bbf8f884013c66c28ba09c2fbd573b656", + "0x0000000000000000000000000000000000206c391ca06fac27d1908e94570243", + "0x0000000000000000000000000000002d4f5aaed88ba4f79612d53b804ca8f194", + "0x00000000000000000000000000000000001674011c96392df08970fa6b7b4cb8", + "0x0000000000000000000000000000009f88297c1729d76c4d9306853598c91325", + "0x0000000000000000000000000000000000256f51adfcacc3c1e340be4d32d3e9", + "0x0000000000000000000000000000000ab9955eec0d74eb799afed2a802b24d75", + "0x00000000000000000000000000000000001fcbe43ea105b30d36ed0b21b03411", + "0x000000000000000000000000000000d66b1d5433f1aa5305cd1edce7c22de466", + "0x00000000000000000000000000000000002331546a256b8a3b751956806680d4", + "0x000000000000000000000000000000e97954ad6cd6f45fb15c91434121db4304", + "0x00000000000000000000000000000000002e20a97e09d50f227ced47e7a98250", + "0x0000000000000000000000000000001ebbc27eb9ebededefba79522eb58ae89b", + "0x0000000000000000000000000000000000090efa4974e566e81d1177b85a30be", + "0x0000000000000000000000000000005eafa070b9c9632404052642e3bc14f9fd", + "0x00000000000000000000000000000000001489068864102daca6a6b8bc4d448b", + "0x0000000000000000000000000000009ebc91aaaac036a6477cadbe54e8556dfd", + "0x00000000000000000000000000000000000ef6d835e2ed3343b95c82c8c54037", + "0x00000000000000000000000000000033b28b529dff46e93af4e7422530478e4a", + "0x000000000000000000000000000000000020a86c2f8591bf190bcddcc03c42fb", + "0x000000000000000000000000000000a9679d0acc088f7dc27bf6d866bcd2dda2", + "0x00000000000000000000000000000000002fb9d0d2d4099402bed74f738f64cc", + "0x00000000000000000000000000000023b09f876a29a061582848a8b9a5870c12", + "0x00000000000000000000000000000000001d5bb906f03f0d49e9c4791bc43af9", + "0x00000000000000000000000000000017aac9854ea240d8ec97bf760c4d4ba870", + "0x00000000000000000000000000000000000b227a556c414ada0dc75bb303e30e", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000009b624fa65d1a24b7f14a8f25f3789622af", + "0x000000000000000000000000000000000013d47bff8c630e847b70e2732fd3f0", + "0x00000000000000000000000000000061d21663e93132f32921075f4c936a84df", + "0x00000000000000000000000000000000001a74ca4e118fb480b9b999902989a3", +]