diff --git a/.noir-sync-commit b/.noir-sync-commit index 6d1a3f2c2323..9326943a3102 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -3dab4dd771b7d8b9242ce3a9aeff5770f4d85cf6 +d6f60d70dc41640ad84f7a968927b20818bcaf2a diff --git a/noir-projects/aztec-nr/authwit/src/auth.nr b/noir-projects/aztec-nr/authwit/src/auth.nr index 9e240682fae7..a533bd589b53 100644 --- a/noir-projects/aztec-nr/authwit/src/auth.nr +++ b/noir-projects/aztec-nr/authwit/src/auth.nr @@ -215,7 +215,11 @@ pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf * @param on_behalf_of The address that have authorized the current call * @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) { +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. let result: Field = context.static_call_private_function( on_behalf_of, @@ -262,7 +266,11 @@ pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_ * * @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) { +pub fn assert_inner_hash_valid_authwit_public( + context: &mut PublicContext, + on_behalf_of: AztecAddress, + inner_hash: Field +) { let result: Field = context.call_public_function( CANONICAL_AUTH_REGISTRY_ADDRESS, comptime { @@ -338,7 +346,12 @@ pub fn compute_authwit_nullifier(on_behalf_of: AztecAddress, inner_hash: Field) * @param version The version of the chain that the message is being consumed on * @param inner_hash The hash of the "inner" message that is being consumed */ -pub fn compute_authwit_message_hash(consumer: AztecAddress, chain_id: Field, version: Field, inner_hash: Field) -> Field { +pub fn compute_authwit_message_hash( + consumer: AztecAddress, + chain_id: Field, + version: Field, + inner_hash: Field +) -> Field { poseidon2_hash_with_separator( [ consumer.to_field(), diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index 39af0fba85a0..eaa143772bc5 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -137,12 +137,12 @@ mod test { } }; - /// 1. `EncryptedLogIncomingBody::from_note` calls `note.to_be_bytes(storage_slot)` function which serializes - /// the note to bytes - note that in the case of `AddressNote` the `to_be_bytes` function was automatically - /// implemented by Aztec macros. + // 1. `EncryptedLogIncomingBody::from_note` calls `note.to_be_bytes(storage_slot)` function which serializes + // the note to bytes - note that in the case of `AddressNote` the `to_be_bytes` function was automatically + // implemented by Aztec macros. let body = EncryptedLogIncomingBody::from_note(note, storage_slot); - /// 2. `body.compute_ciphertext(...)` function then derives symmetric key from `eph_sk` and `ivpk` and encrypts + // 2. `body.compute_ciphertext(...)` function then derives symmetric key from `eph_sk` and `ivpk` and encrypts // the note plaintext using AES-128. let ciphertext = body.compute_ciphertext(eph_sk, ivpk); diff --git a/noir-projects/noir-protocol-circuits/crates/blob/src/main.nr b/noir-projects/noir-protocol-circuits/crates/blob/src/main.nr index de1cd87646b2..1a5ca5e8fdc2 100644 --- a/noir-projects/noir-protocol-circuits/crates/blob/src/main.nr +++ b/noir-projects/noir-protocol-circuits/crates/blob/src/main.nr @@ -344,28 +344,24 @@ fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F { // Making a call to this function causes a "stack too deep" error, so I've put the body of that function here, instead: // let fracs = __compute_fracs(z, ys); // { y_i / (z - ω^i) } - /** - * - * Note: it's more efficient (saving 30k constraints) to compute: - * ___d-1 - * \ / y_i \ - * / | --------- | . ω^i - * /____ \ z - ω^i / - * i=0 - * ^^^^^^^^^ - * frac - * - * ... than to compute: - * - * ___d-1 - * \ / ω^i \ - * / y_i . | --------- | - * /____ \ z - ω^i / - * i=0 - * - * perhaps because all the ω^i terms are constant witnesses? - * - */ + // Note: it's more efficient (saving 30k constraints) to compute: + // ___d-1 + // \ / y_i \ + // / | --------- | . ω^i + // /____ \ z - ω^i / + // i=0 + // ^^^^^^^^^ + // frac + // + // ... than to compute: + // + // ___d-1 + // \ / ω^i \ + // / y_i . | --------- | + // /____ \ z - ω^i / + // i=0 + // + // perhaps because all the ω^i terms are constant witnesses? //***************************************************************** // This section is only needed because `__compute_fracs` isn't working (stack too deep error). @@ -415,28 +411,25 @@ fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F { // which implies...we can accomodate up to EIGHT additions of product terms before we risk overflowing // (this is really messy! I never considered the case of giant linear sequences of products) let mut sum: F = BigNum::new(); - /** - * Seeking: - * ___d-1 - * \ ω^i - * sum = / y_i . --------- - * /____ z - ω^i - * i=0 - */ + + // Seeking: + // ___d-1 + // \ ω^i + // sum = / y_i . --------- + // /____ z - ω^i + // i=0 let NUM_PARTIAL_SUMS = FIELDS_PER_BLOB / 8; for i in 0..NUM_PARTIAL_SUMS { let mut partial_sum: F = BigNum::new(); let mut lhs: [F; 8] = [BigNum::new(); 8]; let mut rhs = lhs; - /** - * Seeking: - * ___i*8 + 7 - * \ ω^k - * partial_sum = / y_k . --------- - * /____ z - ω^k - * k=i*8 + 0 - */ + // Seeking: + // ___i*8 + 7 + // \ ω^k + // partial_sum = / y_k . --------- + // /____ z - ω^k + // k=i*8 + 0 for j in 0..8 { let k = i * 8 + j; @@ -459,26 +452,25 @@ fn barycentric_evaluate_blob_at_z(z: F, ys: [F; FIELDS_PER_BLOB]) -> F { std::as_witness(partial_sum.limbs[2]); } - /** - * Seeking: - * ___i*8 - 1 ___i*8 + 7 - * \ ω^i \ / y_k \ - * sum_out = / y_i . --------- + / ω^k . | --------- | - * /____ z - ω^i /____ \ z - ω^k / - * 0 k = i*8 - * ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - * sum partial_sum - * - * ... that is: - * - * ___i*8 - 1 ___ 7 - * \ ω^i \ - * sum_out = / y_i . --------- + / lhs[j] . rhs[j] - * /____ z - ω^i /____ - * 0 j = 0 - * ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ - * sum partial_sum - */ + // Seeking: + // ___i*8 - 1 ___i*8 + 7 + // \ ω^i \ / y_k \ + // sum_out = / y_i . --------- + / ω^k . | --------- | + // /____ z - ω^i /____ \ z - ω^k / + // 0 k = i*8 + // ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // sum partial_sum + // + // ... that is: + // + // ___i*8 - 1 ___ 7 + // \ ω^i \ + // sum_out = / y_i . --------- + / lhs[j] . rhs[j] + // /____ z - ω^i /____ + // 0 j = 0 + // ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + // sum partial_sum + // let mut sum_out = sum.__add(partial_sum); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_propagated_from_private_call.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_propagated_from_private_call.nr index 16e28db0d44f..4b7888665cc8 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_propagated_from_private_call.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_circuit_output_validator_builder/validate_propagated_from_private_call.nr @@ -85,10 +85,7 @@ fn validate_propagated_from_private_call_note_hash_read_requests_output_extra_no * With previous kernel. */ -fn append_note_hash_read_requests_to_previous_kernel( - builder: &mut PrivateKernelCircuitOutputValidatorBuilder, - num_requests: u32 -) { +fn append_note_hash_read_requests_to_previous_kernel(builder: &mut PrivateKernelCircuitOutputValidatorBuilder, num_requests: u32) { builder.previous_kernel.append_note_hash_read_requests(num_requests); builder.output.append_note_hash_read_requests(num_requests); builder.offset_values(num_requests as Field); diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index b37a42ca5e8b..a42e25a10b5c 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -900,10 +900,6 @@ mod tests { #[test] unconstrained fn new_nullifier_tree_empty() { - /** - * DESCRIPTION - */ - // This test checks for insertions of all 0 values // In this special case we will not need to provide sibling paths to check insertion of the nullifier values // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr index 6e597b1d275c..d787969abd20 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/components.nr @@ -24,7 +24,10 @@ use dep::types::{ * Asserts that the tree formed by rollup circuits is filled greedily from L to R * */ -pub fn assert_txs_filled_from_left(left: BaseOrMergeRollupPublicInputs, right: BaseOrMergeRollupPublicInputs) { +pub fn assert_txs_filled_from_left( + left: BaseOrMergeRollupPublicInputs, + right: BaseOrMergeRollupPublicInputs +) { // assert that the left rollup is either a base (1 tx) or a balanced tree (num txs = power of 2) if (left.rollup_type == 1) { let left_txs = left.num_txs; @@ -47,7 +50,10 @@ pub fn assert_txs_filled_from_left(left: BaseOrMergeRollupPublicInputs, right: B * Asserts that the constants used in the left and right child are identical * */ -pub fn assert_equal_constants(left: BaseOrMergeRollupPublicInputs, right: BaseOrMergeRollupPublicInputs) { +pub fn assert_equal_constants( + left: BaseOrMergeRollupPublicInputs, + right: BaseOrMergeRollupPublicInputs +) { assert(left.constants.eq(right.constants), "input proofs have different constants"); } diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 3f56f5b6965a..796ac50ca9f8 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -274,7 +274,7 @@ dependencies = [ "ark-std", "derivative", "hashbrown 0.13.2", - "itertools 0.10.5", + "itertools", "num-traits", "zeroize", ] @@ -291,7 +291,7 @@ dependencies = [ "ark-std", "derivative", "digest", - "itertools 0.10.5", + "itertools", "num-bigint", "num-traits", "paste", @@ -380,15 +380,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "ascii-canvas" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term", -] - [[package]] name = "assert_cmd" version = "2.0.12" @@ -1069,7 +1060,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools 0.10.5", + "itertools", "num-traits", "once_cell", "oorandom", @@ -1090,7 +1081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools 0.10.5", + "itertools", ] [[package]] @@ -1410,15 +1401,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ena" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" -dependencies = [ - "log", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -2207,15 +2189,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.9" @@ -2400,37 +2373,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools 0.11.0", - "lalrpop-util", - "petgraph", - "pico-args", - "regex", - "regex-syntax 0.8.2", - "string_cache", - "term", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" -dependencies = [ - "regex-automata 0.4.7", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -2731,12 +2673,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - [[package]] name = "nibble_vec" version = "0.1.0" @@ -3034,8 +2970,6 @@ dependencies = [ "fm", "im", "iter-extended", - "lalrpop", - "lalrpop-util", "noirc_arena", "noirc_errors", "noirc_printable_type", @@ -3377,12 +3311,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -3489,12 +3417,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "predicates" version = "2.1.5" @@ -3503,7 +3425,7 @@ checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ "difflib", "float-cmp", - "itertools 0.10.5", + "itertools", "normalize-line-endings", "predicates-core", "regex", @@ -3517,7 +3439,7 @@ checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" dependencies = [ "anstyle", "difflib", - "itertools 0.10.5", + "itertools", "predicates-core", ] @@ -4396,19 +4318,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.1", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "strsim" version = "0.10.0" diff --git a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs index cd25ed6197bb..41e288829932 100644 --- a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs @@ -1643,7 +1643,7 @@ proptest! { #[test] fn keccak256_injective(inputs_distinct_inputs in any_distinct_inputs(Some(8), 0, 32)) { let (inputs, distinct_inputs) = inputs_distinct_inputs; - let (result, message) = prop_assert_injective(inputs, distinct_inputs, 32, Some(32), keccak256_op); + let (result, message) = prop_assert_injective(inputs, distinct_inputs, 32, Some(8), keccak256_op); prop_assert!(result, "{}", message); } diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index ec1d395725db..4ba8951c2f99 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -66,7 +66,9 @@ fn transform( // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast - for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { + for submodule in + ast.submodules.iter_mut().map(|m| &mut m.item).filter(|submodule| submodule.is_contract) + { if transform_module( &file_id, &mut submodule.contents, @@ -111,7 +113,8 @@ fn transform_module( } let has_initializer = module.functions.iter().any(|func| { - func.def + func.item + .def .attributes .secondary .iter() @@ -121,6 +124,7 @@ fn transform_module( let mut stubs: Vec<_> = vec![]; for func in module.functions.iter_mut() { + let func = &mut func.item; let mut is_private = false; let mut is_public = false; let mut is_initializer = false; @@ -175,6 +179,7 @@ fn transform_module( let private_functions: Vec<_> = module .functions .iter() + .map(|t| &t.item) .filter(|func| { func.def .attributes @@ -187,6 +192,7 @@ fn transform_module( let public_functions: Vec<_> = module .functions .iter() + .map(|func| &func.item) .filter(|func| { func.def .attributes diff --git a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs index 8983266dab9e..4d5dcc6f1af5 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs @@ -166,7 +166,7 @@ fn generate_compute_note_hash_and_optionally_a_nullifier( assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); let mut function_ast = function_ast.into_sorted(); - function_ast.functions.remove(0) + function_ast.functions.remove(0).item } fn generate_compute_note_hash_and_optionally_a_nullifier_source( diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 7a8a71878577..e2de30d6d93d 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -1,7 +1,7 @@ use acvm::acir::AcirField; use noirc_errors::Location; -use noirc_frontend::ast::{Ident, NoirFunction, UnresolvedTypeData}; +use noirc_frontend::ast::{Documented, Ident, NoirFunction, UnresolvedTypeData}; use noirc_frontend::{ graph::CrateId, macros_api::{FieldElement, FileId, HirContext, HirExpression, HirLiteral, HirStatement}, @@ -267,15 +267,16 @@ pub fn generate_contract_interface( .methods .iter() .enumerate() - .map(|(i, (method, orig_span))| { + .map(|(i, (documented_method, orig_span))| { + let method = &documented_method.item; if method.name() == "at" || method.name() == "interface" || method.name() == "storage" { - (method.clone(), *orig_span) + (documented_method.clone(), *orig_span) } else { let (_, new_location) = stubs[i]; let mut modified_method = method.clone(); modified_method.def.name = Ident::new(modified_method.name().to_string(), new_location.span); - (modified_method, *orig_span) + (Documented::not_documented(modified_method), *orig_span) } }) .collect(); diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index ede8a350bf24..d753bb434717 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -1,4 +1,4 @@ -use noirc_frontend::ast::{ItemVisibility, NoirFunction, NoirTraitImpl, TraitImplItem}; +use noirc_frontend::ast::{Documented, ItemVisibility, NoirFunction, NoirTraitImpl, TraitImplItem}; use noirc_frontend::macros_api::{NodeInterner, StructId}; use noirc_frontend::token::SecondaryAttribute; use noirc_frontend::{ @@ -34,10 +34,11 @@ pub fn generate_event_impls( // print!("\ngenerate_event_interface_impl COUNT: {}\n", event_struct.name.0.contents); // } - for submodule in module.submodules.iter_mut() { - let annotated_event_structs = submodule.contents.types.iter_mut().filter(|typ| { - typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) - }); + for submodule in module.submodules.iter_mut().map(|m| &mut m.item) { + let annotated_event_structs = + submodule.contents.types.iter_mut().map(|typ| &mut typ.item).filter(|typ| { + typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) + }); for event_struct in annotated_event_structs { // event_struct.attributes.push(SecondaryAttribute::Abi("events".to_string())); @@ -52,7 +53,9 @@ pub fn generate_event_impls( let mut event_fields = vec![]; - for (field_ident, field_type) in event_struct.fields.iter() { + for field in event_struct.fields.iter() { + let field_ident = &field.item.name; + let field_type = &field.item.typ; event_fields.push(( field_ident.0.contents.to_string(), field_type.typ.to_string().replace("plain::", ""), @@ -64,18 +67,30 @@ pub fn generate_event_impls( event_byte_len, empty_spans, )?; - event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_get_event_type_id(event_type.as_str(), event_len, empty_spans)?, + event_interface_trait_impl.items.push(Documented::not_documented( + TraitImplItem::Function(generate_fn_get_event_type_id( + event_type.as_str(), + event_len, + empty_spans, + )?), + )); + event_interface_trait_impl.items.push(Documented::not_documented( + TraitImplItem::Function(generate_fn_private_to_be_bytes( + event_type.as_str(), + event_byte_len, + empty_spans, + )?), )); - event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_private_to_be_bytes(event_type.as_str(), event_byte_len, empty_spans)?, + event_interface_trait_impl.items.push(Documented::not_documented( + TraitImplItem::Function(generate_fn_to_be_bytes( + event_type.as_str(), + event_byte_len, + empty_spans, + )?), )); - event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_to_be_bytes(event_type.as_str(), event_byte_len, empty_spans)?, + event_interface_trait_impl.items.push(Documented::not_documented( + TraitImplItem::Function(generate_fn_emit(event_type.as_str(), empty_spans)?), )); - event_interface_trait_impl - .items - .push(TraitImplItem::Function(generate_fn_emit(event_type.as_str(), empty_spans)?)); submodule.contents.trait_impls.push(event_interface_trait_impl); let serialize_trait_impl = generate_trait_impl_serialize( @@ -245,7 +260,7 @@ fn generate_fn_get_event_type_id( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -292,7 +307,7 @@ fn generate_fn_private_to_be_bytes( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -337,7 +352,7 @@ fn generate_fn_to_be_bytes( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -361,7 +376,7 @@ fn generate_fn_emit(event_type: &str, empty_spans: bool) -> Result, + types: &mut Vec>, func: &NoirFunction, empty_spans: bool, ) -> Result<(), AztecMacroError> { diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index df237926486f..6e95efa637c0 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -1,7 +1,7 @@ use noirc_errors::Span; use noirc_frontend::ast::{ - ItemVisibility, LetStatement, NoirFunction, NoirStruct, PathKind, TraitImplItem, TypeImpl, - UnresolvedTypeData, UnresolvedTypeExpression, + Documented, ItemVisibility, LetStatement, NoirFunction, NoirStruct, PathKind, StructField, + TraitImplItem, TypeImpl, UnresolvedTypeData, UnresolvedTypeExpression, }; use noirc_frontend::{ graph::CrateId, @@ -35,10 +35,10 @@ pub fn generate_note_interface_impl( empty_spans: bool, ) -> Result<(), AztecMacroError> { // Find structs annotated with #[aztec(note)] - let annotated_note_structs = module - .types - .iter_mut() - .filter(|typ| typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(note)"))); + let annotated_note_structs = + module.types.iter_mut().map(|t| &mut t.item).filter(|typ| { + typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(note)")) + }); let mut structs_to_inject = vec![]; @@ -110,26 +110,28 @@ pub fn generate_note_interface_impl( ); // Automatically inject the header field if it's not present - let (header_field_name, _) = if let Some(existing_header) = - note_struct.fields.iter().find(|(_, field_type)| match &field_type.typ { + let header_field_name = if let Some(existing_header) = + note_struct.fields.iter().find(|field| match &field.item.typ.typ { UnresolvedTypeData::Named(path, _, _) => path.last_name() == "NoteHeader", _ => false, }) { - existing_header.clone() + existing_header.clone().item.name } else { - let generated_header = ( - ident("header"), - make_type(UnresolvedTypeData::Named( + let generated_header = StructField { + name: ident("header"), + typ: make_type(UnresolvedTypeData::Named( chained_dep!("aztec", "note", "note_header", "NoteHeader"), Default::default(), false, )), - ); - note_struct.fields.push(generated_header.clone()); - generated_header + }; + note_struct.fields.push(Documented::not_documented(generated_header.clone())); + generated_header.name }; - for (field_ident, field_type) in note_struct.fields.iter() { + for field in note_struct.fields.iter() { + let field_ident = &field.item.name; + let field_type = &field.item.typ; note_fields.push(( field_ident.0.contents.to_string(), field_type.typ.to_string().replace("plain::", ""), @@ -138,7 +140,10 @@ pub fn generate_note_interface_impl( if !check_trait_method_implemented(trait_impl, "serialize_content") && !check_trait_method_implemented(trait_impl, "deserialize_content") - && !note_impl.methods.iter().any(|(func, _)| func.def.name.0.contents == "properties") + && !note_impl + .methods + .iter() + .any(|(func, _)| func.item.def.name.0.contents == "properties") { let note_serialize_content_fn = generate_note_serialize_content( ¬e_type, @@ -148,7 +153,9 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(note_serialize_content_fn)); + trait_impl.items.push(Documented::not_documented(TraitImplItem::Function( + note_serialize_content_fn, + ))); let note_deserialize_content_fn = generate_note_deserialize_content( ¬e_type, @@ -158,7 +165,9 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(note_deserialize_content_fn)); + trait_impl.items.push(Documented::not_documented(TraitImplItem::Function( + note_deserialize_content_fn, + ))); let note_properties_struct = generate_note_properties_struct( ¬e_type, @@ -167,7 +176,7 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - structs_to_inject.push(note_properties_struct); + structs_to_inject.push(Documented::not_documented(note_properties_struct)); let note_properties_fn = generate_note_properties_fn( ¬e_type, ¬e_fields, @@ -175,7 +184,9 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - note_impl.methods.push((note_properties_fn, note_impl.type_span)); + note_impl + .methods + .push((Documented::not_documented(note_properties_fn), note_impl.type_span)); } if !check_trait_method_implemented(trait_impl, "get_header") { @@ -185,7 +196,9 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + trait_impl + .items + .push(Documented::not_documented(TraitImplItem::Function(get_header_fn))); } if !check_trait_method_implemented(trait_impl, "set_header") { let set_header_fn = generate_note_set_header( @@ -194,14 +207,18 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(set_header_fn)); + trait_impl + .items + .push(Documented::not_documented(TraitImplItem::Function(set_header_fn))); } if !check_trait_method_implemented(trait_impl, "get_note_type_id") { let note_type_id = compute_note_type_id(¬e_type); let get_note_type_id_fn = generate_get_note_type_id(note_type_id, note_interface_impl_span, empty_spans)?; - trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); + trait_impl + .items + .push(Documented::not_documented(TraitImplItem::Function(get_note_type_id_fn))); } if !check_trait_method_implemented(trait_impl, "compute_note_hiding_point") { @@ -210,7 +227,9 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(compute_note_hiding_point_fn)); + trait_impl.items.push(Documented::not_documented(TraitImplItem::Function( + compute_note_hiding_point_fn, + ))); } if !check_trait_method_implemented(trait_impl, "to_be_bytes") { @@ -221,7 +240,9 @@ pub fn generate_note_interface_impl( note_interface_impl_span, empty_spans, )?; - trait_impl.items.push(TraitImplItem::Function(to_be_bytes_fn)); + trait_impl + .items + .push(Documented::not_documented(TraitImplItem::Function(to_be_bytes_fn))); } } @@ -275,7 +296,7 @@ fn generate_note_to_be_bytes( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -307,7 +328,7 @@ fn generate_note_get_header( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -338,7 +359,7 @@ fn generate_note_set_header( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -372,7 +393,7 @@ fn generate_get_note_type_id( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -407,7 +428,7 @@ fn generate_note_properties_struct( } let mut struct_ast = struct_ast.into_sorted(); - Ok(struct_ast.types.remove(0)) + Ok(struct_ast.types.remove(0).item) } // Generate the deserialize_content method as @@ -445,7 +466,7 @@ fn generate_note_deserialize_content( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -483,7 +504,7 @@ fn generate_note_serialize_content( } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -508,7 +529,7 @@ fn generate_note_properties_fn( }); } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -547,7 +568,7 @@ fn generate_compute_note_hiding_point( }); } let mut function_ast = function_ast.into_sorted(); - let mut noir_fn = function_ast.functions.remove(0); + let mut noir_fn = function_ast.functions.remove(0).item; noir_fn.def.span = impl_span; noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) @@ -579,7 +600,7 @@ fn generate_note_exports_global( } let mut global_ast = global_ast.into_sorted(); - Ok(global_ast.globals.pop().unwrap()) + Ok(global_ast.globals.pop().unwrap().item) } // Source code generator functions. These utility methods produce Noir code as strings, that are then parsed and added to the AST. diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index 7dd21f1a8ac1..a6bf2e14fb34 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -1,8 +1,8 @@ use acvm::acir::AcirField; use noirc_errors::Span; use noirc_frontend::ast::{ - BlockExpression, Expression, ExpressionKind, FunctionDefinition, GenericTypeArgs, Ident, - Literal, NoirFunction, NoirStruct, Pattern, StatementKind, TypeImpl, UnresolvedType, + BlockExpression, Documented, Expression, ExpressionKind, FunctionDefinition, GenericTypeArgs, + Ident, Literal, NoirFunction, NoirStruct, Pattern, StatementKind, TypeImpl, UnresolvedType, UnresolvedTypeData, }; use noirc_frontend::{ @@ -38,6 +38,7 @@ pub fn check_for_storage_definition( let result: Vec<&NoirStruct> = module .types .iter() + .map(|t| &t.item) .filter(|r#struct| { r#struct.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(storage)")) }) @@ -88,6 +89,7 @@ pub fn inject_context_in_storage(module: &mut SortedModule) -> Result<(), AztecM let storage_struct = module .types .iter_mut() + .map(|t| &mut t.item) .find(|r#struct| { r#struct.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(storage)")) }) @@ -96,7 +98,7 @@ pub fn inject_context_in_storage(module: &mut SortedModule) -> Result<(), AztecM storage_struct .fields .iter_mut() - .map(|(_, field)| inject_context_in_storage_field(field)) + .map(|field| inject_context_in_storage_field(&mut field.item.typ)) .collect::, _>>()?; Ok(()) } @@ -200,6 +202,7 @@ pub fn generate_storage_implementation( let definition = module .types .iter() + .map(|t| &t.item) .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) .unwrap(); @@ -212,8 +215,10 @@ pub fn generate_storage_implementation( .fields .iter() .flat_map(|field| { - generate_storage_field_constructor(field, slot_zero.clone()) - .map(|expression| (field.0.clone(), expression)) + let ident = &field.item.name; + let typ = &field.item.typ; + generate_storage_field_constructor(&(ident.clone(), typ.clone()), slot_zero.clone()) + .map(|expression| (field.item.name.clone(), expression)) }) .collect(); @@ -249,7 +254,7 @@ pub fn generate_storage_implementation( type_span: Span::default(), generics: vec![generic_context_ident.into()], - methods: vec![(init, Span::default())], + methods: vec![(Documented::not_documented(init), Span::default())], where_clause: vec![], }; @@ -509,13 +514,15 @@ pub fn generate_storage_layout( let definition = module .types .iter() + .map(|t| &t.item) .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) .unwrap(); let mut storable_fields = vec![]; let mut storable_fields_impl = vec![]; - definition.fields.iter().for_each(|(field_ident, _)| { + definition.fields.iter().for_each(|field| { + let field_ident = &field.item.name; storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident)); storable_fields_impl .push(format!("{}: dep::aztec::prelude::Storable {{ slot: 0 }}", field_ident,)); diff --git a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs index 316aa60da627..b68946ec0200 100644 --- a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs @@ -179,7 +179,7 @@ pub fn index_array(array: Ident, index: &str) -> Expression { } pub fn check_trait_method_implemented(trait_impl: &NoirTraitImpl, method_name: &str) -> bool { - trait_impl.items.iter().any(|item| match item { + trait_impl.items.iter().any(|item| match &item.item { TraitImplItem::Function(func) => func.def.name.0.contents == method_name, _ => false, }) 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 e7b3e347a966..dce3af1402be 100644 --- a/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs @@ -64,7 +64,7 @@ fn empty_noir_trait(noir_trait: &mut NoirTrait) { empty_unresolved_generics(&mut noir_trait.generics); empty_unresolved_trait_constraints(&mut noir_trait.where_clause); for item in noir_trait.items.iter_mut() { - empty_trait_item(item); + empty_trait_item(&mut item.item); } } @@ -74,7 +74,7 @@ fn empty_noir_trait_impl(noir_trait_impl: &mut NoirTraitImpl) { empty_unresolved_type(&mut noir_trait_impl.object_type); empty_unresolved_trait_constraints(&mut noir_trait_impl.where_clause); for item in noir_trait_impl.items.iter_mut() { - empty_trait_impl_item(item); + empty_trait_impl_item(&mut item.item); } } @@ -84,7 +84,7 @@ fn empty_type_impl(type_impl: &mut TypeImpl) { empty_unresolved_generics(&mut type_impl.generics); empty_unresolved_trait_constraints(&mut type_impl.where_clause); for (noir_function, _) in type_impl.methods.iter_mut() { - empty_noir_function(noir_function); + empty_noir_function(&mut noir_function.item); } } @@ -187,9 +187,9 @@ fn empty_use_tree(use_tree: &mut UseTree) { fn empty_noir_struct(noir_struct: &mut NoirStruct) { noir_struct.span = Default::default(); empty_ident(&mut noir_struct.name); - for (name, typ) in noir_struct.fields.iter_mut() { - empty_ident(name); - empty_unresolved_type(typ); + for field in noir_struct.fields.iter_mut() { + empty_ident(&mut field.item.name); + empty_unresolved_type(&mut field.item.typ); } empty_unresolved_generics(&mut noir_struct.generics); } @@ -401,6 +401,7 @@ fn empty_pattern(pattern: &mut Pattern) { empty_pattern(pattern); } } + Pattern::Interned(_, _) => (), } } diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 31c279bc0f31..18a13517b75d 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -212,23 +212,25 @@ fn add_debug_source_to_file_manager(file_manager: &mut FileManager) { /// Adds the file from the file system at `Path` to the crate graph as a root file /// -/// Note: This methods adds the stdlib as a dependency to the crate. -/// This assumes that the stdlib has already been added to the file manager. +/// Note: If the stdlib dependency has not been added yet, it's added. Otherwise +/// this method assumes the root crate is the stdlib (useful for running tests +/// in the stdlib, getting LSP stuff for the stdlib, etc.). pub fn prepare_crate(context: &mut Context, file_name: &Path) -> CrateId { let path_to_std_lib_file = Path::new(STD_CRATE_NAME).join("lib.nr"); - let std_file_id = context - .file_manager - .name_to_id(path_to_std_lib_file) - .expect("stdlib file id is expected to be present"); - let std_crate_id = context.crate_graph.add_stdlib(std_file_id); + let std_file_id = context.file_manager.name_to_id(path_to_std_lib_file); + let std_crate_id = std_file_id.map(|std_file_id| context.crate_graph.add_stdlib(std_file_id)); let root_file_id = context.file_manager.name_to_id(file_name.to_path_buf()).unwrap_or_else(|| panic!("files are expected to be added to the FileManager before reaching the compiler file_path: {file_name:?}")); - let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + if let Some(std_crate_id) = std_crate_id { + let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - add_dep(context, root_crate_id, std_crate_id, STD_CRATE_NAME.parse().unwrap()); + add_dep(context, root_crate_id, std_crate_id, STD_CRATE_NAME.parse().unwrap()); - root_crate_id + root_crate_id + } else { + context.crate_graph.add_crate_root_and_stdlib(root_file_id) + } } pub fn link_to_debug_crate(context: &mut Context, root_crate_id: CrateId) { diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index c0f6c8965fbd..510cff08dec6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -31,7 +31,6 @@ cfg-if.workspace = true tracing.workspace = true petgraph = "0.6" rangemap = "1.4.0" -lalrpop-util = { version = "0.20.2", features = ["lexer"] } strum = "0.24" strum_macros = "0.24" @@ -39,9 +38,6 @@ strum_macros = "0.24" [dev-dependencies] base64.workspace = true -[build-dependencies] -lalrpop = "0.20.2" - [features] experimental_parser = [] bn254 = [] diff --git a/noir/noir-repo/compiler/noirc_frontend/build.rs b/noir/noir-repo/compiler/noirc_frontend/build.rs deleted file mode 100644 index eb896a377aee..000000000000 --- a/noir/noir-repo/compiler/noirc_frontend/build.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::fs::{read_to_string, File}; -use std::io::Write; - -fn main() { - lalrpop::Configuration::new() - .emit_rerun_directives(true) - .use_cargo_dir_conventions() - .process() - .unwrap(); - - // here, we get a lint error from "extern crate core" so patching that until lalrpop does - // (adding cfg directives appears to be unsupported by lalrpop) - let out_dir = std::env::var("OUT_DIR").unwrap(); - let parser_path = std::path::Path::new(&out_dir).join("noir_parser.rs"); - let content_str = read_to_string(parser_path.clone()).unwrap(); - let mut parser_file = File::create(parser_path).unwrap(); - for line in content_str.lines() { - if line.contains("extern crate core") { - parser_file - .write_all( - format!("{}\n", line.replace("extern crate core", "use core")).as_bytes(), - ) - .unwrap(); - } else { - parser_file.write_all(format!("{}\n", line).as_bytes()).unwrap(); - } - } -} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/docs.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/docs.rs new file mode 100644 index 000000000000..f00f15c215df --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/docs.rs @@ -0,0 +1,23 @@ +use std::fmt::Display; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Documented { + pub item: T, + pub doc_comments: Vec, +} + +impl Documented { + pub fn new(item: T, doc_comments: Vec) -> Self { + Self { item, doc_comments } + } + + pub fn not_documented(item: T) -> Self { + Self { item, doc_comments: Vec::new() } + } +} + +impl Display for Documented { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.item) + } +} 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 1ed88115fa03..12a8aec05eb3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -4,6 +4,7 @@ //! //! Noir's Ast is produced by the parser and taken as input to name resolution, //! where it is converted into the Hir (defined in the hir_def module). +mod docs; mod expression; mod function; mod statement; @@ -18,6 +19,7 @@ pub use visitor::Visitor; pub use expression::*; pub use function::*; +pub use docs::*; use noirc_errors::Span; use serde::{Deserialize, Serialize}; pub use statement::*; 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 30db8ad63fd6..52c39a49e8a1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -7,13 +7,13 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; use super::{ - BlockExpression, Expression, ExpressionKind, GenericTypeArgs, IndexExpression, ItemVisibility, - MemberAccessExpression, MethodCallExpression, UnresolvedType, + BlockExpression, ConstructorExpression, Expression, ExpressionKind, GenericTypeArgs, + IndexExpression, ItemVisibility, MemberAccessExpression, MethodCallExpression, UnresolvedType, }; 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::macros_api::{NodeInterner, SecondaryAttribute, UnresolvedTypeData}; +use crate::node_interner::{InternedExpressionKind, InternedPattern, InternedStatementKind}; use crate::parser::{ParserError, ParserErrorReason}; use crate::token::Token; @@ -565,6 +565,7 @@ pub enum Pattern { Mutable(Box, Span, /*is_synthesized*/ bool), Tuple(Vec, Span), Struct(Path, Vec<(Ident, Pattern)>, Span), + Interned(InternedPattern, Span), } impl Pattern { @@ -577,7 +578,8 @@ impl Pattern { Pattern::Identifier(ident) => ident.span(), Pattern::Mutable(_, span, _) | Pattern::Tuple(_, span) - | Pattern::Struct(_, _, span) => *span, + | Pattern::Struct(_, _, span) + | Pattern::Interned(_, span) => *span, } } pub fn name_ident(&self) -> &Ident { @@ -595,6 +597,39 @@ impl Pattern { other => panic!("Pattern::into_ident called on {other} pattern with no identifier"), } } + + pub(crate) fn try_as_expression(&self, interner: &NodeInterner) -> Option { + match self { + Pattern::Identifier(ident) => Some(Expression { + kind: ExpressionKind::Variable(Path::from_ident(ident.clone())), + span: ident.span(), + }), + Pattern::Mutable(_, _, _) => None, + Pattern::Tuple(patterns, span) => { + let mut expressions = Vec::new(); + for pattern in patterns { + expressions.push(pattern.try_as_expression(interner)?); + } + Some(Expression { kind: ExpressionKind::Tuple(expressions), span: *span }) + } + Pattern::Struct(path, patterns, span) => { + let mut fields = Vec::new(); + for (field, pattern) in patterns { + let expression = pattern.try_as_expression(interner)?; + fields.push((field.clone(), expression)); + } + Some(Expression { + kind: ExpressionKind::Constructor(Box::new(ConstructorExpression { + type_name: path.clone(), + fields, + struct_type: None, + })), + span: *span, + }) + } + Pattern::Interned(id, _) => interner.get_pattern(*id).try_as_expression(interner), + } + } } impl Recoverable for Pattern { @@ -905,6 +940,9 @@ impl Display for Pattern { let fields = vecmap(fields, |(name, pattern)| format!("{name}: {pattern}")); write!(f, "{} {{ {} }}", typename, fields.join(", ")) } + Pattern::Interned(_, _) => { + write!(f, "?Interned") + } } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs index 732cbee92328..cd42abb29c73 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -6,16 +6,24 @@ use crate::token::SecondaryAttribute; use iter_extended::vecmap; use noirc_errors::Span; +use super::Documented; + /// Ast node for a struct #[derive(Clone, Debug, PartialEq, Eq)] pub struct NoirStruct { pub name: Ident, pub attributes: Vec, pub generics: UnresolvedGenerics, - pub fields: Vec<(Ident, UnresolvedType)>, + pub fields: Vec>, pub span: Span, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StructField { + pub name: Ident, + pub typ: UnresolvedType, +} + impl Display for NoirStruct { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let generics = vecmap(&self.generics, |generic| generic.to_string()); @@ -23,8 +31,8 @@ impl Display for NoirStruct { writeln!(f, "struct {}{} {{", self.name, generics)?; - for (name, typ) in self.fields.iter() { - writeln!(f, " {name}: {typ},")?; + for field in self.fields.iter() { + writeln!(f, " {}: {},", field.item.name, field.item.typ)?; } write!(f, "}}") diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs index e3221f287d3e..0463a5b83926 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -10,7 +10,7 @@ use crate::ast::{ use crate::macros_api::SecondaryAttribute; use crate::node_interner::TraitId; -use super::GenericTypeArgs; +use super::{Documented, GenericTypeArgs}; /// AST node for trait definitions: /// `trait name { ... items ... }` @@ -20,7 +20,7 @@ pub struct NoirTrait { pub generics: UnresolvedGenerics, pub where_clause: Vec, pub span: Span, - pub items: Vec, + pub items: Vec>, pub attributes: Vec, } @@ -54,7 +54,7 @@ pub struct TypeImpl { pub type_span: Span, pub generics: UnresolvedGenerics, pub where_clause: Vec, - pub methods: Vec<(NoirFunction, Span)>, + pub methods: Vec<(Documented, Span)>, } /// Ast node for an implementation of a trait for a particular type @@ -71,7 +71,7 @@ pub struct NoirTraitImpl { pub where_clause: Vec, - pub items: Vec, + pub items: Vec>, } /// Represents a simple trait constraint such as `where Foo: TraitY` diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs index 64b479b5fd6b..fcb4e4c5dd1e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -12,8 +12,8 @@ use crate::{ UseTreeKind, }, node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, - QuotedTypeId, + ExprId, InternedExpressionKind, InternedPattern, InternedStatementKind, + InternedUnresolvedTypeData, QuotedTypeId, }, parser::{Item, ItemKind, ParsedSubModule}, token::{CustomAtrribute, SecondaryAttribute, Tokens}, @@ -30,6 +30,7 @@ use super::{ pub enum AttributeTarget { Module, Struct, + Trait, Function, } @@ -440,6 +441,8 @@ pub trait Visitor { true } + fn visit_interned_pattern(&mut self, _: &InternedPattern, _: Span) {} + fn visit_secondary_attribute( &mut self, _: &SecondaryAttribute, @@ -553,7 +556,7 @@ impl NoirTraitImpl { self.object_type.accept(visitor); for item in &self.items { - item.accept(visitor); + item.item.accept(visitor); } } } @@ -602,7 +605,7 @@ impl TypeImpl { self.object_type.accept(visitor); for (method, span) in &self.methods { - method.accept(*span, visitor); + method.item.accept(*span, visitor); } } } @@ -615,8 +618,12 @@ impl NoirTrait { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + for attribute in &self.attributes { + attribute.accept(AttributeTarget::Trait, visitor); + } + for item in &self.items { - item.accept(visitor); + item.item.accept(visitor); } } } @@ -701,8 +708,8 @@ impl NoirStruct { attribute.accept(AttributeTarget::Struct, visitor); } - for (_name, unresolved_type) in &self.fields { - unresolved_type.accept(visitor); + for field in &self.fields { + field.item.typ.accept(visitor); } } } @@ -1321,6 +1328,9 @@ impl Pattern { } } } + Pattern::Interned(id, span) => { + visitor.visit_interned_pattern(id, *span); + } } } } 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 951c5220707d..ed9265536f93 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -675,6 +675,7 @@ fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { stack.extend(pids.iter().map(|(_, pattern)| (pattern, is_mut))); vars.extend(pids.iter().map(|(id, _)| (id.clone(), false))); } + ast::Pattern::Interned(_, _) => (), } } vars @@ -701,6 +702,7 @@ fn pattern_to_string(pattern: &ast::Pattern) -> String { .join(", "), ) } + ast::Pattern::Interned(_, _) => "?Interned".to_string(), } } 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 0cd0824b6d9a..c2347adcbeee 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -6,6 +6,7 @@ use iter_extended::vecmap; use noirc_errors::{Location, Span}; use crate::{ + ast::Documented, hir::{ comptime::{Interpreter, InterpreterError, Value}, def_collector::{ @@ -24,7 +25,7 @@ use crate::{ Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, }, node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, - parser::{self, TopLevelStatement}, + parser::{self, TopLevelStatement, TopLevelStatementKind}, Type, TypeBindings, UnificationError, }; @@ -158,16 +159,18 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, ) { if let SecondaryAttribute::Custom(attribute) = attribute { - if let Err(error) = self.run_comptime_attribute_name_on_item( - &attribute.contents, - item.clone(), - span, - attribute.contents_span, - attribute_context, - generated_items, - ) { - self.errors.push(error); - } + self.elaborate_in_comptime_context(|this| { + if let Err(error) = this.run_comptime_attribute_name_on_item( + &attribute.contents, + item.clone(), + span, + attribute.contents_span, + attribute_context, + generated_items, + ) { + this.errors.push(error); + } + }); } } @@ -381,8 +384,8 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, location: Location, ) { - match item { - TopLevelStatement::Function(function) => { + match item.kind { + TopLevelStatementKind::Function(function) => { let module_id = self.module_id(); if let Some(id) = dc_mod::collect_function( @@ -391,6 +394,7 @@ impl<'context> Elaborator<'context> { &function, module_id, self.file, + item.doc_comments, &mut self.errors, ) { let functions = vec![(self.local_module, id, function)]; @@ -402,7 +406,7 @@ impl<'context> Elaborator<'context> { }); } } - TopLevelStatement::TraitImpl(mut trait_impl) => { + TopLevelStatementKind::TraitImpl(mut trait_impl) => { let (methods, associated_types, associated_constants) = dc_mod::collect_trait_impl_items( self.interner, @@ -432,11 +436,11 @@ impl<'context> Elaborator<'context> { resolved_trait_generics: Vec::new(), }); } - TopLevelStatement::Global(global) => { + TopLevelStatementKind::Global(global) => { let (global, error) = dc_mod::collect_global( self.interner, self.def_maps.get_mut(&self.crate_id).unwrap(), - global, + Documented::new(global, item.doc_comments), self.file, self.local_module, self.crate_id, @@ -447,11 +451,11 @@ impl<'context> Elaborator<'context> { self.errors.push(error); } } - TopLevelStatement::Struct(struct_def) => { + TopLevelStatementKind::Struct(struct_def) => { if let Some((type_id, the_struct)) = dc_mod::collect_struct( self.interner, self.def_maps.get_mut(&self.crate_id).unwrap(), - struct_def, + Documented::new(struct_def, item.doc_comments), self.file, self.local_module, self.crate_id, @@ -460,21 +464,21 @@ impl<'context> Elaborator<'context> { generated_items.types.insert(type_id, the_struct); } } - TopLevelStatement::Impl(r#impl) => { + TopLevelStatementKind::Impl(r#impl) => { let module = self.module_id(); dc_mod::collect_impl(self.interner, generated_items, r#impl, self.file, module); } // Assume that an error has already been issued - TopLevelStatement::Error => (), - - TopLevelStatement::Module(_) - | TopLevelStatement::Import(..) - | TopLevelStatement::Trait(_) - | TopLevelStatement::TypeAlias(_) - | TopLevelStatement::SubModule(_) - | TopLevelStatement::InnerAttribute(_) => { - let item = item.to_string(); + TopLevelStatementKind::Error => (), + + TopLevelStatementKind::Module(_) + | TopLevelStatementKind::Import(..) + | TopLevelStatementKind::Trait(_) + | TopLevelStatementKind::TypeAlias(_) + | TopLevelStatementKind::SubModule(_) + | TopLevelStatementKind::InnerAttribute(_) => { + let item = item.kind.to_string(); let error = InterpreterError::UnsupportedTopLevelItemUnquote { item, location }; self.errors.push(error.into_compilation_error_pair()); } @@ -597,4 +601,34 @@ impl<'context> Elaborator<'context> { } } } + + /// Perform the given function in a comptime context. + /// This will set the `in_comptime_context` flag on `self` as well as + /// push a new function context to resolve any trait constraints early. + pub(super) fn elaborate_in_comptime_context(&mut self, f: impl FnOnce(&mut Self) -> T) -> T { + let old_comptime_value = std::mem::replace(&mut self.in_comptime_context, true); + // We have to push a new FunctionContext so that we can resolve any constraints + // in this comptime block early before the function as a whole finishes elaborating. + // Otherwise the interpreter below may find expressions for which the underlying trait + // call is not yet solved for. + self.function_context.push(Default::default()); + + let result = f(self); + + self.check_and_pop_function_context(); + self.in_comptime_context = old_comptime_value; + result + } + + /// True if we're currently within a `comptime` block, function, or global + pub(super) fn in_comptime_context(&self) -> bool { + self.in_comptime_context + || match self.current_item { + Some(DependencyId::Function(id)) => { + self.interner.function_modifiers(&id).is_comptime + } + Some(DependencyId::Global(id)) => self.interner.get_global_definition(id).comptime, + _ => false, + } + } } 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 beede7a3a303..15d6ed5506bb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -54,7 +54,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), - ExpressionKind::Quote(quote) => self.elaborate_quote(quote), + ExpressionKind::Quote(quote) => self.elaborate_quote(quote, expr.span), ExpressionKind::Comptime(comptime, _) => { return self.elaborate_comptime_block(comptime, expr.span) } @@ -307,7 +307,13 @@ impl<'context> Elaborator<'context> { let mut arguments = Vec::with_capacity(call.arguments.len()); let args = vecmap(call.arguments, |arg| { let span = arg.span; - let (arg, typ) = self.elaborate_expression(arg); + + let (arg, typ) = if call.is_macro_call { + self.elaborate_in_comptime_context(|this| this.elaborate_expression(arg)) + } else { + self.elaborate_expression(arg) + }; + arguments.push(arg); (typ, arg, span) }); @@ -735,20 +741,21 @@ impl<'context> Elaborator<'context> { (expr, Type::Function(arg_types, Box::new(body_type), Box::new(env_type), false)) } - fn elaborate_quote(&mut self, mut tokens: Tokens) -> (HirExpression, Type) { + fn elaborate_quote(&mut self, mut tokens: Tokens, span: Span) -> (HirExpression, Type) { tokens = self.find_unquoted_exprs_tokens(tokens); - (HirExpression::Quote(tokens), Type::Quoted(QuotedType::Quoted)) + + if self.in_comptime_context() { + (HirExpression::Quote(tokens), Type::Quoted(QuotedType::Quoted)) + } else { + self.push_err(ResolverError::QuoteInRuntimeCode { span }); + (HirExpression::Error, Type::Quoted(QuotedType::Quoted)) + } } fn elaborate_comptime_block(&mut self, block: BlockExpression, span: Span) -> (ExprId, Type) { - // We have to push a new FunctionContext so that we can resolve any constraints - // in this comptime block early before the function as a whole finishes elaborating. - // Otherwise the interpreter below may find expressions for which the underlying trait - // call is not yet solved for. - self.function_context.push(Default::default()); - let (block, _typ) = self.elaborate_block_expression(block); - - self.check_and_pop_function_context(); + let (block, _typ) = + self.elaborate_in_comptime_context(|this| this.elaborate_block_expression(block)); + let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_block(block); let (id, typ) = self.inline_comptime_value(value, 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 d321d04bef9d..6871152edb5a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -158,6 +158,10 @@ pub struct Elaborator<'context> { /// Initially empty, it is set whenever a new top-level item is resolved. local_module: LocalModuleId, + /// True if we're elaborating a comptime item such as a comptime function, + /// block, global, or attribute. + in_comptime_context: bool, + crate_id: CrateId, /// The scope of --debug-comptime, or None if unset @@ -215,6 +219,7 @@ impl<'context> Elaborator<'context> { unresolved_globals: BTreeMap::new(), current_trait: None, interpreter_call_stack, + in_comptime_context: false, } } @@ -1248,7 +1253,9 @@ impl<'context> Elaborator<'context> { let struct_def = this.interner.get_struct(struct_id); this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); - let fields = vecmap(&unresolved.fields, |(ident, typ)| { + let fields = vecmap(&unresolved.fields, |field| { + let ident = &field.item.name; + let typ = &field.item.typ; (ident.clone(), this.resolve_type(typ.clone())) }); @@ -1287,7 +1294,12 @@ impl<'context> Elaborator<'context> { let comptime = let_stmt.comptime; - let (let_statement, _typ) = self.elaborate_let(let_stmt, Some(global_id)); + let (let_statement, _typ) = if comptime { + self.elaborate_in_comptime_context(|this| this.elaborate_let(let_stmt, Some(global_id))) + } else { + self.elaborate_let(let_stmt, Some(global_id)) + }; + let statement_id = self.interner.get_global(global_id).let_statement; self.interner.replace_statement(statement_id, let_statement); @@ -1322,9 +1334,7 @@ impl<'context> Elaborator<'context> { .lookup_id(definition_id, location) .expect("The global should be defined since evaluate_let did not error"); - self.debug_comptime(location, |interner| { - interner.get_global(global_id).let_statement.to_display_ast(interner).kind - }); + self.debug_comptime(location, |interner| value.display(interner).to_string()); self.interner.get_global_mut(global_id).value = Some(value); } @@ -1421,21 +1431,6 @@ impl<'context> Elaborator<'context> { } } - /// True if we're currently within a `comptime` block, function, or global - fn in_comptime_context(&self) -> bool { - // The first context is the global context, followed by the function-specific context. - // Any context after that is a `comptime {}` block's. - if self.function_context.len() > 2 { - return true; - } - - match self.current_item { - Some(DependencyId::Function(id)) => self.interner.function_modifiers(&id).is_comptime, - Some(DependencyId::Global(id)) => self.interner.get_global_definition(id).comptime, - _ => false, - } - } - /// True if we're currently within a constrained function. /// Defaults to `true` if the current function is unknown. fn in_constrained_function(&self) -> bool { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs index 06c153d4c100..f738657fd236 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -143,6 +143,17 @@ impl<'context> Elaborator<'context> { mutable, new_definitions, ), + Pattern::Interned(id, _) => { + let pattern = self.interner.get_pattern(id).clone(); + self.elaborate_pattern_mut( + pattern, + expected_type, + definition, + mutable, + new_definitions, + global_id, + ) + } } } 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 d7d330f891aa..9e29978a9d5b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -441,14 +441,9 @@ impl<'context> Elaborator<'context> { } fn elaborate_comptime_statement(&mut self, statement: Statement) -> (HirStatement, Type) { - // We have to push a new FunctionContext so that we can resolve any constraints - // in this comptime block early before the function as a whole finishes elaborating. - // Otherwise the interpreter below may find expressions for which the underlying trait - // call is not yet solved for. - self.function_context.push(Default::default()); let span = statement.span; - let (hir_statement, _typ) = self.elaborate_statement(statement); - self.check_and_pop_function_context(); + let (hir_statement, _typ) = + self.elaborate_in_comptime_context(|this| this.elaborate_statement(statement)); let mut interpreter = self.setup_interpreter(); let value = interpreter.evaluate_statement(hir_statement); let (expr, typ) = self.inline_comptime_value(value, span); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index f651630baa2e..d6bfd3aa6477 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -13,7 +13,7 @@ use crate::{ BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NodeInterner, NoirFunction, Param, Pattern, UnresolvedType, Visibility, }, - node_interner::{FuncId, TraitId}, + node_interner::{FuncId, ReferenceId, TraitId}, token::Attributes, Kind, ResolvedGeneric, Type, TypeBindings, TypeVariableKind, }; @@ -74,7 +74,7 @@ impl<'context> Elaborator<'context> { return_type, where_clause, body: _, - } = item + } = &item.item { self.recover_generics(|this| { let the_trait = this.interner.get_trait(trait_id); @@ -107,6 +107,11 @@ impl<'context> Elaborator<'context> { func_id, ); + if !item.doc_comments.is_empty() { + let id = ReferenceId::Function(func_id); + this.interner.set_doc_comments(id, item.doc_comments.clone()); + } + let func_meta = this.interner.function_meta(&func_id); let arguments = vecmap(&func_meta.parameters.0, |(_, typ, _)| typ.clone()); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs index 452aef74b36c..094594a50ac5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/graph/mod.rs @@ -16,6 +16,10 @@ pub enum CrateId { Root(usize), Crate(usize), Stdlib(usize), + /// The special case of running the compiler against the stdlib. + /// In that case there's only one crate, and it's both the root + /// crate and the stdlib crate. + RootAndStdlib(usize), Dummy, } @@ -25,11 +29,17 @@ impl CrateId { } pub fn is_stdlib(&self) -> bool { - matches!(self, CrateId::Stdlib(_)) + match self { + CrateId::Stdlib(_) | CrateId::RootAndStdlib(_) => true, + CrateId::Root(_) | CrateId::Crate(_) | CrateId::Dummy => false, + } } pub fn is_root(&self) -> bool { - matches!(self, CrateId::Root(_)) + match self { + CrateId::Root(_) | CrateId::RootAndStdlib(_) => true, + CrateId::Stdlib(_) | CrateId::Crate(_) | CrateId::Dummy => false, + } } } @@ -178,7 +188,7 @@ impl CrateGraph { Some((CrateId::Root(_), _)) => { panic!("ICE: Tried to re-add the root crate as a regular crate") } - Some((CrateId::Stdlib(_), _)) => { + Some((CrateId::Stdlib(_), _)) | Some((CrateId::RootAndStdlib(_), _)) => { panic!("ICE: Tried to re-add the stdlib crate as a regular crate") } Some((CrateId::Dummy, _)) => { @@ -212,6 +222,28 @@ impl CrateGraph { crate_id } + pub fn add_crate_root_and_stdlib(&mut self, file_id: FileId) -> CrateId { + for (crate_id, crate_data) in self.arena.iter() { + if crate_id.is_root() { + panic!("ICE: Cannot add two crate roots to a graph - use `add_crate` instead"); + } + + if crate_id.is_stdlib() { + panic!("ICE: Cannot add two stdlib crates to a graph - use `add_crate` instead"); + } + + if crate_data.root_file_id == file_id { + panic!("ICE: This FileId was already added to the CrateGraph") + } + } + + let data = CrateData { root_file_id: file_id, dependencies: Vec::new() }; + let crate_id = CrateId::RootAndStdlib(self.arena.len()); + let prev = self.arena.insert(crate_id, data); + assert!(prev.is_none()); + crate_id + } + pub fn iter_keys(&self) -> impl Iterator + '_ { self.arena.keys().copied() } 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 4f419a2119d6..9c4761f31560 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 @@ -220,6 +220,14 @@ pub enum InterpreterError { duplicate_location: Location, existing_location: Location, }, + CannotResolveExpression { + location: Location, + expression: String, + }, + CannotSetFunctionBody { + location: Location, + expression: String, + }, // These cases are not errors, they are just used to prevent us from running more code // until the loop can be resumed properly. These cases will never be displayed to users. @@ -291,7 +299,9 @@ impl InterpreterError { | InterpreterError::InvalidAttribute { location, .. } | InterpreterError::GenericNameShouldBeAnIdent { location, .. } | InterpreterError::DuplicateGeneric { duplicate_location: location, .. } - | InterpreterError::TypeAnnotationsNeededForMethodCall { location } => *location, + | InterpreterError::TypeAnnotationsNeededForMethodCall { location } + | InterpreterError::CannotResolveExpression { location, .. } + | InterpreterError::CannotSetFunctionBody { location, .. } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) @@ -602,7 +612,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } InterpreterError::GenericNameShouldBeAnIdent { name, location } => { let msg = - "Generic name needs to be a valid identifier (one word beginning with a letter)" + "Generic name needs to be a valid identifer (one word beginning with a letter)" .to_string(); let secondary = format!("`{name}` is not a valid identifier"); CustomDiagnostic::simple_error(msg, secondary, location.span) @@ -626,6 +636,14 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { ); error } + InterpreterError::CannotResolveExpression { location, expression } => { + let msg = format!("Cannot resolve expression `{expression}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } + InterpreterError::CannotSetFunctionBody { location, expression } => { + let msg = format!("`{expression}` is not a valid function body"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } } } } 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 c3aeac4aec4c..899d62ecb614 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 @@ -22,8 +22,8 @@ use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{ ArrayLiteral, BlockExpression, ConstrainKind, Expression, ExpressionKind, FunctionKind, - FunctionReturnType, IntegerBitSize, LValue, Literal, Statement, StatementKind, UnaryOp, - UnresolvedType, UnresolvedTypeData, Visibility, + FunctionReturnType, IntegerBitSize, LValue, Literal, Pattern, Statement, StatementKind, + UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }, hir::def_collector::dc_crate::CollectedItems, hir::{ @@ -78,6 +78,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "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_let" => expr_as_let(interner, arguments, return_type, location), "expr_as_member_access" => { expr_as_member_access(interner, arguments, return_type, location) } @@ -1500,6 +1501,41 @@ fn expr_as_integer( }) } +// fn as_let(self) -> Option<(Expr, Option, Expr)> +fn expr_as_let( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, +) -> IResult { + expr_as(interner, arguments, return_type.clone(), location, |expr| match expr { + ExprValue::Statement(StatementKind::Let(let_statement)) => { + 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(), 3); + tuple_types.pop().unwrap(); + let option_type = tuple_types.pop().unwrap(); + + let typ = if let_statement.r#type.typ == UnresolvedTypeData::Unspecified { + None + } else { + Some(Value::UnresolvedType(let_statement.r#type.typ)) + }; + + let typ = option(option_type, typ).ok()?; + + Some(Value::Tuple(vec![ + Value::pattern(let_statement.pattern), + typ, + Value::expression(let_statement.expression.kind), + ])) + } + _ => None, + }) +} + // fn as_member_access(self) -> Option<(Expr, Quoted)> fn expr_as_member_access( interner: &NodeInterner, @@ -1777,27 +1813,33 @@ fn expr_resolve( interpreter.current_function }; - let value = - interpreter.elaborate_in_function(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)) + interpreter.elaborate_in_function(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); + Ok(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); + Ok(Value::TypedExpr(TypedExpr::StmtId(stmt_id))) + } + ExprValue::LValue(lvalue) => { + let expr = lvalue.as_expression(); + let (expr_id, _) = elaborator.elaborate_expression(expr); + Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) + } + ExprValue::Pattern(pattern) => { + if let Some(expression) = pattern.try_as_expression(elaborator.interner) { + let (expr_id, _) = elaborator.elaborate_expression(expression); + Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) + } else { + let expression = Value::pattern(pattern).display(elaborator.interner).to_string(); + let location = self_argument_location; + Err(InterpreterError::CannotResolveExpression { location, expression }) } - }); - - Ok(value) + } + }) } fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> ExprValue { @@ -1819,6 +1861,9 @@ fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> Expr ExprValue::LValue(LValue::Interned(id, span)) => { expr_value = ExprValue::LValue(interner.get_lvalue(id, span).clone()); } + ExprValue::Pattern(Pattern::Interned(id, _)) => { + expr_value = ExprValue::Pattern(interner.get_pattern(id).clone()); + } _ => break, } } @@ -2031,6 +2076,16 @@ fn function_def_set_body( }), ExprValue::Statement(statement_kind) => statement_kind, ExprValue::LValue(lvalue) => StatementKind::Expression(lvalue.as_expression()), + ExprValue::Pattern(pattern) => { + if let Some(expression) = pattern.try_as_expression(interpreter.elaborator.interner) { + StatementKind::Expression(expression) + } else { + let expression = + Value::pattern(pattern).display(interpreter.elaborator.interner).to_string(); + let location = body_location; + return Err(InterpreterError::CannotSetFunctionBody { location, expression }); + } + } }; let statement = Statement { kind: statement_kind, span: body_location.span }; 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 f90d50807b82..6e72866bec02 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 @@ -5,8 +5,8 @@ use noirc_errors::Location; use crate::{ ast::{ - BlockExpression, ExpressionKind, IntegerBitSize, LValue, Signedness, StatementKind, - UnresolvedTypeData, + BlockExpression, ExpressionKind, IntegerBitSize, LValue, Pattern, Signedness, + StatementKind, UnresolvedTypeData, }, elaborator::Elaborator, hir::{ @@ -191,6 +191,9 @@ pub(crate) fn get_expr( ExprValue::LValue(LValue::Interned(id, _)) => { Ok(ExprValue::LValue(interner.get_lvalue(id, location.span).clone())) } + ExprValue::Pattern(Pattern::Interned(id, _)) => { + Ok(ExprValue::Pattern(interner.get_pattern(id).clone())) + } _ => Ok(expr), }, value => type_mismatch(value, Type::Quoted(QuotedType::Expr), location), 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 04c557552bd7..f6450175955a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::Display, rc::Rc}; +use std::{borrow::Cow, fmt::Display, rc::Rc, vec}; use acvm::{AcirField, FieldElement}; use chumsky::Parser; @@ -9,11 +9,11 @@ use strum_macros::Display; use crate::{ ast::{ - ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, - ConstrainStatement, ConstructorExpression, ForLoopStatement, ForRange, Ident, IfExpression, - IndexExpression, InfixExpression, IntegerBitSize, LValue, Lambda, LetStatement, - MemberAccessExpression, MethodCallExpression, PrefixExpression, Signedness, Statement, - StatementKind, UnresolvedTypeData, + ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, + CastExpression, ConstrainStatement, ConstructorExpression, ForLoopStatement, ForRange, + GenericTypeArgs, Ident, IfExpression, IndexExpression, InfixExpression, IntegerBitSize, + LValue, Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, Pattern, + PrefixExpression, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, hir_def::{ @@ -78,6 +78,7 @@ pub enum ExprValue { Expression(ExpressionKind), Statement(StatementKind), LValue(LValue), + Pattern(Pattern), } #[derive(Debug, Clone, PartialEq, Eq, Display)] @@ -99,6 +100,10 @@ impl Value { Value::Expr(ExprValue::LValue(lvaue)) } + pub(crate) fn pattern(pattern: Pattern) -> Self { + Value::Expr(ExprValue::Pattern(pattern)) + } + pub(crate) fn get_type(&self) -> Cow { Cow::Owned(match self { Value::Unit => Type::Unit, @@ -273,7 +278,8 @@ impl Value { }) } Value::Expr(ExprValue::LValue(lvalue)) => lvalue.as_expression().kind, - Value::TypedExpr(..) + Value::Expr(ExprValue::Pattern(_)) + | Value::TypedExpr(..) | Value::Pointer(..) | Value::StructDefinition(_) | Value::TraitConstraint(..) @@ -443,6 +449,9 @@ impl Value { Value::Expr(ExprValue::LValue(lvalue)) => { Token::InternedLValue(interner.push_lvalue(lvalue)) } + Value::Expr(ExprValue::Pattern(pattern)) => { + Token::InternedPattern(interner.push_pattern(pattern)) + } Value::UnresolvedType(typ) => { Token::InternedUnresolvedTypeData(interner.push_unresolved_type_data(typ)) } @@ -671,6 +680,9 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::Expr(ExprValue::LValue(lvalue)) => { write!(f, "{}", remove_interned_in_lvalue(self.interner, lvalue.clone())) } + Value::Expr(ExprValue::Pattern(pattern)) => { + write!(f, "{}", remove_interned_in_pattern(self.interner, pattern.clone())) + } Value::TypedExpr(TypedExpr::ExprId(id)) => { let hir_expr = self.interner.expression(id); let expr = hir_expr.to_display_ast(self.interner, Span::default()); @@ -682,12 +694,7 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { write!(f, "{}", stmt.kind) } 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) - } + write!(f, "{}", remove_interned_in_unresolved_type_data(self.interner, typ.clone())) } } } @@ -729,6 +736,10 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); value.display(self.interner).fmt(f) } + Token::InternedPattern(id) => { + let value = Value::pattern(Pattern::Interned(*id, Span::default())); + value.display(self.interner).fmt(f) + } Token::UnquoteMarker(id) => { let value = Value::TypedExpr(TypedExpr::ExprId(*id)); value.display(self.interner).fmt(f) @@ -901,7 +912,9 @@ fn remove_interned_in_statement_kind( ) -> StatementKind { match statement { StatementKind::Let(let_statement) => StatementKind::Let(LetStatement { + pattern: remove_interned_in_pattern(interner, let_statement.pattern), expression: remove_interned_in_expression(interner, let_statement.expression), + r#type: remove_interned_in_unresolved_type(interner, let_statement.r#type), ..let_statement }), StatementKind::Constrain(constrain) => StatementKind::Constrain(ConstrainStatement( @@ -966,3 +979,120 @@ fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue } } } + +fn remove_interned_in_unresolved_type( + interner: &NodeInterner, + typ: UnresolvedType, +) -> UnresolvedType { + UnresolvedType { + typ: remove_interned_in_unresolved_type_data(interner, typ.typ), + span: typ.span, + } +} + +fn remove_interned_in_unresolved_type_data( + interner: &NodeInterner, + typ: UnresolvedTypeData, +) -> UnresolvedTypeData { + match typ { + UnresolvedTypeData::Array(expr, typ) => UnresolvedTypeData::Array( + expr, + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Slice(typ) => { + UnresolvedTypeData::Slice(Box::new(remove_interned_in_unresolved_type(interner, *typ))) + } + UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( + expr, + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Parenthesized(typ) => UnresolvedTypeData::Parenthesized(Box::new( + remove_interned_in_unresolved_type(interner, *typ), + )), + UnresolvedTypeData::Named(path, generic_type_args, is_synthesized) => { + UnresolvedTypeData::Named( + path, + remove_interned_in_generic_type_args(interner, generic_type_args), + is_synthesized, + ) + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + UnresolvedTypeData::TraitAsType( + path, + remove_interned_in_generic_type_args(interner, generic_type_args), + ) + } + UnresolvedTypeData::MutableReference(typ) => UnresolvedTypeData::MutableReference( + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Tuple(types) => UnresolvedTypeData::Tuple(vecmap(types, |typ| { + remove_interned_in_unresolved_type(interner, typ) + })), + UnresolvedTypeData::Function(arg_types, ret_type, env_type, unconstrained) => { + UnresolvedTypeData::Function( + vecmap(arg_types, |typ| remove_interned_in_unresolved_type(interner, typ)), + Box::new(remove_interned_in_unresolved_type(interner, *ret_type)), + Box::new(remove_interned_in_unresolved_type(interner, *env_type)), + unconstrained, + ) + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(Box::new(AsTraitPath { + typ: remove_interned_in_unresolved_type(interner, as_trait_path.typ), + trait_generics: remove_interned_in_generic_type_args( + interner, + as_trait_path.trait_generics, + ), + ..*as_trait_path + })) + } + UnresolvedTypeData::Interned(id) => interner.get_unresolved_type_data(id).clone(), + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => typ, + } +} + +fn remove_interned_in_generic_type_args( + interner: &NodeInterner, + args: GenericTypeArgs, +) -> GenericTypeArgs { + GenericTypeArgs { + ordered_args: vecmap(args.ordered_args, |typ| { + remove_interned_in_unresolved_type(interner, typ) + }), + named_args: vecmap(args.named_args, |(name, typ)| { + (name, remove_interned_in_unresolved_type(interner, typ)) + }), + } +} + +// Returns a new Pattern where all Interned Patterns have been turned into Pattern. +fn remove_interned_in_pattern(interner: &NodeInterner, pattern: Pattern) -> Pattern { + match pattern { + Pattern::Identifier(_) => pattern, + Pattern::Mutable(pattern, span, is_synthesized) => Pattern::Mutable( + Box::new(remove_interned_in_pattern(interner, *pattern)), + span, + is_synthesized, + ), + Pattern::Tuple(patterns, span) => Pattern::Tuple( + vecmap(patterns, |pattern| remove_interned_in_pattern(interner, pattern)), + span, + ), + Pattern::Struct(path, patterns, span) => { + let patterns = vecmap(patterns, |(name, pattern)| { + (name, remove_interned_in_pattern(interner, pattern)) + }); + Pattern::Struct(path, patterns, span) + } + Pattern::Interned(id, _) => interner.get_pattern(id).clone(), + } +} 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 7ee1840690a8..6265d0e65f2b 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 @@ -313,6 +313,11 @@ impl DefCollector { let crate_root = def_map.root; let mut def_collector = DefCollector::new(def_map); + let module_id = ModuleId { krate: crate_id, local_id: crate_root }; + context + .def_interner + .set_doc_comments(ReferenceId::Module(module_id), ast.inner_doc_comments.clone()); + // Collecting module declarations with ModCollector // and lowering the functions // i.e. Use a mod collector to collect the nodes at the root module diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 6c1b7632a2e4..d93b708c91da 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1,3 +1,4 @@ +use core::str; use std::path::Path; use std::rc::Rc; use std::vec; @@ -10,13 +11,13 @@ use num_traits::Num; use rustc_hash::FxHashMap as HashMap; use crate::ast::{ - FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, TraitImplItem, TraitItem, - TypeImpl, + Documented, FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, + NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Pattern, TraitImplItem, + TraitItem, TypeImpl, }; use crate::hir::resolution::errors::ResolverError; use crate::macros_api::{Expression, NodeInterner, StructId, UnresolvedType, UnresolvedTypeData}; -use crate::node_interner::ModuleAttributes; +use crate::node_interner::{ModuleAttributes, ReferenceId}; use crate::token::SecondaryAttribute; use crate::usage_tracker::UnusedItem; use crate::{ @@ -146,7 +147,7 @@ impl<'a> ModCollector<'a> { fn collect_globals( &mut self, context: &mut Context, - globals: Vec, + globals: Vec>, crate_id: CrateId, ) -> Vec<(CompilationError, fm::FileId)> { let mut errors = vec![]; @@ -235,7 +236,7 @@ impl<'a> ModCollector<'a> { fn collect_functions( &mut self, context: &mut Context, - functions: Vec, + functions: Vec>, krate: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut unresolved_functions = UnresolvedFunctions { @@ -252,9 +253,10 @@ impl<'a> ModCollector<'a> { let Some(func_id) = collect_function( &mut context.def_interner, &mut self.def_collector.def_map, - &function, + &function.item, module, self.file_id, + function.doc_comments, &mut errors, ) else { continue; @@ -266,7 +268,7 @@ impl<'a> ModCollector<'a> { // and replace it // With this method we iterate each function in the Crate and not each module // This may not be great because we have to pull the module_data for each function - unresolved_functions.push_fn(self.module_id, func_id, function); + unresolved_functions.push_fn(self.module_id, func_id, function.item); } self.def_collector.items.functions.push(unresolved_functions); @@ -279,7 +281,7 @@ impl<'a> ModCollector<'a> { fn collect_structs( &mut self, context: &mut Context, - types: Vec, + types: Vec>, krate: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut definition_errors = vec![]; @@ -304,11 +306,13 @@ impl<'a> ModCollector<'a> { fn collect_type_aliases( &mut self, context: &mut Context, - type_aliases: Vec, + type_aliases: Vec>, krate: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; for type_alias in type_aliases { + let doc_comments = type_alias.doc_comments; + let type_alias = type_alias.item; let name = type_alias.name.clone(); // And store the TypeId -> TypeAlias mapping somewhere it is reachable @@ -328,6 +332,8 @@ impl<'a> ModCollector<'a> { let type_alias_id = context.def_interner.push_type_alias(&unresolved, resolved_generics); + context.def_interner.set_doc_comments(ReferenceId::Alias(type_alias_id), doc_comments); + // Add the type alias to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] .declare_type_alias(name.clone(), type_alias_id); @@ -357,11 +363,13 @@ impl<'a> ModCollector<'a> { fn collect_traits( &mut self, context: &mut Context, - traits: Vec, + traits: Vec>, krate: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; for trait_definition in traits { + let doc_comments = trait_definition.doc_comments; + let trait_definition = trait_definition.item; let name = trait_definition.name.clone(); // Create the corresponding module for the trait namespace @@ -381,6 +389,8 @@ impl<'a> ModCollector<'a> { } }; + context.def_interner.set_doc_comments(ReferenceId::Trait(trait_id), doc_comments); + // Add the trait to scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] .declare_trait(name.clone(), trait_id); @@ -406,7 +416,7 @@ impl<'a> ModCollector<'a> { let mut associated_types = Generics::new(); for trait_item in &trait_definition.items { - match trait_item { + match &trait_item.item { TraitItem::Function { name, generics, @@ -434,6 +444,13 @@ impl<'a> ModCollector<'a> { .def_interner .push_function_definition(func_id, modifiers, trait_id.0, location); + if !trait_item.doc_comments.is_empty() { + context.def_interner.set_doc_comments( + ReferenceId::Function(func_id), + trait_item.doc_comments.clone(), + ); + } + match self.def_collector.def_map.modules[trait_id.0.local_id.0] .declare_function(name.clone(), ItemVisibility::Public, func_id) { @@ -559,12 +576,15 @@ impl<'a> ModCollector<'a> { context: &mut Context, crate_id: CrateId, parent_module_id: LocalModuleId, - submodules: Vec, + submodules: Vec>, file_id: FileId, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; for submodule in submodules { + let mut doc_comments = submodule.doc_comments; + let submodule = submodule.item; + match self.push_child_module( context, &submodule.name, @@ -584,6 +604,16 @@ impl<'a> ModCollector<'a> { false, ); + if !(doc_comments.is_empty() + && submodule.contents.inner_doc_comments.is_empty()) + { + doc_comments.extend(submodule.contents.inner_doc_comments.clone()); + + context + .def_interner + .set_doc_comments(ReferenceId::Module(child), doc_comments); + } + errors.extend(collect_defs( self.def_collector, submodule.contents, @@ -605,15 +635,19 @@ impl<'a> ModCollector<'a> { /// Search for a module named `mod_name` /// Parse it, add it as a child to the parent module in which it was declared /// and then collect all definitions of the child module + #[allow(clippy::too_many_arguments)] fn parse_module_declaration( &mut self, context: &mut Context, - mod_decl: ModuleDeclaration, + mod_decl: Documented, crate_id: CrateId, parent_file_id: FileId, parent_module_id: LocalModuleId, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { + let mut doc_comments = mod_decl.doc_comments; + let mod_decl = mod_decl.item; + let mut errors: Vec<(CompilationError, FileId)> = vec![]; let child_file_id = match find_module(&context.file_manager, self.file_id, &mod_decl.ident) { @@ -691,6 +725,14 @@ impl<'a> ModCollector<'a> { // Track that the "foo" in `mod foo;` points to the module "foo" context.def_interner.add_module_reference(child_mod_id, location); + if !(doc_comments.is_empty() && ast.inner_doc_comments.is_empty()) { + doc_comments.extend(ast.inner_doc_comments.clone()); + + context + .def_interner + .set_doc_comments(ReferenceId::Module(child_mod_id), doc_comments); + } + errors.extend(collect_defs( self.def_collector, ast, @@ -827,6 +869,7 @@ pub fn collect_function( function: &NoirFunction, module: ModuleId, file: FileId, + doc_comments: Vec, errors: &mut Vec<(CompilationError, FileId)>, ) -> Option { if let Some(field) = function.attributes().get_field_attribute() { @@ -858,6 +901,8 @@ pub fn collect_function( interner.usage_tracker.add_unused_item(module, name.clone(), item, visibility); } + interner.set_doc_comments(ReferenceId::Function(func_id), doc_comments); + // Add function to scope/ns of the module let result = def_map.modules[module.local_id.0].declare_function(name, visibility, func_id); if let Err((first_def, second_def)) = result { @@ -874,12 +919,15 @@ pub fn collect_function( pub fn collect_struct( interner: &mut NodeInterner, def_map: &mut CrateDefMap, - struct_definition: NoirStruct, + struct_definition: Documented, file_id: FileId, module_id: LocalModuleId, krate: CrateId, definition_errors: &mut Vec<(CompilationError, FileId)>, ) -> Option<(StructId, UnresolvedStruct)> { + let doc_comments = struct_definition.doc_comments; + let struct_definition = struct_definition.item; + check_duplicate_field_names(&struct_definition, file_id, definition_errors); let name = struct_definition.name.clone(); @@ -915,6 +963,15 @@ pub fn collect_struct( } }; + interner.set_doc_comments(ReferenceId::Struct(id), doc_comments); + + for (index, field) in unresolved.struct_def.fields.iter().enumerate() { + if !field.doc_comments.is_empty() { + interner + .set_doc_comments(ReferenceId::StructMember(id, index), field.doc_comments.clone()); + } + } + // Add the struct to scope so its path can be looked up later let result = def_map.modules[module_id.0].declare_struct(name.clone(), id); @@ -945,12 +1002,15 @@ pub fn collect_impl( let mut unresolved_functions = UnresolvedFunctions { file_id, functions: Vec::new(), trait_id: None, self_type: None }; - for (mut method, _) in r#impl.methods { + for (method, _) in r#impl.methods { + let doc_comments = method.doc_comments; + let mut method = method.item; let func_id = interner.push_empty_fn(); method.def.where_clause.extend(r#impl.where_clause.clone()); let location = Location::new(method.span(), file_id); interner.push_function(func_id, &method.def, module_id, location); unresolved_functions.push_fn(module_id.local_id, func_id, method); + interner.set_doc_comments(ReferenceId::Function(func_id), doc_comments); } let key = (r#impl.object_type, module_id.local_id); @@ -1064,11 +1124,12 @@ pub(crate) fn collect_trait_impl_items( let module = ModuleId { krate, local_id }; for item in std::mem::take(&mut trait_impl.items) { - match item { + match item.item { TraitImplItem::Function(impl_method) => { let func_id = interner.push_empty_fn(); let location = Location::new(impl_method.span(), file_id); interner.push_function(func_id, &impl_method.def, module, location); + interner.set_doc_comments(ReferenceId::Function(func_id), item.doc_comments); unresolved_functions.push_fn(local_id, func_id, impl_method); } TraitImplItem::Constant(name, typ, expr) => { @@ -1086,11 +1147,14 @@ pub(crate) fn collect_trait_impl_items( pub(crate) fn collect_global( interner: &mut NodeInterner, def_map: &mut CrateDefMap, - global: LetStatement, + global: Documented, file_id: FileId, module_id: LocalModuleId, crate_id: CrateId, ) -> (UnresolvedGlobal, Option<(CompilationError, FileId)>) { + let doc_comments = global.doc_comments; + let global = global.item; + let name = global.pattern.name_ident().clone(); let global_id = interner.push_empty_global( @@ -1112,6 +1176,8 @@ pub(crate) fn collect_global( (err.into(), file_id) }); + interner.set_doc_comments(ReferenceId::Global(global_id), doc_comments); + let global = UnresolvedGlobal { file_id, module_id, global_id, stmt_def: global }; (global, error) } @@ -1122,7 +1188,9 @@ fn check_duplicate_field_names( definition_errors: &mut Vec<(CompilationError, FileId)>, ) { let mut seen_field_names = std::collections::HashSet::new(); - for (field_name, _) in &struct_definition.fields { + for field in &struct_definition.fields { + let field_name = &field.item.name; + if seen_field_names.insert(field_name) { continue; } 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 e74468bdf18d..5abc94b89a25 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 @@ -124,6 +124,8 @@ pub enum ResolverError { AssociatedConstantsMustBeNumeric { span: Span }, #[error("Overflow in `{lhs} {op} {rhs}`")] OverflowInType { lhs: u32, op: crate::BinaryTypeOperator, rhs: u32, span: Span }, + #[error("`quote` cannot be used in runtime code")] + QuoteInRuntimeCode { span: Span }, } impl ResolverError { @@ -504,6 +506,13 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) } + ResolverError::QuoteInRuntimeCode { span } => { + Diagnostic::simple_error( + "`quote` cannot be used in runtime code".to_string(), + "Wrap this in a `comptime` block or function to use it".to_string(), + *span, + ) + }, } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs index b7492396c90a..7fbbb4fccefb 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/lexer.rs @@ -2,9 +2,7 @@ use crate::token::{Attribute, DocStyle}; use super::{ errors::LexerErrorKind, - token::{ - token_to_borrowed_token, BorrowedToken, IntType, Keyword, SpannedToken, Token, Tokens, - }, + token::{IntType, Keyword, SpannedToken, Token, Tokens}, }; use acvm::{AcirField, FieldElement}; use noirc_errors::{Position, Span}; @@ -26,21 +24,6 @@ pub struct Lexer<'a> { pub type SpannedTokenResult = Result; -pub(crate) fn from_spanned_token_result( - token_result: &SpannedTokenResult, -) -> Result<(usize, BorrowedToken<'_>, usize), LexerErrorKind> { - token_result - .as_ref() - .map(|spanned_token| { - ( - spanned_token.to_span().start() as usize, - token_to_borrowed_token(spanned_token.into()), - spanned_token.to_span().end() as usize, - ) - }) - .map_err(Clone::clone) -} - impl<'a> Lexer<'a> { /// Given a source file of noir code, return all the tokens in the file /// in order, along with any lexing errors that occurred. @@ -623,7 +606,7 @@ impl<'a> Lexer<'a> { }; let comment = self.eat_while(None, |ch| ch != '\n'); - if self.skip_comments { + if doc_style.is_none() && self.skip_comments { return self.next_token(); } @@ -668,7 +651,7 @@ impl<'a> Lexer<'a> { } if depth == 0 { - if self.skip_comments { + if doc_style.is_none() && self.skip_comments { return self.next_token(); } Ok(Token::BlockComment(content, doc_style).into_span(start, self.position)) 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 7b805b5fd8db..f5f7f0458d78 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -5,8 +5,8 @@ use std::{fmt, iter::Map, vec::IntoIter}; use crate::{ lexer::errors::LexerErrorKind, node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, - QuotedTypeId, + ExprId, InternedExpressionKind, InternedPattern, InternedStatementKind, + InternedUnresolvedTypeData, QuotedTypeId, }, }; @@ -36,6 +36,7 @@ pub enum BorrowedToken<'input> { InternedStatement(InternedStatementKind), InternedLValue(InternedExpressionKind), InternedUnresolvedTypeData(InternedUnresolvedTypeData), + InternedPattern(InternedPattern), /// < Less, /// <= @@ -151,6 +152,8 @@ pub enum Token { InternedLValue(InternedExpressionKind), /// A reference to an interned `UnresolvedTypeData`. InternedUnresolvedTypeData(InternedUnresolvedTypeData), + /// A reference to an interned `Patter`. + InternedPattern(InternedPattern), /// < Less, /// <= @@ -255,6 +258,7 @@ pub fn token_to_borrowed_token(token: &Token) -> BorrowedToken<'_> { Token::InternedStatement(id) => BorrowedToken::InternedStatement(*id), Token::InternedLValue(id) => BorrowedToken::InternedLValue(*id), Token::InternedUnresolvedTypeData(id) => BorrowedToken::InternedUnresolvedTypeData(*id), + Token::InternedPattern(id) => BorrowedToken::InternedPattern(*id), Token::IntType(ref i) => BorrowedToken::IntType(i.clone()), Token::Less => BorrowedToken::Less, Token::LessEqual => BorrowedToken::LessEqual, @@ -378,7 +382,10 @@ impl fmt::Display for Token { } // 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(_) => { + Token::InternedExpr(_) + | Token::InternedStatement(_) + | Token::InternedLValue(_) + | Token::InternedPattern(_) => { write!(f, "(expr)") } Token::InternedUnresolvedTypeData(_) => write!(f, "(type)"), @@ -439,7 +446,10 @@ pub enum TokenKind { InternedStatement, InternedLValue, InternedUnresolvedTypeData, + InternedPattern, UnquoteMarker, + OuterDocComment, + InnerDocComment, } impl fmt::Display for TokenKind { @@ -457,7 +467,10 @@ impl fmt::Display for TokenKind { TokenKind::InternedStatement => write!(f, "interned statement"), TokenKind::InternedLValue => write!(f, "interned lvalue"), TokenKind::InternedUnresolvedTypeData => write!(f, "interned unresolved type"), + TokenKind::InternedPattern => write!(f, "interned pattern"), TokenKind::UnquoteMarker => write!(f, "macro result"), + TokenKind::OuterDocComment => write!(f, "outer doc comment"), + TokenKind::InnerDocComment => write!(f, "inner doc comment"), } } } @@ -481,6 +494,11 @@ impl Token { Token::InternedStatement(_) => TokenKind::InternedStatement, Token::InternedLValue(_) => TokenKind::InternedLValue, Token::InternedUnresolvedTypeData(_) => TokenKind::InternedUnresolvedTypeData, + Token::InternedPattern(_) => TokenKind::InternedPattern, + Token::LineComment(_, Some(DocStyle::Outer)) + | Token::BlockComment(_, Some(DocStyle::Outer)) => TokenKind::OuterDocComment, + Token::LineComment(_, Some(DocStyle::Inner)) + | Token::BlockComment(_, Some(DocStyle::Inner)) => TokenKind::InnerDocComment, tok => TokenKind::Token(tok.clone()), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs index ec09f680bc23..9f7a05647893 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs @@ -53,7 +53,7 @@ pub mod macros_api { pub use crate::token::SecondaryAttribute; pub use crate::ast::{ - BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, + BlockExpression, CallExpression, CastExpression, Documented, Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, ItemVisibility, LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, Path, PathKind, Pattern, Statement, UnresolvedType, UnresolvedTypeData, Visibility, 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 ce8ef3572e6d..66db72eef551 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -8,6 +8,7 @@ pub enum MonomorphizationError { NoDefaultType { location: Location }, InternalError { message: &'static str, location: Location }, InterpreterError(InterpreterError), + ComptimeFnInRuntimeCode { name: String, location: Location }, } impl MonomorphizationError { @@ -15,6 +16,7 @@ impl MonomorphizationError { match self { MonomorphizationError::UnknownArrayLength { location, .. } | MonomorphizationError::InternalError { location, .. } + | MonomorphizationError::ComptimeFnInRuntimeCode { location, .. } | MonomorphizationError::NoDefaultType { location, .. } => *location, MonomorphizationError::InterpreterError(error) => error.get_location(), } @@ -43,6 +45,12 @@ impl MonomorphizationError { } MonomorphizationError::InterpreterError(error) => return error.into(), MonomorphizationError::InternalError { message, .. } => message.to_string(), + MonomorphizationError::ComptimeFnInRuntimeCode { name, location } => { + let message = format!("Comptime function {name} used in runtime code"); + let secondary = + "Comptime functions must be in a comptime block to be called".into(); + return CustomDiagnostic::simple_error(message, secondary, location.span); + } }; let location = self.location(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index 87b55540bbd1..9357cc65c14b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -133,7 +133,7 @@ pub fn monomorphize_debug( let impl_bindings = perform_impl_bindings(interner, trait_method, next_fn_id, location) .map_err(MonomorphizationError::InterpreterError)?; - monomorphizer.function(next_fn_id, new_id)?; + monomorphizer.function(next_fn_id, new_id, location)?; undo_instantiation_bindings(impl_bindings); undo_instantiation_bindings(bindings); } @@ -278,7 +278,10 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result { let new_main_id = self.next_function_id(); assert_eq!(new_main_id, Program::main_id()); - self.function(main_id, new_main_id)?; + + let location = self.interner.function_meta(&main_id).location; + self.function(main_id, new_main_id, location)?; + self.return_location = self.interner.function(&main_id).block(self.interner).statements().last().and_then( |x| match self.interner.statement(x) { @@ -294,6 +297,7 @@ impl<'interner> Monomorphizer<'interner> { &mut self, f: node_interner::FuncId, id: FuncId, + location: Location, ) -> Result<(), MonomorphizationError> { if let Some((self_type, trait_id)) = self.interner.get_function_trait(&f) { let the_trait = self.interner.get_trait(trait_id); @@ -313,6 +317,10 @@ impl<'interner> Monomorphizer<'interner> { let modifiers = self.interner.function_modifiers(&f); let name = self.interner.function_name(&f).to_owned(); + if modifiers.is_comptime { + return Err(MonomorphizationError::ComptimeFnInRuntimeCode { name, location }); + } + let body_expr_id = self.interner.function(&f).as_expr(); let body_return_type = self.interner.id_type(body_expr_id); let return_type = match meta.return_type() { 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 aa51779d24bd..f298559e65c8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -16,6 +16,7 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::ExpressionKind; use crate::ast::Ident; use crate::ast::LValue; +use crate::ast::Pattern; use crate::ast::StatementKind; use crate::ast::UnresolvedTypeData; use crate::graph::CrateId; @@ -222,6 +223,9 @@ pub struct NodeInterner { // Interned `UnresolvedTypeData`s during comptime code. interned_unresolved_type_datas: noirc_arena::Arena, + // Interned `Pattern`s during comptime code. + interned_patterns: noirc_arena::Arena, + /// Determins whether to run in LSP mode. In LSP mode references are tracked. pub(crate) lsp_mode: bool, @@ -269,6 +273,9 @@ pub struct NodeInterner { pub(crate) comptime_scopes: Vec>, pub(crate) usage_tracker: UsageTracker, + + /// Captures the documentation comments for each module, struct, trait, function, etc. + pub(crate) doc_comments: HashMap>, } /// A dependency in the dependency graph may be a type or a definition. @@ -607,6 +614,9 @@ pub struct InternedStatementKind(noirc_arena::Index); #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct InternedUnresolvedTypeData(noirc_arena::Index); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InternedPattern(noirc_arena::Index); + impl Default for NodeInterner { fn default() -> Self { NodeInterner { @@ -647,6 +657,7 @@ impl Default for NodeInterner { interned_expression_kinds: Default::default(), interned_statement_kinds: Default::default(), interned_unresolved_type_datas: Default::default(), + interned_patterns: Default::default(), lsp_mode: false, location_indices: LocationIndices::default(), reference_graph: petgraph::graph::DiGraph::new(), @@ -656,6 +667,7 @@ impl Default for NodeInterner { comptime_scopes: vec![HashMap::default()], trait_impl_associated_types: HashMap::default(), usage_tracker: UsageTracker::new(), + doc_comments: HashMap::default(), } } } @@ -2097,6 +2109,14 @@ impl NodeInterner { LValue::from_expression_kind(self.get_expression_kind(id).clone(), span) } + pub fn push_pattern(&mut self, pattern: Pattern) -> InternedPattern { + InternedPattern(self.interned_patterns.insert(pattern)) + } + + pub fn get_pattern(&self, id: InternedPattern) -> &Pattern { + &self.interned_patterns[id.0] + } + pub fn push_unresolved_type_data( &mut self, typ: UnresolvedTypeData, @@ -2196,6 +2216,16 @@ impl NodeInterner { bindings } + + pub fn set_doc_comments(&mut self, id: ReferenceId, doc_comments: Vec) { + if !doc_comments.is_empty() { + self.doc_comments.insert(id, doc_comments); + } + } + + pub fn doc_comments(&self, id: ReferenceId) -> Option<&Vec> { + self.doc_comments.get(&id) + } } impl Methods { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop deleted file mode 100644 index 01b8be8f7212..000000000000 --- a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop +++ /dev/null @@ -1,170 +0,0 @@ -use noirc_errors::Span; - -use crate::lexer::token::BorrowedToken; -use crate::lexer::token as noir_token; -use crate::lexer::errors::LexerErrorKind; -use crate::parser::TopLevelStatement; -use crate::ast::{Ident, Path, PathKind, PathSegment, UseTree, UseTreeKind}; - -use lalrpop_util::ErrorRecovery; - -grammar<'input, 'err>(input: &'input str, errors: &'err mut [ErrorRecovery, &'static str>]); - -extern { - type Location = usize; - - type Error = LexerErrorKind; - - // NOTE: each token needs a terminal defined - enum BorrowedToken<'input> { - string => BorrowedToken::Str(<&'input str>), - ident => BorrowedToken::Ident(<&'input str>), - - // symbols - "<" => BorrowedToken::Less, - "<=" => BorrowedToken::LessEqual, - ">" => BorrowedToken::Greater, - ">=" => BorrowedToken::GreaterEqual, - "==" => BorrowedToken::Equal, - "!=" => BorrowedToken::NotEqual, - "+" => BorrowedToken::Plus, - "-" => BorrowedToken::Minus, - "*" => BorrowedToken::Star, - "/" => BorrowedToken::Slash, - "%" => BorrowedToken::Percent, - "&" => BorrowedToken::Ampersand, - "^" => BorrowedToken::Caret, - "<<" => BorrowedToken::ShiftLeft, - ">>" => BorrowedToken::ShiftRight, - "." => BorrowedToken::Dot, - ".." => BorrowedToken::DoubleDot, - "(" => BorrowedToken::LeftParen, - ")" => BorrowedToken::RightParen, - "{" => BorrowedToken::LeftBrace, - "}" => BorrowedToken::RightBrace, - "[" => BorrowedToken::LeftBracket, - "]" => BorrowedToken::RightBracket, - "->" => BorrowedToken::Arrow, - "|" => BorrowedToken::Pipe, - "#" => BorrowedToken::Pound, - "," => BorrowedToken::Comma, - ":" => BorrowedToken::Colon, - "::" => BorrowedToken::DoubleColon, - ";" => BorrowedToken::Semicolon, - "!" => BorrowedToken::Bang, - "=" => BorrowedToken::Assign, - // keywords - "as" => BorrowedToken::Keyword(noir_token::Keyword::As), - "assert" => BorrowedToken::Keyword(noir_token::Keyword::Assert), - "assert_eq" => BorrowedToken::Keyword(noir_token::Keyword::AssertEq), - "bool" => BorrowedToken::Keyword(noir_token::Keyword::Bool), - "break" => BorrowedToken::Keyword(noir_token::Keyword::Break), - "call_data" => BorrowedToken::Keyword(noir_token::Keyword::CallData), - "char" => BorrowedToken::Keyword(noir_token::Keyword::Char), - "comptime" => BorrowedToken::Keyword(noir_token::Keyword::Comptime), - "constrain" => BorrowedToken::Keyword(noir_token::Keyword::Constrain), - "continue" => BorrowedToken::Keyword(noir_token::Keyword::Continue), - "contract" => BorrowedToken::Keyword(noir_token::Keyword::Contract), - "crate" => BorrowedToken::Keyword(noir_token::Keyword::Crate), - "dep" => BorrowedToken::Keyword(noir_token::Keyword::Dep), - "else" => BorrowedToken::Keyword(noir_token::Keyword::Else), - "Field" => BorrowedToken::Keyword(noir_token::Keyword::Field), - "fn" => BorrowedToken::Keyword(noir_token::Keyword::Fn), - "for" => BorrowedToken::Keyword(noir_token::Keyword::For), - "fmtstr" => BorrowedToken::Keyword(noir_token::Keyword::FormatString), - "global" => BorrowedToken::Keyword(noir_token::Keyword::Global), - "if" => BorrowedToken::Keyword(noir_token::Keyword::If), - "impl" => BorrowedToken::Keyword(noir_token::Keyword::Impl), - "in" => BorrowedToken::Keyword(noir_token::Keyword::In), - "let" => BorrowedToken::Keyword(noir_token::Keyword::Let), - "mod" => BorrowedToken::Keyword(noir_token::Keyword::Mod), - "mut" => BorrowedToken::Keyword(noir_token::Keyword::Mut), - "pub" => BorrowedToken::Keyword(noir_token::Keyword::Pub), - "return" => BorrowedToken::Keyword(noir_token::Keyword::Return), - "return_data" => BorrowedToken::Keyword(noir_token::Keyword::ReturnData), - "str" => BorrowedToken::Keyword(noir_token::Keyword::String), - "struct" => BorrowedToken::Keyword(noir_token::Keyword::Struct), - "trait" => BorrowedToken::Keyword(noir_token::Keyword::Trait), - "type" => BorrowedToken::Keyword(noir_token::Keyword::Type), - "unchecked" => BorrowedToken::Keyword(noir_token::Keyword::Unchecked), - "unconstrained" => BorrowedToken::Keyword(noir_token::Keyword::Unconstrained), - "use" => BorrowedToken::Keyword(noir_token::Keyword::Use), - "where" => BorrowedToken::Keyword(noir_token::Keyword::Where), - "while" => BorrowedToken::Keyword(noir_token::Keyword::While), - // bool - "true" => BorrowedToken::Bool(true), - "false" => BorrowedToken::Bool(false), - - r"[\t\r\n ]+" => BorrowedToken::Whitespace(_), - - EOF => BorrowedToken::EOF, - } -} - -pub(crate) TopLevelStatement: TopLevelStatement = { - "use" r"[\t\r\n ]+" ";" EOF => { - TopLevelStatement::Import(use_tree, crate::ast::ItemVisibility::Private) - } -} - -UseTree: UseTree = { - // path::to::ident as SomeAlias - => { - let ident = prefix.pop().ident; - let kind = UseTreeKind::Path(ident, alias); - UseTree { prefix, kind } - }, -} - -pub(crate) Path: Path = { - "crate" "::" => { - let kind = PathKind::Crate; - let span = Span::from(lo as u32..hi as u32); - Path { segments, kind, span } - }, - - "dep" "::" => { - let kind = PathKind::Plain; - let span = Span::from(lo as u32..hi as u32); - Path { segments, kind, span } - }, - - => { - segments.insert(0, id); - let kind = PathKind::Plain; - let span = Span::from(lo as u32..hi as u32); - Path { segments, kind, span } - }, -} - -PathSegments: Vec = { - )*> => { - segments - } -} - -PathSegment: PathSegment = { - => { - let token = noir_token::Token::Ident(i.to_string()); - let span = Span::from(lo as u32..hi as u32); - PathSegment::from(Ident::from_token(token, span)) - }, -} - -Alias: Ident = { - r"[\t\r\n ]+" "as" r"[\t\r\n ]+" => <>, -} - -Ident: Ident = { - => { - let token = noir_token::Token::Ident(i.to_string()); - let span = Span::from(lo as u32..hi as u32); - Ident::from_token(token, span) - }, -} - -Bool: BorrowedToken<'input> = { - "true" => BorrowedToken::Bool(true), - "false" => BorrowedToken::Bool(false), -}; - 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 66be0fdced54..968af82a8b33 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -12,9 +12,9 @@ mod labels; mod parser; use crate::ast::{ - Expression, Ident, ImportStatement, ItemVisibility, LetStatement, ModuleDeclaration, - NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Recoverable, StatementKind, - TypeImpl, UseTree, + Documented, Expression, Ident, ImportStatement, ItemVisibility, LetStatement, + ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, + Recoverable, StatementKind, TypeImpl, UseTree, }; use crate::token::{Keyword, SecondaryAttribute, Token}; @@ -31,7 +31,13 @@ pub use parser::{ }; #[derive(Debug, Clone)] -pub enum TopLevelStatement { +pub struct TopLevelStatement { + pub kind: TopLevelStatementKind, + pub doc_comments: Vec, +} + +#[derive(Debug, Clone)] +pub enum TopLevelStatementKind { Function(NoirFunction), Module(ModuleDeclaration), Import(UseTree, ItemVisibility), @@ -46,21 +52,21 @@ pub enum TopLevelStatement { Error, } -impl TopLevelStatement { +impl TopLevelStatementKind { pub fn into_item_kind(self) -> Option { match self { - TopLevelStatement::Function(f) => Some(ItemKind::Function(f)), - TopLevelStatement::Module(m) => Some(ItemKind::ModuleDecl(m)), - TopLevelStatement::Import(i, visibility) => Some(ItemKind::Import(i, visibility)), - TopLevelStatement::Struct(s) => Some(ItemKind::Struct(s)), - TopLevelStatement::Trait(t) => Some(ItemKind::Trait(t)), - TopLevelStatement::TraitImpl(t) => Some(ItemKind::TraitImpl(t)), - TopLevelStatement::Impl(i) => Some(ItemKind::Impl(i)), - TopLevelStatement::TypeAlias(t) => Some(ItemKind::TypeAlias(t)), - TopLevelStatement::SubModule(s) => Some(ItemKind::Submodules(s)), - TopLevelStatement::Global(c) => Some(ItemKind::Global(c)), - TopLevelStatement::InnerAttribute(a) => Some(ItemKind::InnerAttribute(a)), - TopLevelStatement::Error => None, + TopLevelStatementKind::Function(f) => Some(ItemKind::Function(f)), + TopLevelStatementKind::Module(m) => Some(ItemKind::ModuleDecl(m)), + TopLevelStatementKind::Import(i, visibility) => Some(ItemKind::Import(i, visibility)), + TopLevelStatementKind::Struct(s) => Some(ItemKind::Struct(s)), + TopLevelStatementKind::Trait(t) => Some(ItemKind::Trait(t)), + TopLevelStatementKind::TraitImpl(t) => Some(ItemKind::TraitImpl(t)), + TopLevelStatementKind::Impl(i) => Some(ItemKind::Impl(i)), + TopLevelStatementKind::TypeAlias(t) => Some(ItemKind::TypeAlias(t)), + TopLevelStatementKind::SubModule(s) => Some(ItemKind::Submodules(s)), + TopLevelStatementKind::Global(c) => Some(ItemKind::Global(c)), + TopLevelStatementKind::InnerAttribute(a) => Some(ItemKind::InnerAttribute(a)), + TopLevelStatementKind::Error => None, } } } @@ -222,11 +228,11 @@ fn parameter_name_recovery() -> impl NoirParser { try_skip_until([Colon, RightParen, Comma], [RightParen, Comma]) } -fn top_level_statement_recovery() -> impl NoirParser { +fn top_level_statement_recovery() -> impl NoirParser { none_of([Token::RightBrace, Token::EOF]) .repeated() .ignore_then(one_of([Token::Semicolon])) - .map(|_| TopLevelStatement::Error) + .map(|_| TopLevelStatementKind::Error) } /// Force the given parser to succeed, logging any errors it had @@ -237,21 +243,22 @@ fn force<'a, T: 'a>(parser: impl NoirParser + 'a) -> impl NoirParser, - pub functions: Vec, - pub types: Vec, - pub traits: Vec, + pub functions: Vec>, + pub types: Vec>, + pub traits: Vec>, pub trait_impls: Vec, pub impls: Vec, - pub type_aliases: Vec, - pub globals: Vec, + pub type_aliases: Vec>, + pub globals: Vec>, /// Module declarations like `mod foo;` - pub module_decls: Vec, + pub module_decls: Vec>, /// Full submodules as in `mod foo { ... definitions ... }` - pub submodules: Vec, + pub submodules: Vec>, pub inner_attributes: Vec, + pub inner_doc_comments: Vec, } impl std::fmt::Display for SortedModule { @@ -296,6 +303,7 @@ impl std::fmt::Display for SortedModule { #[derive(Clone, Debug, Default)] pub struct ParsedModule { pub items: Vec, + pub inner_doc_comments: Vec, } impl ParsedModule { @@ -305,19 +313,27 @@ impl ParsedModule { for item in self.items { match item.kind { ItemKind::Import(import, visibility) => module.push_import(import, visibility), - ItemKind::Function(func) => module.push_function(func), - ItemKind::Struct(typ) => module.push_type(typ), - ItemKind::Trait(noir_trait) => module.push_trait(noir_trait), + ItemKind::Function(func) => module.push_function(func, item.doc_comments), + ItemKind::Struct(typ) => module.push_type(typ, item.doc_comments), + ItemKind::Trait(noir_trait) => module.push_trait(noir_trait, item.doc_comments), ItemKind::TraitImpl(trait_impl) => module.push_trait_impl(trait_impl), ItemKind::Impl(r#impl) => module.push_impl(r#impl), - ItemKind::TypeAlias(type_alias) => module.push_type_alias(type_alias), - ItemKind::Global(global) => module.push_global(global), - ItemKind::ModuleDecl(mod_name) => module.push_module_decl(mod_name), - ItemKind::Submodules(submodule) => module.push_submodule(submodule.into_sorted()), + ItemKind::TypeAlias(type_alias) => { + module.push_type_alias(type_alias, item.doc_comments); + } + ItemKind::Global(global) => module.push_global(global, item.doc_comments), + ItemKind::ModuleDecl(mod_name) => { + module.push_module_decl(mod_name, item.doc_comments); + } + ItemKind::Submodules(submodule) => { + module.push_submodule(submodule.into_sorted(), item.doc_comments); + } ItemKind::InnerAttribute(attribute) => module.inner_attributes.push(attribute), } } + module.inner_doc_comments = self.inner_doc_comments; + module } } @@ -326,6 +342,7 @@ impl ParsedModule { pub struct Item { pub kind: ItemKind, pub span: Span, + pub doc_comments: Vec, } #[derive(Clone, Debug)] @@ -385,16 +402,16 @@ pub struct SortedSubModule { } impl SortedModule { - fn push_function(&mut self, func: NoirFunction) { - self.functions.push(func); + fn push_function(&mut self, func: NoirFunction, doc_comments: Vec) { + self.functions.push(Documented::new(func, doc_comments)); } - fn push_type(&mut self, typ: NoirStruct) { - self.types.push(typ); + fn push_type(&mut self, typ: NoirStruct, doc_comments: Vec) { + self.types.push(Documented::new(typ, doc_comments)); } - fn push_trait(&mut self, noir_trait: NoirTrait) { - self.traits.push(noir_trait); + fn push_trait(&mut self, noir_trait: NoirTrait, doc_comments: Vec) { + self.traits.push(Documented::new(noir_trait, doc_comments)); } fn push_trait_impl(&mut self, trait_impl: NoirTraitImpl) { @@ -405,24 +422,24 @@ impl SortedModule { self.impls.push(r#impl); } - fn push_type_alias(&mut self, type_alias: NoirTypeAlias) { - self.type_aliases.push(type_alias); + fn push_type_alias(&mut self, type_alias: NoirTypeAlias, doc_comments: Vec) { + self.type_aliases.push(Documented::new(type_alias, doc_comments)); } fn push_import(&mut self, import_stmt: UseTree, visibility: ItemVisibility) { self.imports.extend(import_stmt.desugar(None, visibility)); } - fn push_module_decl(&mut self, mod_decl: ModuleDeclaration) { - self.module_decls.push(mod_decl); + fn push_module_decl(&mut self, mod_decl: ModuleDeclaration, doc_comments: Vec) { + self.module_decls.push(Documented::new(mod_decl, doc_comments)); } - fn push_submodule(&mut self, submodule: SortedSubModule) { - self.submodules.push(submodule); + fn push_submodule(&mut self, submodule: SortedSubModule, doc_comments: Vec) { + self.submodules.push(Documented::new(submodule, doc_comments)); } - fn push_global(&mut self, global: LetStatement) { - self.globals.push(global); + fn push_global(&mut self, global: LetStatement, doc_comments: Vec) { + self.globals.push(Documented::new(global, doc_comments)); } } @@ -503,27 +520,27 @@ impl Precedence { } } -impl std::fmt::Display for TopLevelStatement { +impl std::fmt::Display for TopLevelStatementKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TopLevelStatement::Function(fun) => fun.fmt(f), - TopLevelStatement::Module(m) => m.fmt(f), - TopLevelStatement::Import(tree, visibility) => { + TopLevelStatementKind::Function(fun) => fun.fmt(f), + TopLevelStatementKind::Module(m) => m.fmt(f), + TopLevelStatementKind::Import(tree, visibility) => { if visibility == &ItemVisibility::Private { write!(f, "use {tree}") } else { write!(f, "{visibility} use {tree}") } } - TopLevelStatement::Trait(t) => t.fmt(f), - TopLevelStatement::TraitImpl(i) => i.fmt(f), - TopLevelStatement::Struct(s) => s.fmt(f), - TopLevelStatement::Impl(i) => i.fmt(f), - TopLevelStatement::TypeAlias(t) => t.fmt(f), - TopLevelStatement::SubModule(s) => s.fmt(f), - TopLevelStatement::Global(c) => c.fmt(f), - TopLevelStatement::InnerAttribute(a) => write!(f, "#![{}]", a), - TopLevelStatement::Error => write!(f, "error"), + TopLevelStatementKind::Trait(t) => t.fmt(f), + TopLevelStatementKind::TraitImpl(i) => i.fmt(f), + TopLevelStatementKind::Struct(s) => s.fmt(f), + TopLevelStatementKind::Impl(i) => i.fmt(f), + TopLevelStatementKind::TypeAlias(t) => t.fmt(f), + TopLevelStatementKind::SubModule(s) => s.fmt(f), + TopLevelStatementKind::Global(c) => c.fmt(f), + TopLevelStatementKind::InnerAttribute(a) => write!(f, "#![{}]", a), + TopLevelStatementKind::Error => write!(f, "error"), } } } 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 48d25e7a1d80..0ffeb691d35d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -27,6 +27,7 @@ use self::path::as_trait_path; use self::primitives::{keyword, macro_quote_marker, mutable_reference, variable}; use self::types::{generic_type_args, maybe_comp_time}; use attributes::{attributes, inner_attribute, validate_secondary_attributes}; +use doc_comments::{inner_doc_comments, outer_doc_comments}; pub use types::parse_type; use visibility::item_visibility; pub use visibility::visibility; @@ -35,30 +36,30 @@ use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, parenthesized, then_commit, then_commit_ignore, top_level_statement_recovery, ExprParser, NoirParser, ParsedModule, ParsedSubModule, ParserError, ParserErrorReason, Precedence, - TopLevelStatement, + TopLevelStatementKind, }; -use super::{spanned, Item, ItemKind}; +use super::{spanned, Item, TopLevelStatement}; use crate::ast::{ - BinaryOp, BinaryOpKind, BlockExpression, ForLoopStatement, ForRange, GenericTypeArgs, Ident, - IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, NoirTypeAlias, Param, Path, - Pattern, Recoverable, Statement, TypeImpl, UnaryRhsMemberAccess, UnaryRhsMethodCall, UseTree, - UseTreeKind, Visibility, + BinaryOp, BinaryOpKind, BlockExpression, Documented, ForLoopStatement, ForRange, + GenericTypeArgs, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, + NoirTypeAlias, Param, Path, Pattern, Recoverable, Statement, TypeImpl, UnaryRhsMemberAccess, + UnaryRhsMethodCall, UseTree, UseTreeKind, Visibility, }; use crate::ast::{ Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, }; -use crate::lexer::{lexer::from_spanned_token_result, Lexer}; +use crate::lexer::Lexer; use crate::parser::{force, ignore_then_commit, statement_recovery}; use crate::token::{Keyword, Token, TokenKind}; use acvm::AcirField; use chumsky::prelude::*; use iter_extended::vecmap; -use lalrpop_util::lalrpop_mod; use noirc_errors::{Span, Spanned}; mod assertion; mod attributes; +mod doc_comments; mod function; mod lambdas; mod literals; @@ -69,9 +70,6 @@ pub(super) mod traits; mod types; mod visibility; -// synthesized by LALRPOP -lalrpop_mod!(pub noir_parser); - #[cfg(test)] mod test_helpers; @@ -95,79 +93,9 @@ pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { parsing_errors.extend(lexing_errors.into_iter().map(Into::into)); let parsed_module = module.unwrap_or_default(); - if cfg!(feature = "experimental_parser") { - for parsed_item in &parsed_module.items { - if lalrpop_parser_supports_kind(&parsed_item.kind) { - match &parsed_item.kind { - ItemKind::Import(parsed_use_tree, _visibility) => { - prototype_parse_use_tree(Some(parsed_use_tree), source_program); - } - // other kinds prevented by lalrpop_parser_supports_kind - _ => unreachable!(), - } - } - } - } (parsed_module, parsing_errors) } -fn prototype_parse_use_tree(expected_use_tree_opt: Option<&UseTree>, input: &str) { - // TODO(https://github.com/noir-lang/noir/issues/4777): currently skipping - // recursive use trees, e.g. "use std::{foo, bar}" - if input.contains('{') { - return; - } - - let mut lexer = Lexer::new(input); - lexer = lexer.skip_whitespaces(false); - let mut errors = Vec::new(); - - // NOTE: this is a hack to get the references working - // => this likely means that we'll want to propagate the <'input> lifetime further into Token - let lexer_result = lexer.collect::>(); - let referenced_lexer_result = lexer_result.iter().map(from_spanned_token_result); - - let calculated = noir_parser::TopLevelStatementParser::new().parse( - input, - &mut errors, - referenced_lexer_result, - ); - - if let Some(expected_use_tree) = expected_use_tree_opt { - assert!( - calculated.is_ok(), - "calculated not Ok(_): {:?}\n\nlexer: {:?}\n\ninput: {:?}", - calculated, - lexer_result, - input - ); - - match calculated.unwrap() { - TopLevelStatement::Import(parsed_use_tree, _visibility) => { - assert_eq!(expected_use_tree, &parsed_use_tree); - } - unexpected_calculated => { - panic!( - "expected a TopLevelStatement::Import, but found: {:?}", - unexpected_calculated - ) - } - } - } else { - assert!( - calculated.is_err(), - "calculated not Err(_): {:?}\n\nlexer: {:?}\n\ninput: {:?}", - calculated, - lexer_result, - input - ); - } -} - -fn lalrpop_parser_supports_kind(kind: &ItemKind) -> bool { - matches!(kind, ItemKind::Import(..)) -} - /// program: module EOF fn program() -> impl NoirParser { module().then_ignore(just(Token::EOF)) @@ -177,13 +105,24 @@ fn program() -> impl NoirParser { /// | %empty pub fn module() -> impl NoirParser { recursive(|module_parser| { - empty() - .to(ParsedModule::default()) - .then(spanned(top_level_statement(module_parser)).repeated()) - .foldl(|mut program, (statement, span)| { - if let Some(kind) = statement.into_item_kind() { - program.items.push(Item { kind, span }); - } + inner_doc_comments() + .then( + empty() + .to(ParsedModule::default()) + .then(spanned(top_level_statement(module_parser)).repeated()) + .foldl(|mut program, (statement, span)| { + if let Some(kind) = statement.kind.into_item_kind() { + program.items.push(Item { + kind, + span, + doc_comments: statement.doc_comments, + }); + } + program + }), + ) + .map(|(doc_comments, mut program)| { + program.inner_doc_comments = doc_comments; program }) }) @@ -194,6 +133,14 @@ pub fn top_level_items() -> impl NoirParser> { top_level_statement(module()).repeated() } +pub fn top_level_statement<'a>( + module_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + outer_doc_comments() + .then(top_level_statement_kind(module_parser)) + .map(|(doc_comments, kind)| TopLevelStatement { kind, doc_comments }) +} + /// top_level_statement: function_definition /// | struct_definition /// | trait_definition @@ -202,11 +149,11 @@ pub fn top_level_items() -> impl NoirParser> { /// | module_declaration /// | use_statement /// | global_declaration -pub fn top_level_statement<'a>( +fn top_level_statement_kind<'a>( module_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { +) -> impl NoirParser + 'a { choice(( - function::function_definition(false).map(TopLevelStatement::Function), + function::function_definition(false).map(TopLevelStatementKind::Function), structs::struct_definition(), traits::trait_definition(), traits::trait_implementation(), @@ -217,7 +164,7 @@ pub fn top_level_statement<'a>( module_declaration().then_ignore(force(just(Token::Semicolon))), use_statement().then_ignore(force(just(Token::Semicolon))), global_declaration().then_ignore(force(just(Token::Semicolon))), - inner_attribute().map(TopLevelStatement::InnerAttribute), + inner_attribute().map(TopLevelStatementKind::InnerAttribute), )) .recover_via(top_level_statement_recovery()) } @@ -225,9 +172,15 @@ pub fn top_level_statement<'a>( /// Parses a non-trait implementation, adding a set of methods to a type. /// /// implementation: 'impl' generics type '{' function_definition ... '}' -fn implementation() -> impl NoirParser { +fn implementation() -> impl NoirParser { + let method = spanned(function::function_definition(true)); + let methods = outer_doc_comments() + .then(method) + .map(|(doc_comments, (method, span))| (Documented::new(method, doc_comments), span)) + .repeated(); + let methods_or_error = just(Token::LeftBrace) - .ignore_then(spanned(function::function_definition(true)).repeated()) + .ignore_then(methods) .then_ignore(just(Token::RightBrace)) .or_not() .validate(|methods, span, emit| { @@ -250,7 +203,7 @@ fn implementation() -> impl NoirParser { .map(|args| { let ((other_args, where_clause), methods) = args; let (generics, (object_type, type_span)) = other_args; - TopLevelStatement::Impl(TypeImpl { + TopLevelStatementKind::Impl(TypeImpl { generics, object_type, type_span, @@ -261,7 +214,7 @@ fn implementation() -> impl NoirParser { } /// global_declaration: 'global' ident global_type_annotation '=' literal -fn global_declaration() -> impl NoirParser { +fn global_declaration() -> impl NoirParser { let p = attributes::attributes() .then(maybe_comp_time()) .then(spanned(keyword(Keyword::Mut)).or_not()) @@ -285,11 +238,13 @@ fn global_declaration() -> impl NoirParser { LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes } }, ) - .map(TopLevelStatement::Global) + .map(TopLevelStatementKind::Global) } /// submodule: 'mod' ident '{' module '}' -fn submodule(module_parser: impl NoirParser) -> impl NoirParser { +fn submodule( + module_parser: impl NoirParser, +) -> impl NoirParser { attributes() .then_ignore(keyword(Keyword::Mod)) .then(ident()) @@ -298,7 +253,7 @@ fn submodule(module_parser: impl NoirParser) -> impl NoirParser) -> impl NoirParser) -> impl NoirParser { +fn contract( + module_parser: impl NoirParser, +) -> impl NoirParser { attributes() .then_ignore(keyword(Keyword::Contract)) .then(ident()) @@ -317,7 +274,7 @@ fn contract(module_parser: impl NoirParser) -> impl NoirParser) -> impl NoirParser impl NoirParser { +fn type_alias_definition() -> impl NoirParser { use self::Keyword::Type; let p = ignore_then_commit(keyword(Type), ident()); @@ -335,7 +292,7 @@ fn type_alias_definition() -> impl NoirParser { let p = then_commit(p, parse_type()); p.map_with_span(|((name, generics), typ), span| { - TopLevelStatement::TypeAlias(NoirTypeAlias { name, generics, typ, span }) + TopLevelStatementKind::TypeAlias(NoirTypeAlias { name, generics, typ, span }) }) } @@ -450,20 +407,20 @@ fn optional_type_annotation<'a>() -> impl NoirParser + 'a { }) } -fn module_declaration() -> impl NoirParser { +fn module_declaration() -> impl NoirParser { attributes().then_ignore(keyword(Keyword::Mod)).then(ident()).validate( |(attributes, ident), span, emit| { let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatement::Module(ModuleDeclaration { ident, outer_attributes: attributes }) + TopLevelStatementKind::Module(ModuleDeclaration { ident, outer_attributes: attributes }) }, ) } -fn use_statement() -> impl NoirParser { +fn use_statement() -> impl NoirParser { item_visibility() .then_ignore(keyword(Keyword::Use)) .then(use_tree()) - .map(|(visibility, use_tree)| TopLevelStatement::Import(use_tree, visibility)) + .map(|(visibility, use_tree)| TopLevelStatementKind::Import(use_tree, visibility)) } fn rename() -> impl NoirParser> { @@ -637,7 +594,15 @@ pub fn pattern() -> impl NoirParser { .delimited_by(just(Token::LeftParen), just(Token::RightParen)) .map_with_span(Pattern::Tuple); - choice((mut_pattern, tuple_pattern, struct_pattern, ident_pattern)) + let interned = + token_kind(TokenKind::InternedPattern).map_with_span(|token, span| match token { + Token::InternedPattern(id) => Pattern::Interned(id, span), + _ => unreachable!( + "token_kind(InternedPattern) guarantees we parse an interned pattern" + ), + }); + + choice((mut_pattern, tuple_pattern, struct_pattern, ident_pattern, interned)) }) .labelled(ParsingRuleLabel::Pattern) } @@ -1580,12 +1545,12 @@ mod test { for (use_statement_str, expect_valid) in use_statements { let mut use_statement_str = use_statement_str.to_string(); - let expected_use_statement = if expect_valid { + if expect_valid { let (result_opt, _diagnostics) = parse_recover(&use_statement(), &use_statement_str); use_statement_str.push(';'); match result_opt.unwrap() { - TopLevelStatement::Import(expected_use_statement, _visibility) => { + TopLevelStatementKind::Import(expected_use_statement, _visibility) => { Some(expected_use_statement) } _ => unreachable!(), @@ -1595,8 +1560,6 @@ mod test { assert!(result.is_err()); None }; - - prototype_parse_use_tree(expected_use_statement.as_ref(), &use_statement_str); } } @@ -1842,7 +1805,7 @@ mod test { assert_eq!(errors[0].message, "expected <, where or { after impl type"); let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatement::Impl(impl_) = top_level_statement else { + let TopLevelStatementKind::Impl(impl_) = top_level_statement else { panic!("Expected to parse an impl"); }; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs new file mode 100644 index 000000000000..151ff21017f3 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -0,0 +1,36 @@ +use chumsky::Parser; + +use crate::{ + parser::NoirParser, + token::{DocStyle, Token, TokenKind}, +}; + +use super::primitives::token_kind; + +fn outer_doc_comment() -> impl NoirParser { + token_kind(TokenKind::OuterDocComment).map(|token| match token { + Token::LineComment(comment, Some(DocStyle::Outer)) => comment, + Token::BlockComment(comment, Some(DocStyle::Outer)) => comment, + _ => unreachable!( + "Parser should have already errored due to token not being an outer doc comment" + ), + }) +} + +pub(super) fn outer_doc_comments() -> impl NoirParser> { + outer_doc_comment().repeated() +} + +fn inner_doc_comment() -> impl NoirParser { + token_kind(TokenKind::InnerDocComment).map(|token| match token { + Token::LineComment(comment, Some(DocStyle::Inner)) => comment, + Token::BlockComment(comment, Some(DocStyle::Inner)) => comment, + _ => unreachable!( + "Parser should have already errored due to token not being an inner doc comment" + ), + }) +} + +pub(super) fn inner_doc_comments() -> impl NoirParser> { + inner_doc_comment().repeated() +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs index 58bf1693eee4..66d30a6f407d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -1,6 +1,6 @@ use chumsky::prelude::*; -use crate::ast::{Ident, NoirStruct, UnresolvedType}; +use crate::ast::{Documented, NoirStruct, StructField}; use crate::{ parser::{ parser::{ @@ -8,12 +8,14 @@ use crate::{ function, parse_type, primitives::{ident, keyword}, }, - NoirParser, TopLevelStatement, + NoirParser, TopLevelStatementKind, }, token::{Keyword, Token}, }; -pub(super) fn struct_definition() -> impl NoirParser { +use super::doc_comments::outer_doc_comments; + +pub(super) fn struct_definition() -> impl NoirParser { use self::Keyword::Struct; use Token::*; @@ -34,16 +36,16 @@ pub(super) fn struct_definition() -> impl NoirParser { .then(fields) .validate(|(((attributes, name), generics), fields), span, emit| { let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) + TopLevelStatementKind::Struct(NoirStruct { name, attributes, generics, fields, span }) }) } -fn struct_fields() -> impl NoirParser> { - ident() - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .separated_by(just(Token::Comma)) - .allow_trailing() +fn struct_fields() -> impl NoirParser>> { + let field = ident().then_ignore(just(Token::Colon)).then(parse_type()); + let field = outer_doc_comments().then(field).map(|(doc_comments, (name, typ))| { + Documented::new(StructField { name, typ }, doc_comments) + }); + field.separated_by(just(Token::Comma)).allow_trailing() } #[cfg(test)] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs index bf5a4b4d0b42..cb17bf2caf71 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,6 +1,7 @@ use chumsky::prelude::*; use super::attributes::{attributes, validate_secondary_attributes}; +use super::doc_comments::outer_doc_comments; use super::function::function_return_type; use super::path::path_no_turbofish; use super::{ @@ -8,21 +9,21 @@ use super::{ }; use crate::ast::{ - Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, - UnresolvedTraitConstraint, UnresolvedType, + Documented, Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, + TraitItem, UnresolvedTraitConstraint, UnresolvedType, }; use crate::macros_api::Pattern; use crate::{ parser::{ ignore_then_commit, parenthesized, parser::primitives::keyword, NoirParser, ParserError, - ParserErrorReason, TopLevelStatement, + ParserErrorReason, TopLevelStatementKind, }, token::{Keyword, Token}, }; use super::{generic_type_args, parse_type, primitives::ident}; -pub(super) fn trait_definition() -> impl NoirParser { +pub(super) fn trait_definition() -> impl NoirParser { let trait_body_or_error = just(Token::LeftBrace) .ignore_then(trait_body()) .then_ignore(just(Token::RightBrace)) @@ -47,7 +48,7 @@ pub(super) fn trait_definition() -> impl NoirParser { .then(trait_body_or_error) .validate(|((((attributes, name), generics), where_clause), items), span, emit| { let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatement::Trait(NoirTrait { + TopLevelStatementKind::Trait(NoirTrait { name, generics, where_clause, @@ -58,10 +59,12 @@ pub(super) fn trait_definition() -> impl NoirParser { }) } -fn trait_body() -> impl NoirParser> { - trait_function_declaration() - .or(trait_type_declaration()) - .or(trait_constant_declaration()) +fn trait_body() -> impl NoirParser>> { + let item = + trait_function_declaration().or(trait_type_declaration()).or(trait_constant_declaration()); + outer_doc_comments() + .then(item) + .map(|(doc_comments, item)| Documented::new(item, doc_comments)) .repeated() } @@ -122,7 +125,7 @@ fn trait_type_declaration() -> impl NoirParser { /// and an optional `where` clause is also useable. /// /// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' -pub(super) fn trait_implementation() -> impl NoirParser { +pub(super) fn trait_implementation() -> impl NoirParser { let body_or_error = just(Token::LeftBrace) .ignore_then(trait_implementation_body()) @@ -152,7 +155,7 @@ pub(super) fn trait_implementation() -> impl NoirParser { .map(|args| { let (((other_args, object_type), where_clause), items) = args; let ((impl_generics, trait_name), trait_generics) = other_args; - TopLevelStatement::TraitImpl(NoirTraitImpl { + TopLevelStatementKind::TraitImpl(NoirTraitImpl { impl_generics, trait_name, trait_generics, @@ -163,7 +166,7 @@ pub(super) fn trait_implementation() -> impl NoirParser { }) } -fn trait_implementation_body() -> impl NoirParser> { +fn trait_implementation_body() -> impl NoirParser>> { let function = function::function_definition(true).validate(|mut f, span, emit| { if f.def().is_unconstrained || f.def().visibility != ItemVisibility::Private { emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); @@ -190,7 +193,11 @@ fn trait_implementation_body() -> impl NoirParser> { }, ); - choice((function, alias, let_statement)).repeated() + let item = choice((function, alias, let_statement)); + outer_doc_comments() + .then(item) + .map(|(doc_comments, item)| Documented::new(item, doc_comments)) + .repeated() } pub(super) fn where_clause() -> impl NoirParser> { @@ -291,7 +298,7 @@ mod test { assert_eq!(errors[0].message, "expected <, where or { after trait name"); let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatement::Trait(trait_) = top_level_statement else { + let TopLevelStatementKind::Trait(trait_) = top_level_statement else { panic!("Expected to parse a trait"); }; @@ -308,7 +315,7 @@ mod test { assert_eq!(errors[0].message, "expected <, where or { after trait impl for type"); let top_level_statement = top_level_statement.unwrap(); - let TopLevelStatement::TraitImpl(trait_impl) = top_level_statement else { + let TopLevelStatementKind::TraitImpl(trait_impl) = top_level_statement else { panic!("Expected to parse a trait impl"); }; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 3e01c3701543..64c9b7471b46 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -857,7 +857,7 @@ fn get_program_captures(src: &str) -> Vec> { let interner = context.def_interner; let mut all_captures: Vec> = Vec::new(); for func in program.into_sorted().functions { - let func_id = interner.find_function(func.name()).unwrap(); + let func_id = interner.find_function(func.item.name()).unwrap(); let hir_func = interner.function(&func_id); // Iterate over function statements and apply filtering function find_lambda_captures(hir_func.block(&interner).statements(), &interner, &mut all_captures); @@ -3373,7 +3373,7 @@ fn unquoted_integer_as_integer_token() { #[attr] pub fn foobar() {} - fn attr(_f: FunctionDefinition) -> Quoted { + comptime fn attr(_f: FunctionDefinition) -> Quoted { let serialized_len = 1; // We are testing that when we unquote $serialized_len, it's unquoted // as the token `1` and not as something else that later won't be parsed correctly diff --git a/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md b/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md index 651e7f5723bb..70590ad30d80 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md @@ -11,11 +11,6 @@ Note that due to hash collisions, the actual maximum number of elements stored b hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since every hash value will be performed modulo `MaxLen`. -When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already -known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which -will likely change the result of the program. This behavior is set to become an error in future -versions instead. - Example: ```rust 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 3a3c61b41f58..7ee33027354a 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 @@ -85,9 +85,16 @@ array and the index. #include_code as_integer noir_stdlib/src/meta/expr.nr rust -If this element is an integer literal, return the integer as a field +If this expression is an integer literal, return the integer as a field as well as whether the integer is negative (true) or not (false). +### as_let + +#include_code as_let noir_stdlib/src/meta/expr.nr rust + +If this expression is a let statement, returns the let pattern as an `Expr`, +the optional type annotation, and the assigned expression. + ### as_member_access #include_code as_member_access noir_stdlib/src/meta/expr.nr rust 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 63dd1fad2e51..c9fa3b345f01 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 @@ -100,4 +100,4 @@ This means any functions called at compile-time are invalid targets for this met Mutates the function to be unconstrained (if `true` is given) or not (if `false` is given). 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. \ No newline at end of file +This means any functions called at compile-time are invalid targets for this method. diff --git a/noir/noir-repo/noir_stdlib/src/append.nr b/noir/noir-repo/noir_stdlib/src/append.nr index 4577ae199b87..22baa9205a07 100644 --- a/noir/noir-repo/noir_stdlib/src/append.nr +++ b/noir/noir-repo/noir_stdlib/src/append.nr @@ -25,11 +25,11 @@ impl Append for [T] { } impl Append for Quoted { - fn empty() -> Self { + comptime fn empty() -> Self { quote {} } - fn append(self, other: Self) -> Self { + comptime fn append(self, other: Self) -> Self { quote { $self $other } } } diff --git a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr index a4c0f642a828..fede1b17c07e 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr @@ -1,45 +1,188 @@ use crate::{cmp::Eq, convert::From}; +/// A `BoundedVec` is a growable storage similar to a `Vec` except that it +/// is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +/// via slices and thus is not subject to the same restrictions slices are (notably, nested +/// slices - and thus nested vectors as well - are disallowed). +/// +/// Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +/// pushing an additional element is also more efficient - the length only needs to be increased +/// by one. +/// +/// For these reasons `BoundedVec` should generally be preferred over `Vec` when there +/// is a reasonable maximum bound that can be placed on the vector. +/// +/// Example: +/// +/// ```noir +/// let mut vector: BoundedVec = BoundedVec::new(); +/// for i in 0..5 { +/// vector.push(i); +/// } +/// assert(vector.len() == 5); +/// assert(vector.max_len() == 10); +/// ``` struct BoundedVec { storage: [T; MaxLen], len: u32, } impl BoundedVec { + /// Creates a new, empty vector of length zero. + /// + /// Since this container is backed by an array internally, it still needs an initial value + /// to give each element. To resolve this, each element is zeroed internally. This value + /// is guaranteed to be inaccessible unless `get_unchecked` is used. + /// + /// Example: + /// + /// ```noir + /// let empty_vector: BoundedVec = BoundedVec::new(); + /// assert(empty_vector.len() == 0); + /// ``` + /// + /// Note that whenever calling `new` the maximum length of the vector should always be specified + /// via a type signature: + /// + /// ```noir + /// fn good() -> BoundedVec { + /// // Ok! MaxLen is specified with a type annotation + /// let v1: BoundedVec = BoundedVec::new(); + /// let v2 = BoundedVec::new(); + /// + /// // Ok! MaxLen is known from the type of `good`'s return value + /// v2 + /// } + /// + /// fn bad() { + /// // Error: Type annotation needed + /// // The compiller can't infer `MaxLen` from the following code: + /// let mut v3 = BoundedVec::new(); + /// v3.push(5); + /// } + /// ``` + /// + /// This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions + /// but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a + /// constraint failure at runtime when the vec is pushed to. pub fn new() -> Self { let zeroed = crate::mem::zeroed(); BoundedVec { storage: [zeroed; MaxLen], len: 0 } } - /// Get an element from the vector at the given index. - /// Panics if the given index points beyond the end of the vector (`self.len()`). + /// Retrieves an element from the vector at the given index, starting from zero. + /// + /// If the given index is equal to or greater than the length of the vector, this + /// will issue a constraint failure. + /// + /// Example: + /// + /// ```noir + /// fn foo(v: BoundedVec) { + /// let first = v.get(0); + /// let last = v.get(v.len() - 1); + /// assert(first != last); + /// } + /// ``` pub fn get(self, index: u32) -> T { assert(index < self.len, "Attempted to read past end of BoundedVec"); self.get_unchecked(index) } - /// Get an element from the vector at the given index. - /// Responds with undefined data for `index` where `self.len < index < self.max_len()`. + /// Retrieves an element from the vector at the given index, starting from zero, without + /// performing a bounds check. + /// + /// Since this function does not perform a bounds check on length before accessing the element, + /// it is unsafe! Use at your own risk! + /// + /// Example: + /// + /// ```noir + /// fn sum_of_first_three(v: BoundedVec) -> u32 { + /// // Always ensure the length is larger than the largest + /// // index passed to get_unchecked + /// assert(v.len() > 2); + /// let first = v.get_unchecked(0); + /// let second = v.get_unchecked(1); + /// let third = v.get_unchecked(2); + /// first + second + third + /// } + /// ``` pub fn get_unchecked(self, index: u32) -> T { self.storage[index] } - /// Write an element to the vector at the given index. - /// Panics if the given index points beyond the end of the vector (`self.len()`). + /// Writes an element to the vector at the given index, starting from zero. + /// + /// If the given index is equal to or greater than the length of the vector, this will issue a constraint failure. + /// + /// Example: + /// + /// ```noir + /// fn foo(v: BoundedVec) { + /// let first = v.get(0); + /// assert(first != 42); + /// v.set(0, 42); + /// let new_first = v.get(0); + /// assert(new_first == 42); + /// } + /// ``` pub fn set(&mut self, index: u32, value: T) { assert(index < self.len, "Attempted to write past end of BoundedVec"); self.set_unchecked(index, value) } - /// Write an element to the vector at the given index. - /// Does not check whether the passed `index` is a valid index within the vector. - /// - /// Silently writes past the end of the vector for `index` where `self.len < index < self.max_len()` - /// Panics if the given index points beyond the maximum length of the vector (`self.max_len()`). + /// Writes an element to the vector at the given index, starting from zero, without performing a bounds check. + /// + /// Since this function does not perform a bounds check on length before accessing the element, it is unsafe! Use at your own risk! + /// + /// Example: + /// + /// ```noir + /// fn set_unchecked_example() { + /// let mut vec: BoundedVec = BoundedVec::new(); + /// vec.extend_from_array([1, 2]); + /// + /// // Here we're safely writing within the valid range of `vec` + /// // `vec` now has the value [42, 2] + /// vec.set_unchecked(0, 42); + /// + /// // We can then safely read this value back out of `vec`. + /// // Notice that we use the checked version of `get` which would prevent reading unsafe values. + /// assert_eq(vec.get(0), 42); + /// + /// // We've now written past the end of `vec`. + /// // As this index is still within the maximum potential length of `v`, + /// // it won't cause a constraint failure. + /// vec.set_unchecked(2, 42); + /// println(vec); + /// + /// // This will write past the end of the maximum potential length of `vec`, + /// // it will then trigger a constraint failure. + /// vec.set_unchecked(5, 42); + /// println(vec); + /// } + /// ``` pub fn set_unchecked(&mut self, index: u32, value: T) { self.storage[index] = value; } + /// Pushes an element to the end of the vector. This increases the length + /// of the vector by one. + /// + /// Panics if the new length of the vector will be greater than the max length. + /// + /// Example: + /// + /// ```noir + /// let mut v: BoundedVec = BoundedVec::new(); + /// + /// v.push(1); + /// v.push(2); + /// + /// // Panics with failed assertion "push out of bounds" + /// v.push(3); + /// ``` pub fn push(&mut self, elem: T) { assert(self.len < MaxLen, "push out of bounds"); @@ -47,20 +190,82 @@ impl BoundedVec { self.len += 1; } + /// Returns the current length of this vector + /// + /// Example: + /// + /// ```noir + /// let mut v: BoundedVec = BoundedVec::new(); + /// assert(v.len() == 0); + /// + /// v.push(100); + /// assert(v.len() == 1); + /// + /// v.push(200); + /// v.push(300); + /// v.push(400); + /// assert(v.len() == 4); + /// + /// let _ = v.pop(); + /// let _ = v.pop(); + /// assert(v.len() == 2); + /// ``` pub fn len(self) -> u32 { self.len } + /// Returns the maximum length of this vector. This is always + /// equal to the `MaxLen` parameter this vector was initialized with. + /// + /// Example: + /// + /// ```noir + /// let mut v: BoundedVec = BoundedVec::new(); + /// + /// assert(v.max_len() == 5); + /// v.push(10); + /// assert(v.max_len() == 5); + /// ``` pub fn max_len(_self: BoundedVec) -> u32 { MaxLen } - // This is a intermediate method, while we don't have an - // .extend method + /// Returns the internal array within this vector. + /// + /// Since arrays in Noir are immutable, mutating the returned storage array will not mutate + /// the storage held internally by this vector. + /// + /// Note that uninitialized elements may be zeroed out! + /// + /// Example: + /// + /// ```noir + /// let mut v: BoundedVec = BoundedVec::new(); + /// + /// assert(v.storage() == [0, 0, 0, 0, 0]); + /// + /// v.push(57); + /// assert(v.storage() == [57, 0, 0, 0, 0]); + /// ``` pub fn storage(self) -> [T; MaxLen] { self.storage } + /// Pushes each element from the given array to this vector. + /// + /// Panics if pushing each element would cause the length of this vector + /// to exceed the maximum length. + /// + /// Example: + /// + /// ```noir + /// let mut vec: BoundedVec = BoundedVec::new(); + /// vec.extend_from_array([2, 4]); + /// + /// assert(vec.len == 2); + /// assert(vec.get(0) == 2); + /// assert(vec.get(1) == 4); + /// ``` pub fn extend_from_array(&mut self, array: [T; Len]) { let new_len = self.len + array.len(); assert(new_len <= MaxLen, "extend_from_array out of bounds"); @@ -70,6 +275,21 @@ impl BoundedVec { self.len = new_len; } + /// Pushes each element from the given slice to this vector. + /// + /// Panics if pushing each element would cause the length of this vector + /// to exceed the maximum length. + /// + /// Example: + /// + /// ```noir + /// let mut vec: BoundedVec = BoundedVec::new(); + /// vec.extend_from_slice(&[2, 4]); + /// + /// assert(vec.len == 2); + /// assert(vec.get(0) == 2); + /// assert(vec.get(1) == 4); + /// ``` pub fn extend_from_slice(&mut self, slice: [T]) { let new_len = self.len + slice.len(); assert(new_len <= MaxLen, "extend_from_slice out of bounds"); @@ -79,6 +299,22 @@ impl BoundedVec { self.len = new_len; } + /// Pushes each element from the other vector to this vector. The length of + /// the other vector is left unchanged. + /// + /// Panics if pushing each element would cause the length of this vector + /// to exceed the maximum length. + /// + /// ```noir + /// let mut v1: BoundedVec = BoundedVec::new(); + /// let mut v2: BoundedVec = BoundedVec::new(); + /// + /// v2.extend_from_array([1, 2, 3]); + /// v1.extend_from_bounded_vec(v2); + /// + /// assert(v1.storage() == [1, 2, 3, 0, 0]); + /// assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); + /// ``` pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { let append_len = vec.len(); let new_len = self.len + append_len; @@ -94,6 +330,14 @@ impl BoundedVec { self.len = new_len; } + /// Creates a new vector, populating it with values derived from an array input. + /// The maximum length of the vector is determined based on the type signature. + /// + /// Example: + /// + /// ```noir + /// let bounded_vec: BoundedVec = BoundedVec::from_array([1, 2, 3]) + /// ``` pub fn from_array(array: [T; Len]) -> Self { assert(Len <= MaxLen, "from array out of bounds"); let mut vec: BoundedVec = BoundedVec::new(); @@ -101,6 +345,27 @@ impl BoundedVec { vec } + /// Pops the element at the end of the vector. This will decrease the length + /// of the vector by one. + /// + /// Panics if the vector is empty. + /// + /// Example: + /// + /// ```noir + /// let mut v: BoundedVec = BoundedVec::new(); + /// v.push(1); + /// v.push(2); + /// + /// let two = v.pop(); + /// let one = v.pop(); + /// + /// assert(two == 2); + /// assert(one == 1); + /// + /// // error: cannot pop from an empty vector + /// let _ = v.pop(); + /// ``` pub fn pop(&mut self) -> T { assert(self.len > 0); self.len -= 1; @@ -110,6 +375,18 @@ impl BoundedVec { elem } + /// Returns true if the given predicate returns true for any element + /// in this vector. + /// + /// Example: + /// + /// ```noir + /// let mut v: BoundedVec = BoundedVec::new(); + /// v.extend_from_array([2, 4, 6]); + /// + /// let all_even = !v.any(|elem: u32| elem % 2 != 0); + /// assert(all_even); + /// ``` pub fn any(self, predicate: fn[Env](T) -> bool) -> bool { let mut ret = false; let mut exceeded_len = false; @@ -122,6 +399,17 @@ impl BoundedVec { ret } + /// Creates a new vector of equal size by calling a closure on each element in this vector. + /// + /// Example: + /// + /// ```noir + /// let vec: BoundedVec = BoundedVec::from_array([1, 2, 3, 4]); + /// let result = vec.map(|value| value * 2); + /// + /// let expected = BoundedVec::from_array([2, 4, 6, 8]); + /// assert_eq(result, expected); + /// ``` pub fn map(self, f: fn[Env](T) -> U) -> BoundedVec { let mut ret = BoundedVec::new(); ret.len = self.len(); diff --git a/noir/noir-repo/noir_stdlib/src/collections/map.nr b/noir/noir-repo/noir_stdlib/src/collections/map.nr index 27a7d0d35507..e84103aafc5a 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/map.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/map.nr @@ -10,14 +10,30 @@ use crate::collections::bounded_vec::BoundedVec; global MAX_LOAD_FACTOR_NUMERATOR = 3; global MAX_LOAD_FACTOR_DEN0MINATOR = 4; -// Hash table with open addressing and quadratic probing. -// Size of the underlying table must be known at compile time. -// It is advised to select capacity N as a power of two, or a prime number -// because utilized probing scheme is best tailored for it. +/// `HashMap` is used to efficiently store and look up key-value pairs. +/// +/// `HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +/// Note that due to hash collisions, the actual maximum number of elements stored by any particular +/// hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +/// every hash value will be performed modulo `MaxLen`. +/// +/// Example: +/// +/// ```noir +/// // Create a mapping from Fields to u32s with a maximum length of 12 +/// // using a poseidon2 hasher +/// use std::hash::poseidon2::Poseidon2Hasher; +/// let mut map: HashMap> = HashMap::default(); +/// +/// map.insert(1, 2); +/// map.insert(3, 4); +/// +/// let two = map.get(1).unwrap(); +/// ``` struct HashMap { _table: [Slot; N], - // Amount of valid elements in the map. + /// Amount of valid elements in the map. _len: u32, _build_hasher: B @@ -77,7 +93,16 @@ impl Slot { // that if we have went that far without finding desired, // it is very unlikely to be after - performance will be heavily degraded. impl HashMap { - // Creates a new instance of HashMap with specified BuildHasher. + /// Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple + /// hashmaps are created with the same hasher instance. + /// + /// Example: + /// + /// ```noir + /// let my_hasher: BuildHasherDefault = Default::default(); + /// let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + /// assert(hashmap.is_empty()); + /// ``` // docs:start:with_hasher pub fn with_hasher(_build_hasher: B) -> Self where @@ -88,7 +113,15 @@ impl HashMap { Self { _table, _len, _build_hasher } } - // Clears the map, removing all key-value entries. + /// Clears the hashmap, removing all key-value pairs from it. + /// + /// Example: + /// + /// ```noir + /// assert(!map.is_empty()); + /// map.clear(); + /// assert(map.is_empty()); + /// ``` // docs:start:clear pub fn clear(&mut self) { // docs:end:clear @@ -96,7 +129,19 @@ impl HashMap { self._len = 0; } - // Returns true if the map contains a value for the specified key. + /// Returns `true` if the hashmap contains the given key. Unlike `get`, this will not also return + /// the value associated with the key. + /// + /// Example: + /// + /// ```noir + /// if map.contains_key(7) { + /// let value = map.get(7); + /// assert(value.is_some()); + /// } else { + /// println("No value for key 7!"); + /// } + /// ``` // docs:start:contains_key pub fn contains_key( self, @@ -110,15 +155,43 @@ impl HashMap { self.get(key).is_some() } - // Returns true if the map contains no elements. + /// Returns `true` if the length of the hash map is empty. + /// + /// Example: + /// + /// ```noir + /// assert(map.is_empty()); + /// + /// map.insert(1, 2); + /// assert(!map.is_empty()); + /// + /// map.remove(1); + /// assert(map.is_empty()); + /// ``` // docs:start:is_empty pub fn is_empty(self) -> bool { // docs:end:is_empty self._len == 0 } - // Returns a BoundedVec of all valid entries in this HashMap. - // The length of the returned vector will always match the length of this HashMap. + /// Returns a vector of each key-value pair present in the hashmap. + /// + /// The length of the returned vector is always equal to the length of the hashmap. + /// + /// Example: + /// + /// ```noir + /// let entries = map.entries(); + /// + /// // The length of a hashmap may not be compile-time known, so we + /// // need to loop over its capacity instead + /// for i in 0..map.capacity() { + /// if i < entries.len() { + /// let (key, value) = entries.get(i); + /// println(f"{key} -> {value}"); + /// } + /// } + /// ``` // docs:start:entries pub fn entries(self) -> BoundedVec<(K, V), N> { // docs:end:entries @@ -138,8 +211,23 @@ impl HashMap { entries } - // Returns a BoundedVec containing all the keys within this HashMap. - // The length of the returned vector will always match the length of this HashMap. + /// Returns a vector of each key present in the hashmap. + /// + /// The length of the returned vector is always equal to the length of the hashmap. + /// + /// Example: + /// + /// ```noir + /// let keys = map.keys(); + /// + /// for i in 0..keys.max_len() { + /// if i < keys.len() { + /// let key = keys.get_unchecked(i); + /// let value = map.get(key).unwrap_unchecked(); + /// println(f"{key} -> {value}"); + /// } + /// } + /// ``` // docs:start:keys pub fn keys(self) -> BoundedVec { // docs:end:keys @@ -158,8 +246,22 @@ impl HashMap { keys } - // Returns a BoundedVec containing all the values within this HashMap. - // The length of the returned vector will always match the length of this HashMap. + /// Returns a vector of each value present in the hashmap. + /// + /// The length of the returned vector is always equal to the length of the hashmap. + /// + /// Example: + /// + /// ```noir + /// let values = map.values(); + /// + /// for i in 0..values.max_len() { + /// if i < values.len() { + /// let value = values.get_unchecked(i); + /// println(f"Found value {value}"); + /// } + /// } + /// ``` // docs:start:values pub fn values(self) -> BoundedVec { // docs:end:values @@ -180,7 +282,22 @@ impl HashMap { values } - // For each key-value entry applies mutator function. + /// Iterates through each key-value pair of the HashMap, setting each key-value pair to the + /// result returned from the given function. + /// + /// Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated + /// through. If this is not desired, use `iter_values_mut` if only values need to be mutated, + /// or `entries` if neither keys nor values need to be mutated. + /// + /// The iteration order is left unspecified. As a result, if two keys are mutated to become + /// equal, which of the two values that will be present for the key in the resulting map is also unspecified. + /// + /// Example: + /// + /// ```noir + /// // Add 1 to each key in the map, and double the value associated with that key. + /// map.iter_mut(|k, v| (k + 1, v * 2)); + /// ``` // docs:start:iter_mut pub fn iter_mut( &mut self, @@ -205,7 +322,22 @@ impl HashMap { self._table = new_map._table; } - // For each key applies mutator function. + /// Iterates through the HashMap, mutating each key to the result returned from + /// the given function. + /// + /// Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated + /// through. If only iteration is desired and the keys are not intended to be mutated, + /// prefer using `entries` instead. + /// + /// The iteration order is left unspecified. As a result, if two keys are mutated to become + /// equal, which of the two values that will be present for the key in the resulting map is also unspecified. + /// + /// Example: + /// + /// ```noir + /// // Double each key, leaving the value associated with that key untouched + /// map.iter_keys_mut(|k| k * 2); + /// ``` // docs:start:iter_keys_mut pub fn iter_keys_mut( &mut self, @@ -230,7 +362,16 @@ impl HashMap { self._table = new_map._table; } - // For each value applies mutator function. + /// Iterates through the HashMap, applying the given function to each value and mutating the + /// value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` + /// because the keys are untouched and the underlying hashmap thus does not need to be reordered. + /// + /// Example: + /// + /// ```noir + /// // Halve each value + /// map.iter_values_mut(|v| v / 2); + /// ``` // docs:start:iter_values_mut pub fn iter_values_mut(&mut self, f: fn(V) -> V) { // docs:end:iter_values_mut @@ -244,7 +385,14 @@ impl HashMap { } } - // Retains only the elements specified by the predicate. + /// Retains only the key-value pairs for which the given function returns true. + /// Any key-value pairs for which the function returns false will be removed from the map. + /// + /// Example: + /// + /// ```noir + /// map.retain(|k, v| (k != 0) & (v != 0)); + /// ``` // docs:start:retain pub fn retain(&mut self, f: fn(K, V) -> bool) { // docs:end:retain @@ -261,21 +409,67 @@ impl HashMap { } } - // Amount of active key-value entries. + /// Returns the current length of this hash map. + /// + /// Example: + /// + /// ```noir + /// // This is equivalent to checking map.is_empty() + /// assert(map.len() == 0); + /// + /// map.insert(1, 2); + /// map.insert(3, 4); + /// map.insert(5, 6); + /// assert(map.len() == 3); + /// + /// // 3 was already present as a key in the hash map, so the length is unchanged + /// map.insert(3, 7); + /// assert(map.len() == 3); + /// + /// map.remove(1); + /// assert(map.len() == 2); + /// ``` // docs:start:len pub fn len(self) -> u32 { // docs:end:len self._len } - // Get the compile-time map capacity. + /// Returns the maximum capacity of this hashmap. This is always equal to the capacity + /// specified in the hashmap's type. + /// + /// Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a + /// static capacity that does not increase as the map grows larger. Thus, this capacity + /// is also the maximum possible element count that can be inserted into the hashmap. + /// Due to hash collisions (modulo the hashmap length), it is likely the actual maximum + /// element count will be lower than the full capacity. + /// + /// Example: + /// + /// ```noir + /// let empty_map: HashMap> = HashMap::default(); + /// assert(empty_map.len() == 0); + /// assert(empty_map.capacity() == 42); + /// ``` // docs:start:capacity pub fn capacity(_self: Self) -> u32 { // docs:end:capacity N } - // Get the value by key. If it does not exist, returns none(). + /// Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + /// + /// Example: + /// + /// ```noir + /// fn get_example(map: HashMap>) { + /// let x = map.get(12); + /// + /// if x.is_some() { + /// assert(x.unwrap() == 42); + /// } + /// } + /// ``` // docs:start:get pub fn get( self, @@ -310,7 +504,16 @@ impl HashMap { result } - // Insert key-value entry. In case key was already present, value is overridden. + /// Inserts a new key-value pair into the map. If the key was already in the map, its + /// previous value will be overridden with the newly provided one. + /// + /// Example: + /// + /// ```noir + /// let mut map: HashMap> = HashMap::default(); + /// map.insert(12, 42); + /// assert(map.len() == 1); + /// ``` // docs:start:insert pub fn insert( &mut self, @@ -353,7 +556,23 @@ impl HashMap { } } - // Removes a key-value entry. If key is not present, HashMap remains unchanged. + /// Removes the given key-value pair from the map. If the key was not already present + /// in the map, this does nothing. + /// + /// Example: + /// + /// ```noir + /// let mut map: HashMap> = HashMap::default(); + /// map.insert(12, 42); + /// assert(!map.is_empty()); + /// + /// map.remove(12); + /// assert(map.is_empty()); + /// + /// // If a key was not present in the map, remove does nothing + /// map.remove(12); + /// assert(map.is_empty()); + /// ``` // docs:start:remove pub fn remove( &mut self, @@ -432,6 +651,22 @@ where B: BuildHasher, H: Hasher { + /// Checks if two HashMaps are equal. + /// + /// Example: + /// + /// ```noir + /// let mut map1: HashMap> = HashMap::default(); + /// let mut map2: HashMap> = HashMap::default(); + /// + /// map1.insert(1, 2); + /// map1.insert(3, 4); + /// + /// map2.insert(3, 4); + /// map2.insert(1, 2); + /// + /// assert(map1 == map2); + /// ``` fn eq(self, other: HashMap) -> bool { // docs:end:eq let mut equal = false; @@ -466,8 +701,16 @@ where B: BuildHasher + Default, H: Hasher + Default { + /// Constructs an empty HashMap. + /// + /// Example: + /// + /// ```noir + /// let hashmap: HashMap> = HashMap::default(); + /// assert(hashmap.is_empty()); + /// ``` fn default() -> Self { -// docs:end:default + // docs:end:default let _build_hasher = B::default(); let map: HashMap = HashMap::with_hasher(_build_hasher); map diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon/bn254/consts.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon/bn254/consts.nr index 81d78377ce8f..3b28ced5835f 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon/bn254/consts.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon/bn254/consts.nr @@ -5,11 +5,6 @@ // Consistent with https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom and https://github.com/iden3/circomlib/blob/master/circuits/poseidon_constants.circom use crate::hash::poseidon::PoseidonConfig; use crate::hash::poseidon::config; -// Number of full rounds -// Number of partial rounds -fn rp() -> [u8; 16] { - [56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68] -} // S-box power fn alpha() -> Field { 5 diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon/mod.nr index 963808f60532..cf9b6187c023 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon/mod.nr @@ -1,5 +1,4 @@ mod bn254; // Instantiations of Poseidon for prime field of the same order as BN254 -use crate::field::modulus_num_bits; use crate::hash::Hasher; use crate::default::Default; @@ -166,13 +165,6 @@ fn sigma(x: [Field; O]) -> [Field; O] { y } -// Check security of sponge instantiation -fn check_security(rate: Field, width: Field, security: Field) -> bool { - let n = modulus_num_bits(); - - ((n - 1) as Field * (width - rate) / 2) as u8 > security as u8 -} - struct PoseidonHasher{ _state: [Field], } diff --git a/noir/noir-repo/noir_stdlib/src/lib.nr b/noir/noir-repo/noir_stdlib/src/lib.nr index 81c408c01bc9..714079d306a1 100644 --- a/noir/noir-repo/noir_stdlib/src/lib.nr +++ b/noir/noir-repo/noir_stdlib/src/lib.nr @@ -28,6 +28,7 @@ mod runtime; mod meta; mod append; mod mem; +mod panic; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident diff --git a/noir/noir-repo/noir_stdlib/src/meta/expr.nr b/noir/noir-repo/noir_stdlib/src/meta/expr.nr index 43638ad791b0..642dbecc36b0 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/expr.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/expr.nr @@ -66,6 +66,11 @@ impl Expr { fn as_index(self) -> Option<(Expr, Expr)> {} // docs:end:as_index + #[builtin(expr_as_let)] + // docs:start:as_let + fn as_let(self) -> Option<(Expr, Option, Expr)> {} + // docs:end:as_let + #[builtin(expr_as_member_access)] // docs:start:as_member_access fn as_member_access(self) -> Option<(Expr, Quoted)> {} @@ -134,6 +139,7 @@ impl Expr { 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_let(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)); @@ -153,7 +159,7 @@ impl Expr { } // docs:start:quoted - fn quoted(self) -> Quoted { + comptime fn quoted(self) -> Quoted { // docs:end:quoted quote { $self } } @@ -280,6 +286,17 @@ fn modify_index(expr: Expr, f: fn[Env](Expr) -> Option) -> Option(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { + expr.as_let().map( + |expr: (Expr, Option, Expr)| { + let (pattern, typ, expr) = expr; + let pattern = pattern.modify(f); + let expr = expr.modify(f); + new_let(pattern, typ, expr) + } + ) +} + fn modify_member_access(expr: Expr, f: fn[Env](Expr) -> Option) -> Option { expr.as_member_access().map( |expr: (Expr, Quoted)| { @@ -364,12 +381,12 @@ fn modify_expressions(exprs: [Expr], f: fn[Env](Expr) -> Option) -> [ exprs.map(|expr: Expr| expr.modify(f)) } -fn new_array(exprs: [Expr]) -> Expr { +comptime 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 { +comptime fn new_assert(predicate: Expr, msg: Option) -> Expr { if msg.is_some() { let msg = msg.unwrap(); quote { assert($predicate, $msg) }.as_expr().unwrap() @@ -378,7 +395,7 @@ fn new_assert(predicate: Expr, msg: Option) -> Expr { } } -fn new_assert_eq(lhs: Expr, rhs: Expr, msg: Option) -> Expr { +comptime fn new_assert_eq(lhs: Expr, rhs: Expr, msg: Option) -> Expr { if msg.is_some() { let msg = msg.unwrap(); quote { assert_eq($lhs, $rhs, $msg) }.as_expr().unwrap() @@ -387,30 +404,30 @@ fn new_assert_eq(lhs: Expr, rhs: Expr, msg: Option) -> Expr { } } -fn new_assign(lhs: Expr, rhs: Expr) -> Expr { +comptime 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 { +comptime 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 { +comptime 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 { +comptime fn new_cast(expr: Expr, typ: UnresolvedType) -> Expr { quote { ($expr) as $typ }.as_expr().unwrap() } -fn new_comptime(exprs: [Expr]) -> Expr { +comptime 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 { +comptime 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() @@ -419,21 +436,30 @@ fn new_if(condition: Expr, consequence: Expr, alternative: Option) -> Expr } } -fn new_index(object: Expr, index: Expr) -> Expr { +comptime fn new_index(object: Expr, index: Expr) -> Expr { quote { $object[$index] }.as_expr().unwrap() } -fn new_member_access(object: Expr, name: Quoted) -> Expr { +comptime fn new_let(pattern: Expr, typ: Option, expr: Expr) -> Expr { + if typ.is_some() { + let typ = typ.unwrap(); + quote { let $pattern : $typ = $expr; }.as_expr().unwrap() + } else { + quote { let $pattern = $expr; }.as_expr().unwrap() + } +} + +comptime fn new_member_access(object: Expr, name: Quoted) -> Expr { quote { $object.$name }.as_expr().unwrap() } -fn new_function_call(function: Expr, arguments: [Expr]) -> Expr { +comptime 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 { +comptime fn new_method_call(object: Expr, name: Quoted, generics: [UnresolvedType], arguments: [Expr]) -> Expr { let arguments = join_expressions(arguments, quote { , }); if generics.len() == 0 { @@ -444,30 +470,30 @@ fn new_method_call(object: Expr, name: Quoted, generics: [UnresolvedType], argum } } -fn new_repeated_element_array(expr: Expr, length: Expr) -> Expr { +comptime 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 { +comptime fn new_repeated_element_slice(expr: Expr, length: Expr) -> Expr { quote { &[$expr; $length] }.as_expr().unwrap() } -fn new_slice(exprs: [Expr]) -> Expr { +comptime fn new_slice(exprs: [Expr]) -> Expr { let exprs = join_expressions(exprs, quote { , }); quote { &[$exprs]}.as_expr().unwrap() } -fn new_tuple(exprs: [Expr]) -> Expr { +comptime 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 { +comptime 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 { +comptime fn new_unsafe(exprs: [Expr]) -> Expr { let exprs = join_expressions(exprs, quote { ; }); quote { unsafe { $exprs }}.as_expr().unwrap() } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 9fc399ddbf91..1079cc6013a1 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -122,6 +122,7 @@ mod tests { quote { 1 } } + #[test] fn returning_versus_macro_insertion() { comptime { diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index f3060a1648be..4b1044862018 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/op.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/op.nr @@ -28,7 +28,7 @@ impl UnaryOp { } // docs:start:unary_quoted - pub fn quoted(self) -> Quoted { + pub comptime fn quoted(self) -> Quoted { // docs:end:unary_quoted if self.is_minus() { quote { - } @@ -39,7 +39,8 @@ impl UnaryOp { } else if self.is_dereference() { quote { * } } else { - crate::mem::zeroed() + let op = self; + crate::panic::panic(f"Unexpected unary operator in UnaryOp::quoted: {op}") } } } @@ -146,7 +147,7 @@ impl BinaryOp { } // docs:start:binary_quoted - pub fn quoted(self) -> Quoted { + pub comptime fn quoted(self) -> Quoted { // docs:end:binary_quoted if self.is_add() { quote { + } @@ -181,7 +182,8 @@ impl BinaryOp { } else if self.is_modulo() { quote { % } } else { - crate::mem::zeroed() + let op = self; + crate::panic::panic(f"Unexpected binary operator in BinaryOp::quoted: {op}") } } } diff --git a/noir/noir-repo/noir_stdlib/src/panic.nr b/noir/noir-repo/noir_stdlib/src/panic.nr new file mode 100644 index 000000000000..833c1f3dcfc4 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/panic.nr @@ -0,0 +1,4 @@ +pub fn panic(message: fmtstr) -> U { + assert(false, message); + crate::mem::zeroed() +} diff --git a/noir/noir-repo/noir_stdlib/src/prelude.nr b/noir/noir-repo/noir_stdlib/src/prelude.nr index b14f38bdf559..b6e54eaae60a 100644 --- a/noir/noir-repo/noir_stdlib/src/prelude.nr +++ b/noir/noir-repo/noir_stdlib/src/prelude.nr @@ -7,3 +7,4 @@ pub use crate::cmp::{Eq, Ord}; pub use crate::default::Default; pub use crate::convert::{From, Into}; pub use crate::meta::{derive, derive_via}; +pub use crate::panic::panic; diff --git a/noir/noir-repo/test_programs/compile_failure/quote_at_runtime/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/quote_at_runtime/Nargo.toml new file mode 100644 index 000000000000..3ce62412b326 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/quote_at_runtime/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "quote_at_runtime" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_failure/quote_at_runtime/src/main.nr b/noir/noir-repo/test_programs/compile_failure/quote_at_runtime/src/main.nr new file mode 100644 index 000000000000..5da409682f69 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/quote_at_runtime/src/main.nr @@ -0,0 +1,7 @@ +fn main() { + foo(quote { test }) +} + +fn foo(q: Quoted) { + println(q); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr index 25c9402035c0..79bf3e295339 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/attributes_struct/src/main.nr @@ -14,7 +14,7 @@ struct Foo { } -fn add_attribute(s: StructDefinition) { +comptime fn add_attribute(s: StructDefinition) { assert(!s.has_named_attribute(quote { foo })); s.add_attribute("foo"); assert(s.has_named_attribute(quote { foo })); 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 62f119cc0c0d..6bfd8ef96990 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 @@ -74,7 +74,7 @@ mod foo { #[attr] pub fn some() {} - fn attr(f: FunctionDefinition) { + comptime fn attr(f: FunctionDefinition) { assert_eq(f.module().name(), quote { foo }); assert(!f.is_unconstrained()); diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/Nargo.toml new file mode 100644 index 000000000000..2de6e4d149eb --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_global_using_trait" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr new file mode 100644 index 000000000000..a1a2c4b125a8 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_global_using_trait/src/main.nr @@ -0,0 +1,3 @@ +comptime global FOO: i32 = Default::default(); + +fn main() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr index baf45c517edb..c3ba7ba46434 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/main.nr @@ -21,23 +21,23 @@ mod separate_module; comptime mut global counter = 0; -fn increment_counter() { +comptime fn increment_counter() { counter += 1; } -fn outer_attribute_func(m: Module) -> Quoted { +comptime fn outer_attribute_func(m: Module) -> Quoted { assert_eq(m.name(), quote { yet_another_module }); increment_counter(); quote { pub fn generated_outer_function() {} } } -fn inner_attribute_func(m: Module) -> Quoted { +comptime fn inner_attribute_func(m: Module) -> Quoted { assert_eq(m.name(), quote { yet_another_module }); increment_counter(); quote { pub fn generated_inner_function() {} } } -fn outer_attribute_separate_module(m: Module) { +comptime fn outer_attribute_separate_module(m: Module) { assert_eq(m.name(), quote { separate_module }); increment_counter(); } @@ -49,7 +49,7 @@ mod add_to_me { fn add_to_me_function() {} } -fn add_function(m: Module) { +comptime fn add_function(m: Module) { m.add_item( quote { pub fn added_function() -> super::Foo { add_to_me_function(); diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/separate_module.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/separate_module.nr index 53784101507c..c1aeac4622c1 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/separate_module.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_module/src/separate_module.nr @@ -1,5 +1,6 @@ #![inner_attribute_separate_module] -fn inner_attribute_separate_module(m: Module) { + +comptime fn inner_attribute_separate_module(m: Module) { assert_eq(m.name(), quote { separate_module }); super::increment_counter(); } 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 index 26be3135133c..9dc8cf1ed476 100644 --- 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 @@ -28,7 +28,7 @@ fn zero() -> Field { 0 } -fn inject_context(f: FunctionDefinition) { +comptime 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())); @@ -39,7 +39,7 @@ fn inject_context(f: FunctionDefinition) { f.set_body(body); } -fn mapping_function(expr: Expr, f: FunctionDefinition) -> Option { +comptime fn mapping_function(expr: Expr, f: FunctionDefinition) -> Option { expr.as_function_call().and_then( |func_call: (Expr, [Expr])| { let (name, arguments) = func_call; diff --git a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr index e5aa5f88a94c..7b3e63df0729 100644 --- a/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/noir/noir-repo/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -1,6 +1,6 @@ #[test] -fn test_vec_new_foo() { - foo(); +fn test_vec_new_good() { + good(); } #[test(should_fail)] @@ -9,19 +9,19 @@ fn test_vec_new_bad() { } // docs:start:new_example -fn foo() -> BoundedVec { +fn good() -> BoundedVec { // Ok! MaxLen is specified with a type annotation let v1: BoundedVec = BoundedVec::new(); let v2 = BoundedVec::new(); - // Ok! MaxLen is known from the type of foo's return value + // Ok! MaxLen is known from the type of `good`'s return value v2 } fn bad() { + // Error: Type annotation needed + // The compiller can't infer `MaxLen` from this code. let mut v3 = BoundedVec::new(); - - // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. v3.push(5); } // docs:end:new_example 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 c082c1dde331..c1f70e7aceef 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 @@ -16,7 +16,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_array() { + fn test_expr_modify_for_array() { comptime { let expr = quote { [1, 2, 4] }.as_expr().unwrap(); @@ -46,7 +46,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_assert() { + fn test_expr_modify_for_assert() { comptime { let expr = quote { assert(1) }.as_expr().unwrap(); @@ -82,7 +82,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_assert_eq() { + fn test_expr_modify_for_assert_eq() { comptime { let expr = quote { assert_eq(1, 2) }.as_expr().unwrap(); @@ -113,7 +113,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_assign() { + fn test_expr_modify_for_assign() { comptime { let expr = quote { { a = 1; } }.as_expr().unwrap(); @@ -142,7 +142,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_block() { + fn test_expr_modify_for_block() { comptime { let expr = quote { { 1; 4; 23 } }.as_expr().unwrap(); @@ -178,7 +178,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_method_call() { + fn test_expr_modify_for_method_call() { comptime { let expr = quote { foo.bar(3, 4) }.as_expr().unwrap(); @@ -209,7 +209,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_integer() { + fn test_expr_modify_for_integer() { comptime { let expr = quote { 1 }.as_expr().unwrap(); @@ -243,7 +243,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_binary_op() { + fn test_expr_modify_for_binary_op() { comptime { let expr = quote { 3 + 4 }.as_expr().unwrap(); @@ -280,7 +280,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_cast() { + fn test_expr_modify_for_cast() { comptime { let expr = quote { 1 as Field }.as_expr().unwrap(); @@ -302,7 +302,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_comptime() { + fn test_expr_modify_for_comptime() { comptime { let expr = quote { comptime { 1; 4; 23 } }.as_expr().unwrap(); @@ -342,7 +342,7 @@ mod tests { // docs:end:as_expr_example #[test] - fn test_expr_mutate_for_function_call() { + fn test_expr_modify_for_function_call() { comptime { let expr = quote { foo(42) }.as_expr().unwrap(); @@ -368,7 +368,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_if() { + fn test_expr_modify_for_if() { comptime { let expr = quote { if 1 { 2 } }.as_expr().unwrap(); @@ -400,7 +400,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_index() { + fn test_expr_modify_for_index() { comptime { let expr = quote { 1[2] }.as_expr().unwrap(); @@ -422,7 +422,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_member_access() { + fn test_expr_modify_for_member_access() { comptime { let expr = quote { 1.bar }.as_expr().unwrap(); @@ -457,7 +457,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_repeated_element_array() { + fn test_expr_modify_for_repeated_element_array() { comptime { let expr = quote { [1; 3] }.as_expr().unwrap(); @@ -480,7 +480,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_repeated_element_slice() { + fn test_expr_modify_for_repeated_element_slice() { comptime { let expr = quote { &[1; 3] }.as_expr().unwrap(); @@ -505,7 +505,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_slice() { + fn test_expr_modify_for_slice() { comptime { let expr = quote { &[1, 3, 5] }.as_expr().unwrap(); @@ -529,7 +529,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_tuple() { + fn test_expr_modify_for_tuple() { comptime { let expr = quote { (1, 2) }.as_expr().unwrap(); @@ -553,7 +553,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_unary_op() { + fn test_expr_modify_for_unary_op() { comptime { let expr = quote { -(1) }.as_expr().unwrap(); @@ -575,7 +575,7 @@ mod tests { } #[test] - fn test_expr_mutate_for_unsafe() { + fn test_expr_modify_for_unsafe() { comptime { let expr = quote { unsafe { 1; 4; 23 } }.as_expr().unwrap(); @@ -606,6 +606,41 @@ mod tests { } } + #[test] + fn test_expr_as_let() { + comptime + { + let expr = quote { let x: Field = 1; }.as_expr().unwrap(); + let (_pattern, typ, expr) = expr.as_let().unwrap(); + assert(typ.unwrap().is_field()); + assert_eq(expr.as_integer().unwrap(), (1, false)); + } + } + + #[test] + fn test_expr_modify_for_let() { + comptime + { + let expr = quote { let x : Field = 1; }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (_pattern, typ, expr) = expr.as_let().unwrap(); + assert(typ.unwrap().is_field()); + assert_eq(expr.as_integer().unwrap(), (2, false)); + } + } + + #[test] + fn test_expr_modify_for_let_without_type() { + comptime + { + let expr = quote { let x = 1; }.as_expr().unwrap(); + let expr = expr.modify(times_two); + let (_pattern, typ, expr) = expr.as_let().unwrap(); + assert(typ.is_none()); + assert_eq(expr.as_integer().unwrap(), (2, false)); + } + } + #[test] fn test_automatically_unwraps_parenthesized_expression() { comptime diff --git a/noir/noir-repo/tooling/lsp/src/modules.rs b/noir/noir-repo/tooling/lsp/src/modules.rs index d78da15a8ffa..cce7f324a2e6 100644 --- a/noir/noir-repo/tooling/lsp/src/modules.rs +++ b/noir/noir-repo/tooling/lsp/src/modules.rs @@ -122,7 +122,7 @@ pub(crate) fn module_id_path( if !is_relative { // We don't record module attributes for the root module, // so we handle that case separately - if let CrateId::Root(_) = target_module_id.krate { + if target_module_id.krate.is_root() { segments.push("crate"); } } diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 87e7bea8c3b0..96e339ee212c 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -7,7 +7,7 @@ use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use fm::{FileManager, FileMap}; use fxhash::FxHashMap as HashMap; use lsp_types::{DiagnosticTag, Url}; -use noirc_driver::{check_crate, file_manager_with_stdlib}; +use noirc_driver::check_crate; use noirc_errors::{DiagnosticKind, FileDiagnostic}; use crate::types::{ @@ -120,7 +120,8 @@ pub(crate) fn process_workspace_for_noir_document( ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) })?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); + insert_all_files_for_workspace_into_file_manager( state, &workspace, diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion.rs b/noir/noir-repo/tooling/lsp/src/requests/completion.rs index dad0d37aba74..de5c36bc59f7 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion.rs @@ -4,10 +4,7 @@ use std::{ }; use async_lsp::ResponseError; -use completion_items::{ - crate_completion_item, field_completion_item, simple_completion_item, snippet_completion_item, - struct_field_completion_item, -}; +use completion_items::{field_completion_item, simple_completion_item, snippet_completion_item}; use convert_case::{Case, Casing}; use fm::{FileId, FileMap, PathString}; use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}; @@ -183,20 +180,23 @@ impl<'a> NodeFinder<'a> { 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); - } + let mut fields: Vec<_> = + struct_type.get_fields_as_written().into_iter().enumerate().collect(); // Remove the ones that already exists in the constructor - for (field, _) in &constructor_expression.fields { - fields.remove(&field.0.contents); + for (used_name, _) in &constructor_expression.fields { + fields.retain(|(_, (name, _))| name != &used_name.0.contents); } let self_prefix = false; - for (field, typ) in fields { - self.completion_items.push(struct_field_completion_item(field, typ, self_prefix)); + for (field_index, (field, typ)) in &fields { + self.completion_items.push(self.struct_field_completion_item( + field, + typ, + struct_type.id, + *field_index, + self_prefix, + )); } } @@ -508,6 +508,7 @@ impl<'a> NodeFinder<'a> { self.collect_local_variables(pattern); } } + Pattern::Interned(..) => (), } } @@ -652,9 +653,15 @@ impl<'a> NodeFinder<'a> { prefix: &str, self_prefix: bool, ) { - for (name, typ) in &struct_type.get_fields(generics) { + for (field_index, (name, typ)) in struct_type.get_fields(generics).iter().enumerate() { if name_matches(name, prefix) { - self.completion_items.push(struct_field_completion_item(name, typ, self_prefix)); + self.completion_items.push(self.struct_field_completion_item( + name, + typ, + struct_type.id, + field_index, + self_prefix, + )); } } } @@ -746,7 +753,10 @@ impl<'a> NodeFinder<'a> { 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)); + let root_id = self.def_maps[&dependency.crate_id].root(); + let module_id = ModuleId { krate: dependency.crate_id, local_id: root_id }; + self.completion_items + .push(self.crate_completion_item(dependency_name, module_id)); } } @@ -846,7 +856,7 @@ impl<'a> NodeFinder<'a> { } } Pattern::Mutable(pattern, ..) => self.try_set_self_type(pattern), - Pattern::Tuple(..) | Pattern::Struct(..) => (), + Pattern::Tuple(..) | Pattern::Struct(..) | Pattern::Interned(..) => (), } } @@ -937,7 +947,7 @@ impl<'a> Visitor for NodeFinder<'a> { self.collect_type_parameters_in_generics(&noir_trait_impl.impl_generics); for item in &noir_trait_impl.items { - item.accept(self); + item.item.accept(self); } self.type_parameters.clear(); @@ -952,7 +962,7 @@ impl<'a> Visitor for NodeFinder<'a> { self.collect_type_parameters_in_generics(&type_impl.generics); for (method, span) in &type_impl.methods { - method.accept(*span, self); + method.item.accept(*span, self); // Optimization: stop looking in functions past the completion cursor if span.end() as usize > self.byte_index { @@ -973,8 +983,8 @@ impl<'a> Visitor for NodeFinder<'a> { 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); + for field in &noir_struct.fields { + field.item.typ.accept(self); } self.type_parameters.clear(); 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 bca1061ff47b..520c158d2607 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -87,7 +87,7 @@ impl<'a> NodeFinder<'a> { pub(super) fn suggest_builtin_attributes(&mut self, prefix: &str, target: AttributeTarget) { match target { - AttributeTarget::Module => (), + AttributeTarget::Module | AttributeTarget::Trait => (), AttributeTarget::Struct => { self.suggest_one_argument_attributes(prefix, &["abi"]); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs index c3afc225f525..7e7511cdaa32 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/completion_items.rs @@ -1,11 +1,13 @@ use lsp_types::{ - Command, CompletionItem, CompletionItemKind, CompletionItemLabelDetails, InsertTextFormat, + Command, CompletionItem, CompletionItemKind, CompletionItemLabelDetails, Documentation, + InsertTextFormat, MarkupContent, MarkupKind, }; use noirc_frontend::{ ast::AttributeTarget, + hir::def_map::ModuleId, hir_def::{function::FuncMeta, stmt::HirPattern}, - macros_api::ModuleDefId, - node_interner::{FuncId, GlobalId}, + macros_api::{ModuleDefId, StructId}, + node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, QuotedType, Type, }; @@ -47,6 +49,7 @@ impl<'a> NodeFinder<'a> { match target { AttributeTarget::Module => Some(Type::Quoted(QuotedType::Module)), AttributeTarget::Struct => Some(Type::Quoted(QuotedType::StructDefinition)), + AttributeTarget::Trait => Some(Type::Quoted(QuotedType::TraitDefinition)), AttributeTarget::Function => Some(Type::Quoted(QuotedType::FunctionDefinition)), } } else { @@ -54,7 +57,7 @@ impl<'a> NodeFinder<'a> { }; match module_def_id { - ModuleDefId::ModuleId(_) => Some(module_completion_item(name)), + ModuleDefId::ModuleId(id) => Some(self.module_completion_item(name, id)), ModuleDefId::FunctionId(func_id) => self.function_completion_item( &name, func_id, @@ -63,23 +66,61 @@ impl<'a> NodeFinder<'a> { attribute_first_type.as_ref(), false, // self_prefix ), - ModuleDefId::TypeId(..) => Some(self.struct_completion_item(name)), - ModuleDefId::TypeAliasId(..) => Some(self.type_alias_completion_item(name)), - ModuleDefId::TraitId(..) => Some(self.trait_completion_item(name)), + ModuleDefId::TypeId(struct_id) => Some(self.struct_completion_item(name, struct_id)), + ModuleDefId::TypeAliasId(id) => Some(self.type_alias_completion_item(name, id)), + ModuleDefId::TraitId(trait_id) => Some(self.trait_completion_item(name, trait_id)), ModuleDefId::GlobalId(global_id) => Some(self.global_completion_item(name, global_id)), } } - fn struct_completion_item(&self, name: String) -> CompletionItem { - simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) + pub(super) fn crate_completion_item( + &self, + name: impl Into, + id: ModuleId, + ) -> CompletionItem { + self.module_completion_item(name, id) + } + + pub(super) fn module_completion_item( + &self, + name: impl Into, + id: ModuleId, + ) -> CompletionItem { + let completion_item = module_completion_item(name); + self.completion_item_with_doc_comments(ReferenceId::Module(id), completion_item) + } + + fn struct_completion_item(&self, name: String, struct_id: StructId) -> CompletionItem { + let completion_item = + simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)); + self.completion_item_with_doc_comments(ReferenceId::Struct(struct_id), completion_item) } - fn type_alias_completion_item(&self, name: String) -> CompletionItem { - simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) + pub(super) fn struct_field_completion_item( + &self, + field: &str, + typ: &Type, + struct_id: StructId, + field_index: usize, + self_type: bool, + ) -> CompletionItem { + let completion_item = struct_field_completion_item(field, typ, self_type); + self.completion_item_with_doc_comments( + ReferenceId::StructMember(struct_id, field_index), + completion_item, + ) + } + + fn type_alias_completion_item(&self, name: String, id: TypeAliasId) -> CompletionItem { + let completion_item = + simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)); + self.completion_item_with_doc_comments(ReferenceId::Alias(id), completion_item) } - fn trait_completion_item(&self, name: String) -> CompletionItem { - simple_completion_item(name.clone(), CompletionItemKind::INTERFACE, Some(name)) + fn trait_completion_item(&self, name: String, trait_id: TraitId) -> CompletionItem { + let completion_item = + simple_completion_item(name.clone(), CompletionItemKind::INTERFACE, Some(name)); + self.completion_item_with_doc_comments(ReferenceId::Trait(trait_id), completion_item) } fn global_completion_item(&self, name: String, global_id: GlobalId) -> CompletionItem { @@ -87,7 +128,9 @@ impl<'a> NodeFinder<'a> { let typ = self.interner.definition_type(global.definition_id); let description = typ.to_string(); - simple_completion_item(name, CompletionItemKind::CONSTANT, Some(description)) + let completion_item = + simple_completion_item(name, CompletionItemKind::CONSTANT, Some(description)); + self.completion_item_with_doc_comments(ReferenceId::Global(global_id), completion_item) } pub(super) fn function_completion_item( @@ -184,10 +227,14 @@ impl<'a> NodeFinder<'a> { function_kind, skip_first_argument, ); - let label = if insert_text.ends_with("()") { - format!("{}()", name) + let (label, insert_text) = if insert_text.ends_with("()") { + if skip_first_argument { + (name.to_string(), insert_text.strip_suffix("()").unwrap().to_string()) + } else { + (format!("{}()", name), insert_text) + } } else { - format!("{}(…)", name) + (format!("{}(…)", name), insert_text) }; snippet_completion_item(label, kind, insert_text, Some(description)) @@ -210,7 +257,8 @@ impl<'a> NodeFinder<'a> { completion_item_with_trigger_parameter_hints_command(completion_item) } }; - + let completion_item = + self.completion_item_with_doc_comments(ReferenceId::Function(func_id), completion_item); Some(completion_item) } @@ -259,6 +307,25 @@ impl<'a> NodeFinder<'a> { text } + fn completion_item_with_doc_comments( + &self, + id: ReferenceId, + completion_item: CompletionItem, + ) -> CompletionItem { + if let Some(doc_comments) = self.interner.doc_comments(id) { + let docs = doc_comments.join("\n"); + CompletionItem { + documentation: Some(Documentation::MarkupContent(MarkupContent { + kind: MarkupKind::Markdown, + value: docs, + })), + ..completion_item + } + } else { + completion_item + } + } + fn hir_pattern_to_argument(&self, pattern: &HirPattern, text: &mut String) { match pattern { HirPattern::Identifier(hir_ident) => { @@ -288,13 +355,6 @@ pub(super) fn module_completion_item(name: impl Into) -> CompletionItem ) } -pub(super) fn crate_completion_item(name: impl Into) -> CompletionItem { - completion_item_with_sort_text( - simple_completion_item(name, CompletionItemKind::MODULE, None), - crate_or_module_sort_text(), - ) -} - fn func_meta_type_to_string(func_meta: &FuncMeta, has_self_type: bool) -> String { let mut typ = &func_meta.typ; if let Type::Forall(_, typ_) = typ { diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs index e6a732e9142a..1b04e060e2c7 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -6,8 +6,8 @@ mod completion_tests { completion::{ completion_items::{ completion_item_with_sort_text, - completion_item_with_trigger_parameter_hints_command, crate_completion_item, - module_completion_item, simple_completion_item, snippet_completion_item, + completion_item_with_trigger_parameter_hints_command, module_completion_item, + simple_completion_item, snippet_completion_item, }, sort_text::{auto_import_sort_text, self_mismatch_sort_text}, }, @@ -208,7 +208,7 @@ mod completion_tests { let src = r#" use s>|< "#; - assert_completion(src, vec![crate_completion_item("std")]).await; + assert_completion(src, vec![module_completion_item("std")]).await; // "std" doesn't show up anymore because of the "crate::" prefix let src = r#" @@ -284,7 +284,7 @@ mod completion_tests { src, vec![ module_completion_item("something"), - crate_completion_item("std"), + module_completion_item("std"), simple_completion_item("super::", CompletionItemKind::KEYWORD, None), ], ) @@ -313,7 +313,7 @@ mod completion_tests { mod something_else {} use crate::s>|< } - + "#; assert_completion(src, vec![module_completion_item("something")]).await; } @@ -325,7 +325,7 @@ mod completion_tests { mod something_else {} use crate::something::s>|< } - + "#; assert_completion(src, vec![module_completion_item("something_else")]).await; } @@ -1952,4 +1952,40 @@ mod completion_tests { ) .await; } + + #[test] + async fn test_suggests_function_attribute_no_arguments() { + let src = r#" + #[some>|<] + fn foo() {} + + fn some_attr(f: FunctionDefinition) {} + "#; + + assert_completion_excluding_auto_import( + src, + vec![function_completion_item("some_attr", "some_attr", "fn(FunctionDefinition)")], + ) + .await; + } + + #[test] + async fn test_suggests_trait_attribute() { + let src = r#" + #[some>|<] + trait SomeTrait {} + + fn some_attr(t: TraitDefinition, x: Field) {} + "#; + + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "some_attr(…)", + "some_attr(${1:x})", + "fn(TraitDefinition, Field)", + )], + ) + .await; + } } 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 e06a34516816..7ab0a45ad0a5 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -10,7 +10,7 @@ use noirc_errors::Span; use noirc_frontend::{ ast::{ Expression, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, - NoirTraitImpl, TypeImpl, UnresolvedType, UnresolvedTypeData, Visitor, + NoirTraitImpl, TypeImpl, UnresolvedType, Visitor, }, parser::ParsedSubModule, ParsedModule, @@ -171,7 +171,9 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { }; let mut children = Vec::new(); - for (field_name, typ) in &noir_struct.fields { + for field in &noir_struct.fields { + let field_name = &field.item.name; + let typ = &field.item.typ; let span = Span::from(field_name.span().start()..typ.span.end()); let Some(field_location) = self.to_lsp_location(span) else { @@ -223,7 +225,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { self.symbols = Vec::new(); for item in &noir_trait.items { - item.accept(self); + item.item.accept(self); } let children = std::mem::take(&mut self.symbols); @@ -350,7 +352,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { self.symbols = Vec::new(); for trait_impl_item in &noir_trait_impl.items { - trait_impl_item.accept(self); + trait_impl_item.item.accept(self); } let children = std::mem::take(&mut self.symbols); @@ -391,13 +393,9 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { return false; }; - let UnresolvedTypeData::Named(name_path, ..) = &type_impl.object_type.typ else { - return false; - }; - - let name = name_path.last_ident(); + let name = type_impl.object_type.typ.to_string(); - let Some(name_location) = self.to_lsp_location(name.span()) else { + let Some(name_location) = self.to_lsp_location(type_impl.object_type.span) else { return false; }; @@ -405,7 +403,7 @@ impl<'a> Visitor for DocumentSymbolCollector<'a> { self.symbols = Vec::new(); for (noir_function, noir_function_span) in &type_impl.methods { - noir_function.accept(*noir_function_span, self); + noir_function.item.accept(*noir_function_span, self); } let children = std::mem::take(&mut self.symbols); @@ -710,6 +708,23 @@ mod document_symbol_tests { } ]), }, + #[allow(deprecated)] + DocumentSymbol { + name: "i32".to_string(), + detail: None, + kind: SymbolKind::NAMESPACE, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 27, character: 0 }, + end: Position { line: 27, character: 11 } + }, + selection_range: Range { + start: Position { line: 27, character: 5 }, + end: Position { line: 27, character: 8 } + }, + children: Some(Vec::new()) + } ] ); } diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index ae1e57f5ecc9..dab6ddd0fc6b 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -5,7 +5,6 @@ use fm::FileMap; use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; use noirc_frontend::{ ast::Visibility, - graph::CrateId, hir::def_map::ModuleId, hir_def::{stmt::HirPattern, traits::Trait}, macros_api::{NodeInterner, StructId}, @@ -60,30 +59,37 @@ fn format_reference(reference: ReferenceId, args: &ProcessRequestCallbackArgs) - fn format_module(id: ModuleId, args: &ProcessRequestCallbackArgs) -> Option { let crate_root = args.def_maps[&id.krate].root(); + let mut string = String::new(); + if id.local_id == crate_root { - let dep = args.dependencies.iter().find(|dep| dep.crate_id == id.krate); - return dep.map(|dep| format!(" crate {}", dep.name)); + let Some(dep) = args.dependencies.iter().find(|dep| dep.crate_id == id.krate) else { + return None; + }; + string.push_str(" crate "); + string.push_str(&dep.name.to_string()); + } else { + // Note: it's not clear why `try_module_attributes` might return None here, but it happens. + // This is a workaround to avoid panicking in that case (which brings the LSP server down). + // Cases where this happens are related to generated code, so once that stops happening + // this won't be an issue anymore. + let module_attributes = args.interner.try_module_attributes(&id)?; + + if let Some(parent_local_id) = module_attributes.parent { + if format_parent_module_from_module_id( + &ModuleId { krate: id.krate, local_id: parent_local_id }, + args, + &mut string, + ) { + string.push('\n'); + } + } + string.push_str(" "); + string.push_str("mod "); + string.push_str(&module_attributes.name); } - // Note: it's not clear why `try_module_attributes` might return None here, but it happens. - // This is a workaround to avoid panicking in that case (which brings the LSP server down). - // Cases where this happens are related to generated code, so once that stops happening - // this won't be an issue anymore. - let module_attributes = args.interner.try_module_attributes(&id)?; + append_doc_comments(args.interner, ReferenceId::Module(id), &mut string); - let mut string = String::new(); - if let Some(parent_local_id) = module_attributes.parent { - if format_parent_module_from_module_id( - &ModuleId { krate: id.krate, local_id: parent_local_id }, - args, - &mut string, - ) { - string.push('\n'); - } - } - string.push_str(" "); - string.push_str("mod "); - string.push_str(&module_attributes.name); Some(string) } @@ -108,6 +114,9 @@ fn format_struct(id: StructId, args: &ProcessRequestCallbackArgs) -> String { string.push_str(",\n"); } string.push_str(" }"); + + append_doc_comments(args.interner, ReferenceId::Struct(id), &mut string); + string } @@ -131,6 +140,9 @@ fn format_struct_member( string.push_str(": "); string.push_str(&format!("{}", field_type)); string.push_str(&go_to_type_links(field_type, args.interner, args.files)); + + append_doc_comments(args.interner, ReferenceId::StructMember(id, field_index), &mut string); + string } @@ -145,6 +157,9 @@ fn format_trait(id: TraitId, args: &ProcessRequestCallbackArgs) -> String { string.push_str("trait "); string.push_str(&a_trait.name.0.contents); format_generics(&a_trait.generics, &mut string); + + append_doc_comments(args.interner, ReferenceId::Trait(id), &mut string); + string } @@ -163,6 +178,9 @@ fn format_global(id: GlobalId, args: &ProcessRequestCallbackArgs) -> String { string.push_str(": "); string.push_str(&format!("{}", typ)); string.push_str(&go_to_type_links(&typ, args.interner, args.files)); + + append_doc_comments(args.interner, ReferenceId::Global(id), &mut string); + string } @@ -220,6 +238,8 @@ fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { string.push_str(&go_to_type_links(return_type, args.interner, args.files)); + append_doc_comments(args.interner, ReferenceId::Function(id), &mut string); + string } @@ -235,6 +255,9 @@ fn format_alias(id: TypeAliasId, args: &ProcessRequestCallbackArgs) -> String { string.push_str(&type_alias.name.0.contents); string.push_str(" = "); string.push_str(&format!("{}", &type_alias.typ)); + + append_doc_comments(args.interner, ReferenceId::Alias(id), &mut string); + string } @@ -270,6 +293,7 @@ fn format_local(id: DefinitionId, args: &ProcessRequestCallbackArgs) -> String { string } +/// Some doc comments fn format_generics(generics: &Generics, string: &mut String) { if generics.is_empty() { return; @@ -351,9 +375,9 @@ fn format_parent_module_from_module_id( } } - // 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(_) = module.krate { + if module.krate.is_root() { segments.push(&args.crate_name); }; @@ -513,6 +537,16 @@ fn format_link(name: String, location: lsp_types::Location) -> String { ) } +fn append_doc_comments(interner: &NodeInterner, id: ReferenceId, string: &mut String) { + if let Some(doc_comments) = interner.doc_comments(id) { + string.push_str("\n\n---\n\n"); + for comment in doc_comments { + string.push_str(comment); + string.push('\n'); + } + } +} + #[cfg(test)] mod hover_tests { use crate::test_utils; diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index af58396550da..fea758e0e525 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -15,7 +15,7 @@ use lsp_types::{ WorkDoneProgressOptions, }; use nargo_fmt::Config; -use noirc_driver::file_manager_with_stdlib; + use noirc_frontend::graph::CrateId; use noirc_frontend::hir::def_map::CrateDefMap; use noirc_frontend::{graph::Dependency, macros_api::NodeInterner}; @@ -432,7 +432,7 @@ where ResponseError::new(ErrorCode::REQUEST_FAILED, "Could not find package for file") })?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager( state, &workspace, diff --git a/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs b/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs index d3b7743557a6..a7362300adc7 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs @@ -9,9 +9,7 @@ use async_lsp::{ErrorCode, ResponseError}; use nargo::ops::report_errors; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_artifacts::debug::DebugArtifact; -use noirc_driver::{ - file_manager_with_stdlib, CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING, -}; +use noirc_driver::{CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::{debug_info::OpCodesCount, Location}; use crate::{ @@ -53,7 +51,7 @@ fn on_profile_run_request_inner( ResponseError::new(ErrorCode::REQUEST_FAILED, err) })?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager( state, &workspace, diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index 5f4f9cd98d09..081eb815c8b7 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -4,9 +4,7 @@ use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, ResponseError}; use nargo::ops::{run_test, TestStatus}; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{ - check_crate, file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, -}; +use noirc_driver::{check_crate, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::hir::FunctionNameMatch; use crate::{ @@ -48,7 +46,7 @@ fn on_test_run_request_inner( ResponseError::new(ErrorCode::REQUEST_FAILED, err) })?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager( state, &workspace, diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index 7203aca7f090..81910bebedba 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -4,7 +4,7 @@ use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use lsp_types::{LogMessageParams, MessageType}; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{check_crate, file_manager_with_stdlib, NOIR_ARTIFACT_VERSION_STRING}; +use noirc_driver::{check_crate, NOIR_ARTIFACT_VERSION_STRING}; use crate::{ get_package_tests_in_crate, parse_diff, @@ -50,7 +50,7 @@ fn on_tests_request_inner( ResponseError::new(ErrorCode::REQUEST_FAILED, err) })?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager( state, &workspace, diff --git a/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr index 39b2c7fff122..a88a6a21e1f1 100644 --- a/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr +++ b/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr @@ -24,3 +24,5 @@ impl SomeTrait for SomeStruct { mod submodule { global SOME_GLOBAL = 1; } + +impl i32 {} diff --git a/noir/noir-repo/tooling/nargo/src/workspace.rs b/noir/noir-repo/tooling/nargo/src/workspace.rs index 0795ffd93041..810a9edb7e16 100644 --- a/noir/noir-repo/tooling/nargo/src/workspace.rs +++ b/noir/noir-repo/tooling/nargo/src/workspace.rs @@ -9,6 +9,9 @@ use std::{ slice, }; +use fm::FileManager; +use noirc_driver::file_manager_with_stdlib; + use crate::{ constants::{CONTRACT_DIR, EXPORT_DIR, PROOFS_DIR, TARGET_DIR}, package::Package, @@ -46,6 +49,21 @@ impl Workspace { pub fn export_directory_path(&self) -> PathBuf { self.root_dir.join(EXPORT_DIR) } + + /// Returns a new `FileManager` for the root directory of this workspace. + /// If the root directory is not the standard library, the standard library + /// is added to the returned `FileManager`. + pub fn new_file_manager(&self) -> FileManager { + if self.is_stdlib() { + FileManager::new(&self.root_dir) + } else { + file_manager_with_stdlib(&self.root_dir) + } + } + + fn is_stdlib(&self) -> bool { + self.members.len() == 1 && self.members[0].name.to_string() == "std" + } } pub enum IntoIter<'a, T> { 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 5239070b4d20..65a09dcdd115 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,8 +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, - NOIR_ARTIFACT_VERSION_STRING, + check_crate, compute_function_abi, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::{ graph::{CrateId, CrateName}, @@ -52,7 +51,7 @@ pub(crate) fn run(args: CheckCommand, config: NargoConfig) -> Result<(), CliErro Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), )?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); 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 85faf574a0a0..ae96f6436e2a 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 @@ -9,8 +9,8 @@ use nargo::package::Package; 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::DEFAULT_EXPRESSION_WIDTH; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; -use noirc_driver::{file_manager_with_stdlib, DEFAULT_EXPRESSION_WIDTH}; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract}; use noirc_frontend::graph::CrateName; @@ -114,7 +114,7 @@ pub(super) fn compile_workspace_full( workspace: &Workspace, compile_options: &CompileOptions, ) -> Result<(), CliError> { - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); 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 19add7f30dc8..c3752db7fbda 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,8 +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, - NOIR_ARTIFACT_VERSION_STRING, + compile_no_check, CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::graph::CrateName; @@ -54,7 +53,7 @@ pub(crate) fn run(args: ExportCommand, config: NargoConfig) -> Result<(), CliErr Some(NOIR_ARTIFACT_VERSION_STRING.to_owned()), )?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs index 8f66a0a328f2..66496db517c8 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -3,7 +3,7 @@ use std::{fs::DirEntry, path::Path}; use clap::Args; use nargo::{insert_all_files_for_workspace_into_file_manager, ops::report_errors}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{file_manager_with_stdlib, NOIR_ARTIFACT_VERSION_STRING}; +use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; use noirc_errors::CustomDiagnostic; use noirc_frontend::{hir::def_map::parse_file, parser::ParserError}; @@ -29,7 +29,7 @@ pub(crate) fn run(args: FormatCommand, config: NargoConfig) -> Result<(), CliErr Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), )?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let config = nargo_fmt::Config::read(&config.program_dir) 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 2f9390d72e02..8b3c0e29c7d4 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 @@ -9,9 +9,7 @@ use nargo::{ prepare_package, }; 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, -}; +use noirc_driver::{check_crate, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::{ graph::CrateName, hir::{FunctionNameMatch, ParsedFiles}, @@ -65,7 +63,7 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), )?; - let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); + let mut workspace_file_manager = workspace.new_file_manager(); insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs index 9e556e0fcbec..ba9a8214702e 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs @@ -151,7 +151,7 @@ impl super::FmtVisitor<'_> { } fn visit_module(&mut self, module: ParsedModule) { - for Item { kind, span } in module.items { + for Item { kind, span, doc_comments } in module.items { match kind { ItemKind::Function(func) => { self.visit_function(span, func); @@ -165,6 +165,11 @@ impl super::FmtVisitor<'_> { continue; } + for doc_comment in doc_comments { + self.push_str(&format!("///{doc_comment}\n")); + self.push_str(&self.indent.to_string()); + } + for attribute in module.outer_attributes { self.push_str(&format!("#[{}]\n", attribute.as_ref())); self.push_str(&self.indent.to_string()); @@ -214,7 +219,7 @@ impl super::FmtVisitor<'_> { self.indent.block_indent(self.config); for (method, span) in impl_.methods { - self.visit_function(span, method); + self.visit_function(span, method.item); } self.close_block((self.last_position..span.end() - 1).into());