diff --git a/.noir-sync-commit b/.noir-sync-commit index cfaf9d81cf2..9326943a310 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -b84009ca428a5790acf53a6c027146b706170574 +d6f60d70dc41640ad84f7a968927b20818bcaf2a diff --git a/noir/noir-repo/.gitattributes b/noir/noir-repo/.gitattributes deleted file mode 100644 index 204cff5c097..00000000000 --- a/noir/noir-repo/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.nr linguist-language=rust diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 3f56f5b6965..796ac50ca9f 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 2a06e07f092..5a15586bf1b 100644 --- a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs @@ -1584,7 +1584,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/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index c07d2d8a4c1..16fb26e55db 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index ec1d395725d..4ba8951c2f9 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 8983266dab9..4d5dcc6f1af 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 7a8a7187857..e2de30d6d93 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 ede8a350bf2..d753bb43471 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 df237926486..6e95efa637c 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 7dd21f1a8ac..a6bf2e14fb3 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 316aa60da62..b68946ec020 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/hir_utils.rs b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs index 0a8ce371708..200ce3099cb 100644 --- a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs @@ -195,7 +195,7 @@ pub fn inject_fn( let trait_id = None; items.functions.push(UnresolvedFunctions { file_id, functions, trait_id, self_type: None }); - let mut errors = Elaborator::elaborate(context, *crate_id, items, None, false); + let mut errors = Elaborator::elaborate(context, *crate_id, items, None); errors.retain(|(error, _)| !CustomDiagnostic::from(error).is_warning()); if !errors.is_empty() { @@ -241,7 +241,7 @@ pub fn inject_global( let mut items = CollectedItems::default(); items.globals.push(UnresolvedGlobal { file_id, module_id, global_id, stmt_def: global }); - let _errors = Elaborator::elaborate(context, *crate_id, items, None, false); + let _errors = Elaborator::elaborate(context, *crate_id, items, None); } pub fn fully_qualified_note_path(context: &HirContext, note_id: StructId) -> Option { 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 e7b3e347a96..dce3af1402b 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 a315e7ed397..18a13517b75 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -120,10 +120,6 @@ pub struct CompileOptions { #[arg(long, hide = true)] pub show_artifact_paths: bool, - /// Temporary flag to enable the experimental arithmetic generics feature - #[arg(long, hide = true)] - pub arithmetic_generics: bool, - /// Flag to turn off the compiler check for under constrained values. /// Warning: This can improve compilation speed but can also lead to correctness errors. /// This check should always be run on production code. @@ -216,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) { @@ -289,7 +287,6 @@ pub fn check_crate( crate_id, context, options.debug_comptime_in_file.as_deref(), - options.arithmetic_generics, error_on_unused_imports, macros, ); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 89445c4d195..8c128c452d0 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -181,7 +181,12 @@ impl<'f> PerFunctionContext<'f> { self.last_loads.get(store_address).is_none() }; - if remove_load && !is_reference_param { + let is_reference_alias = block + .expressions + .get(store_address) + .map_or(false, |expression| matches!(expression, Expression::Dereference(_))); + + if remove_load && !is_reference_param && !is_reference_alias { self.instructions_to_remove.insert(*store_instruction); } } @@ -286,19 +291,19 @@ impl<'f> PerFunctionContext<'f> { } else { references.mark_value_used(address, self.inserter.function); - let expression = if let Some(expression) = references.expressions.get(&result) { - expression.clone() - } else { - references.expressions.insert(result, Expression::Other(result)); - Expression::Other(result) - }; - if let Some(aliases) = references.aliases.get_mut(&expression) { + let expression = + references.expressions.entry(result).or_insert(Expression::Other(result)); + // Make sure this load result is marked an alias to itself + if let Some(aliases) = references.aliases.get_mut(expression) { + // If we have an alias set, add to the set aliases.insert(result); } else { + // Otherwise, create a new alias set containing just the load result references .aliases .insert(Expression::Other(result), AliasSet::known(result)); } + // Mark that we know a load result is equivalent to the address of a load. references.set_known_value(result, address); self.last_loads.insert(address, (instruction, block_id)); @@ -789,4 +794,98 @@ mod tests { // We expect the last eq to be optimized out assert_eq!(b1_instructions.len(), 0); } + + #[test] + fn keep_store_to_alias_in_loop_block() { + // This test makes sure the instruction `store Field 2 at v5` in b2 remains after mem2reg. + // Although the only instruction on v5 is a lone store without any loads, + // v5 is an alias of the reference v0 which is stored in v2. + // This test makes sure that we are not inadvertently removing stores to aliases across blocks. + // + // acir(inline) fn main f0 { + // b0(): + // v0 = allocate + // store Field 0 at v0 + // v2 = allocate + // store v0 at v2 + // jmp b1(Field 0) + // b1(v3: Field): + // v4 = eq v3, Field 0 + // jmpif v4 then: b2, else: b3 + // b2(): + // v5 = load v2 + // store Field 2 at v5 + // v8 = add v3, Field 1 + // jmp b1(v8) + // b3(): + // v9 = load v0 + // v10 = eq v9, Field 2 + // constrain v9 == Field 2 + // v11 = load v2 + // v12 = load v10 + // v13 = eq v12, Field 2 + // constrain v11 == Field 2 + // return + // } + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), main_id); + + let v0 = builder.insert_allocate(Type::field()); + let zero = builder.numeric_constant(0u128, Type::field()); + builder.insert_store(v0, zero); + + let v2 = builder.insert_allocate(Type::field()); + // Construct alias + builder.insert_store(v2, v0); + let v2_type = builder.current_function.dfg.type_of_value(v2); + assert!(builder.current_function.dfg.value_is_reference(v2)); + + let b1 = builder.insert_block(); + builder.terminate_with_jmp(b1, vec![zero]); + + // Loop header + builder.switch_to_block(b1); + let v3 = builder.add_block_parameter(b1, Type::field()); + let is_zero = builder.insert_binary(v3, BinaryOp::Eq, zero); + + let b2 = builder.insert_block(); + let b3 = builder.insert_block(); + builder.terminate_with_jmpif(is_zero, b2, b3); + + // Loop body + builder.switch_to_block(b2); + let v5 = builder.insert_load(v2, v2_type.clone()); + let two = builder.numeric_constant(2u128, Type::field()); + builder.insert_store(v5, two); + let one = builder.numeric_constant(1u128, Type::field()); + let v3_plus_one = builder.insert_binary(v3, BinaryOp::Add, one); + builder.terminate_with_jmp(b1, vec![v3_plus_one]); + + builder.switch_to_block(b3); + let v9 = builder.insert_load(v0, Type::field()); + let _ = builder.insert_binary(v9, BinaryOp::Eq, two); + + builder.insert_constrain(v9, two, None); + let v11 = builder.insert_load(v2, v2_type); + let v12 = builder.insert_load(v11, Type::field()); + let _ = builder.insert_binary(v12, BinaryOp::Eq, two); + + builder.insert_constrain(v11, two, None); + builder.terminate_with_return(vec![]); + + let ssa = builder.finish(); + + // We expect the same result as above. + let ssa = ssa.mem2reg(); + + let main = ssa.main(); + assert_eq!(main.reachable_blocks().len(), 4); + + // The store from the original SSA should remain + assert_eq!(count_stores(main.entry_block(), &main.dfg), 2); + assert_eq!(count_stores(b2, &main.dfg), 1); + + assert_eq!(count_loads(b2, &main.dfg), 1); + assert_eq!(count_loads(b3, &main.dfg), 3); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index c0f6c8965fb..510cff08dec 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 eb896a377ae..00000000000 --- 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 00000000000..f00f15c215d --- /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 3fd63249201..12a8aec05eb 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; @@ -12,11 +13,13 @@ mod traits; mod type_alias; mod visitor; +pub use visitor::AttributeTarget; 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 30db8ad63fd..52c39a49e8a 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 732cbee9232..cd42abb29c7 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 e3221f287d3..0463a5b8392 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 0aeeed39dd0..fcb4e4c5dd1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/visitor.rs @@ -12,11 +12,11 @@ use crate::{ UseTreeKind, }, node_interner::{ - ExprId, InternedExpressionKind, InternedStatementKind, InternedUnresolvedTypeData, - QuotedTypeId, + ExprId, InternedExpressionKind, InternedPattern, InternedStatementKind, + InternedUnresolvedTypeData, QuotedTypeId, }, parser::{Item, ItemKind, ParsedSubModule}, - token::{SecondaryAttribute, Tokens}, + token::{CustomAtrribute, SecondaryAttribute, Tokens}, ParsedModule, QuotedType, }; @@ -26,6 +26,14 @@ use super::{ UnresolvedTypeExpression, }; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum AttributeTarget { + Module, + Struct, + Trait, + Function, +} + /// Implements the [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for Noir's AST. /// /// In this implementation, methods must return a bool: @@ -433,7 +441,17 @@ pub trait Visitor { true } - fn visit_secondary_attribute(&mut self, _: &SecondaryAttribute, _: Span) {} + fn visit_interned_pattern(&mut self, _: &InternedPattern, _: Span) {} + + fn visit_secondary_attribute( + &mut self, + _: &SecondaryAttribute, + _target: AttributeTarget, + ) -> bool { + true + } + + fn visit_custom_attribute(&mut self, _: &CustomAtrribute, _target: AttributeTarget) {} } impl ParsedModule { @@ -484,7 +502,7 @@ impl Item { module_declaration.accept(self.span, visitor); } ItemKind::InnerAttribute(attribute) => { - attribute.accept(self.span, visitor); + attribute.accept(AttributeTarget::Module, visitor); } } } @@ -492,6 +510,10 @@ impl Item { impl ParsedSubModule { pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + for attribute in &self.outer_attributes { + attribute.accept(AttributeTarget::Module, visitor); + } + if visitor.visit_parsed_submodule(self, span) { self.accept_children(visitor); } @@ -510,6 +532,10 @@ impl NoirFunction { } pub fn accept_children(&self, visitor: &mut impl Visitor) { + for attribute in self.secondary_attributes() { + attribute.accept(AttributeTarget::Function, visitor); + } + for param in &self.def.parameters { param.typ.accept(visitor); } @@ -530,7 +556,7 @@ impl NoirTraitImpl { self.object_type.accept(visitor); for item in &self.items { - item.accept(visitor); + item.item.accept(visitor); } } } @@ -579,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); } } } @@ -592,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); } } } @@ -674,8 +704,12 @@ impl NoirStruct { } pub fn accept_children(&self, visitor: &mut impl Visitor) { - for (_name, unresolved_type) in &self.fields { - unresolved_type.accept(visitor); + for attribute in &self.attributes { + attribute.accept(AttributeTarget::Struct, visitor); + } + + for field in &self.fields { + field.item.typ.accept(visitor); } } } @@ -694,6 +728,10 @@ impl NoirTypeAlias { impl ModuleDeclaration { pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { + for attribute in &self.outer_attributes { + attribute.accept(AttributeTarget::Module, visitor); + } + visitor.visit_module_declaration(self, span); } } @@ -1290,13 +1328,30 @@ impl Pattern { } } } + Pattern::Interned(id, span) => { + visitor.visit_interned_pattern(id, *span); + } } } } impl SecondaryAttribute { - pub fn accept(&self, span: Span, visitor: &mut impl Visitor) { - visitor.visit_secondary_attribute(self, span); + pub fn accept(&self, target: AttributeTarget, visitor: &mut impl Visitor) { + if visitor.visit_secondary_attribute(self, target) { + self.accept_children(target, visitor); + } + } + + pub fn accept_children(&self, target: AttributeTarget, visitor: &mut impl Visitor) { + if let SecondaryAttribute::Custom(custom) = self { + custom.accept(target, visitor); + } + } +} + +impl CustomAtrribute { + pub fn accept(&self, target: AttributeTarget, visitor: &mut impl Visitor) { + visitor.visit_custom_attribute(self, target); } } 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 951c5220707..ed9265536f9 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 7da5efd0b5a..c2347adcbee 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::{ @@ -15,7 +16,7 @@ use crate::{ }, dc_mod, }, - def_map::ModuleId, + def_map::{LocalModuleId, ModuleId}, resolution::errors::ResolverError, }, hir_def::expr::HirIdent, @@ -24,20 +25,69 @@ use crate::{ Expression, ExpressionKind, HirExpression, NodeInterner, SecondaryAttribute, StructId, }, node_interner::{DefinitionKind, DependencyId, FuncId, TraitId}, - parser::{self, TopLevelStatement}, + parser::{self, TopLevelStatement, TopLevelStatementKind}, Type, TypeBindings, UnificationError, }; use super::{Elaborator, FunctionContext, ResolverMeta}; +#[derive(Debug, Copy, Clone)] +struct AttributeContext { + // The file where generated items should be added + file: FileId, + // The module where generated items should be added + module: LocalModuleId, + // The file where the attribute is located + attribute_file: FileId, + // The module where the attribute is located + attribute_module: LocalModuleId, +} + +impl AttributeContext { + fn new(file: FileId, module: LocalModuleId) -> Self { + Self { file, module, attribute_file: file, attribute_module: module } + } +} + impl<'context> Elaborator<'context> { /// Elaborate an expression from the middle of a comptime scope. /// When this happens we require additional information to know /// what variables should be in scope. - pub fn elaborate_item_from_comptime<'a, T>( + pub fn elaborate_item_from_comptime_in_function<'a, T>( &'a mut self, current_function: Option, f: impl FnOnce(&mut Elaborator<'a>) -> T, + ) -> T { + self.elaborate_item_from_comptime(f, |elaborator| { + if let Some(function) = current_function { + let meta = elaborator.interner.function_meta(&function); + elaborator.current_item = Some(DependencyId::Function(function)); + elaborator.crate_id = meta.source_crate; + elaborator.local_module = meta.source_module; + elaborator.file = meta.source_file; + elaborator.introduce_generics_into_scope(meta.all_generics.clone()); + } + }) + } + + pub fn elaborate_item_from_comptime_in_module<'a, T>( + &'a mut self, + module: ModuleId, + file: FileId, + f: impl FnOnce(&mut Elaborator<'a>) -> T, + ) -> T { + self.elaborate_item_from_comptime(f, |elaborator| { + elaborator.current_item = None; + elaborator.crate_id = module.krate; + elaborator.local_module = module.local_id; + elaborator.file = file; + }) + } + + fn elaborate_item_from_comptime<'a, T>( + &'a mut self, + f: impl FnOnce(&mut Elaborator<'a>) -> T, + setup: impl FnOnce(&mut Elaborator<'a>), ) -> T { // Create a fresh elaborator to ensure no state is changed from // this elaborator @@ -46,21 +96,13 @@ impl<'context> Elaborator<'context> { self.def_maps, self.crate_id, self.debug_comptime_in_file, - self.enable_arithmetic_generics, self.interpreter_call_stack.clone(), ); elaborator.function_context.push(FunctionContext::default()); elaborator.scopes.start_function(); - if let Some(function) = current_function { - let meta = elaborator.interner.function_meta(&function); - elaborator.current_item = Some(DependencyId::Function(function)); - elaborator.crate_id = meta.source_crate; - elaborator.local_module = meta.source_module; - elaborator.file = meta.source_file; - elaborator.introduce_generics_into_scope(meta.all_generics.clone()); - } + setup(&mut elaborator); elaborator.populate_scope_from_comptime_scopes(); @@ -89,15 +131,22 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn run_comptime_attributes_on_item( + fn run_comptime_attributes_on_item( &mut self, attributes: &[SecondaryAttribute], item: Value, span: Span, + attribute_context: AttributeContext, generated_items: &mut CollectedItems, ) { for attribute in attributes { - self.run_comptime_attribute_on_item(attribute, &item, span, generated_items); + self.run_comptime_attribute_on_item( + attribute, + &item, + span, + attribute_context, + generated_items, + ); } } @@ -106,18 +155,22 @@ impl<'context> Elaborator<'context> { attribute: &SecondaryAttribute, item: &Value, span: Span, + attribute_context: AttributeContext, 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, - 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); + } + }); } } @@ -127,8 +180,12 @@ impl<'context> Elaborator<'context> { item: Value, span: Span, attribute_span: Span, + attribute_context: AttributeContext, generated_items: &mut CollectedItems, ) -> Result<(), (CompilationError, FileId)> { + self.file = attribute_context.attribute_file; + self.local_module = attribute_context.attribute_module; + let location = Location::new(attribute_span, self.file); let Some((function, arguments)) = Self::parse_attribute(attribute, location)? else { // Do not issue an error if the attribute is unknown @@ -156,6 +213,9 @@ impl<'context> Elaborator<'context> { return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); }; + self.file = attribute_context.file; + self.local_module = attribute_context.module; + let mut interpreter = self.setup_interpreter(); let mut arguments = Self::handle_attribute_arguments( &mut interpreter, @@ -318,14 +378,14 @@ impl<'context> Elaborator<'context> { } } - fn add_item( + pub(crate) fn add_item( &mut self, item: TopLevelStatement, 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( @@ -334,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)]; @@ -345,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, @@ -375,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, @@ -390,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, @@ -403,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()); } @@ -463,18 +524,28 @@ impl<'context> Elaborator<'context> { let attributes = &trait_.trait_def.attributes; let item = Value::TraitDefinition(*trait_id); let span = trait_.trait_def.span; - self.local_module = trait_.module_id; - self.file = trait_.file_id; - self.run_comptime_attributes_on_item(attributes, item, span, &mut generated_items); + let context = AttributeContext::new(trait_.file_id, trait_.module_id); + self.run_comptime_attributes_on_item( + attributes, + item, + span, + context, + &mut generated_items, + ); } for (struct_id, struct_def) in types { let attributes = &struct_def.struct_def.attributes; let item = Value::StructDefinition(*struct_id); let span = struct_def.struct_def.span; - self.local_module = struct_def.module_id; - self.file = struct_def.file_id; - self.run_comptime_attributes_on_item(attributes, item, span, &mut generated_items); + let context = AttributeContext::new(struct_def.file_id, struct_def.module_id); + self.run_comptime_attributes_on_item( + attributes, + item, + span, + context, + &mut generated_items, + ); } self.run_attributes_on_functions(functions, &mut generated_items); @@ -496,10 +567,14 @@ impl<'context> Elaborator<'context> { let attribute = &module_attribute.attribute; let span = Span::default(); - self.local_module = module_attribute.attribute_module_id; - self.file = module_attribute.attribute_file_id; + let context = AttributeContext { + file: module_attribute.file_id, + module: module_attribute.module_id, + attribute_file: module_attribute.attribute_file_id, + attribute_module: module_attribute.attribute_module_id, + }; - self.run_comptime_attribute_on_item(attribute, &item, span, generated_items); + self.run_comptime_attribute_on_item(attribute, &item, span, context, generated_items); } } @@ -509,16 +584,51 @@ impl<'context> Elaborator<'context> { generated_items: &mut CollectedItems, ) { for function_set in function_sets { - self.file = function_set.file_id; self.self_type = function_set.self_type.clone(); for (local_module, function_id, function) in &function_set.functions { - self.local_module = *local_module; + let context = AttributeContext::new(function_set.file_id, *local_module); let attributes = function.secondary_attributes(); let item = Value::FunctionDefinition(*function_id); let span = function.span(); - self.run_comptime_attributes_on_item(attributes, item, span, generated_items); + self.run_comptime_attributes_on_item( + attributes, + item, + span, + context, + generated_items, + ); } } } + + /// 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 beede7a3a30..15d6ed5506b 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/lints.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs index 78df10fa94c..8253921d305 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/lints.rs @@ -1,20 +1,19 @@ use crate::{ - ast::FunctionKind, + ast::{FunctionKind, Ident}, graph::CrateId, hir::{ resolution::errors::{PubPosition, ResolverError}, type_check::TypeCheckError, }, - hir_def::expr::HirIdent, + hir_def::{expr::HirIdent, function::FuncMeta}, macros_api::{ - HirExpression, HirLiteral, NodeInterner, NoirFunction, Signedness, UnaryOp, - UnresolvedTypeData, Visibility, + HirExpression, HirLiteral, NodeInterner, NoirFunction, Signedness, UnaryOp, Visibility, }, - node_interner::{DefinitionKind, ExprId, FuncId}, + node_interner::{DefinitionKind, ExprId, FuncId, FunctionModifiers}, Type, }; -use noirc_errors::Span; +use noirc_errors::{Span, Spanned}; pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Option { let HirExpression::Ident(HirIdent { location, id, impl_kind: _ }, _) = @@ -39,16 +38,17 @@ pub(super) fn deprecated_function(interner: &NodeInterner, expr: ExprId) -> Opti /// Inline attributes are only relevant for constrained functions /// as all unconstrained functions are not inlined and so /// associated attributes are disallowed. -pub(super) fn inlining_attributes(func: &NoirFunction) -> Option { - if func.def.is_unconstrained { - let attributes = func.attributes().clone(); - - if attributes.is_no_predicates() { - Some(ResolverError::NoPredicatesAttributeOnUnconstrained { - ident: func.name_ident().clone(), - }) - } else if attributes.is_foldable() { - Some(ResolverError::FoldAttributeOnUnconstrained { ident: func.name_ident().clone() }) +pub(super) fn inlining_attributes( + func: &FuncMeta, + modifiers: &FunctionModifiers, +) -> Option { + if modifiers.is_unconstrained { + if modifiers.attributes.is_no_predicates() { + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::NoPredicatesAttributeOnUnconstrained { ident }) + } else if modifiers.attributes.is_foldable() { + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::FoldAttributeOnUnconstrained { ident }) } else { None } @@ -59,24 +59,30 @@ pub(super) fn inlining_attributes(func: &NoirFunction) -> Option /// Attempting to define new low level (`#[builtin]` or `#[foreign]`) functions outside of the stdlib is disallowed. pub(super) fn low_level_function_outside_stdlib( - func: &NoirFunction, + func: &FuncMeta, + modifiers: &FunctionModifiers, crate_id: CrateId, ) -> Option { let is_low_level_function = - func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); + modifiers.attributes.function.as_ref().map_or(false, |func| func.is_low_level()); if !crate_id.is_stdlib() && is_low_level_function { - Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }) + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident }) } else { None } } /// Oracle definitions (functions with the `#[oracle]` attribute) must be marked as unconstrained. -pub(super) fn oracle_not_marked_unconstrained(func: &NoirFunction) -> Option { +pub(super) fn oracle_not_marked_unconstrained( + func: &FuncMeta, + modifiers: &FunctionModifiers, +) -> Option { let is_oracle_function = - func.attributes().function.as_ref().map_or(false, |func| func.is_oracle()); - if is_oracle_function && !func.def.is_unconstrained { - Some(ResolverError::OracleMarkedAsConstrained { ident: func.name_ident().clone() }) + modifiers.attributes.function.as_ref().map_or(false, |func| func.is_oracle()); + if is_oracle_function && !modifiers.is_unconstrained { + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::OracleMarkedAsConstrained { ident }) } else { None } @@ -106,12 +112,13 @@ pub(super) fn oracle_called_from_constrained_function( } /// `pub` is required on return types for entry point functions -pub(super) fn missing_pub(func: &NoirFunction, is_entry_point: bool) -> Option { - if is_entry_point - && func.return_type().typ != UnresolvedTypeData::Unit - && func.def.return_visibility == Visibility::Private +pub(super) fn missing_pub(func: &FuncMeta, modifiers: &FunctionModifiers) -> Option { + if func.is_entry_point + && func.return_type() != &Type::Unit + && func.return_visibility == Visibility::Private { - Some(ResolverError::NecessaryPub { ident: func.name_ident().clone() }) + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::NecessaryPub { ident }) } else { None } @@ -119,11 +126,12 @@ pub(super) fn missing_pub(func: &NoirFunction, is_entry_point: bool) -> Option Option { - if !is_entry_point && func.kind == FunctionKind::Recursive { - Some(ResolverError::MisplacedRecursiveAttribute { ident: func.name_ident().clone() }) + if !func.is_entry_point && func.kind == FunctionKind::Recursive { + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::MisplacedRecursiveAttribute { ident }) } else { None } @@ -163,14 +171,13 @@ pub(super) fn unconstrained_function_return( /// /// Application of `pub` to other functions is not meaningful and is a mistake. pub(super) fn unnecessary_pub_return( - func: &NoirFunction, + func: &FuncMeta, + modifiers: &FunctionModifiers, is_entry_point: bool, ) -> Option { - if !is_entry_point && func.def.return_visibility == Visibility::Public { - Some(ResolverError::UnnecessaryPub { - ident: func.name_ident().clone(), - position: PubPosition::ReturnType, - }) + if !is_entry_point && func.return_visibility == Visibility::Public { + let ident = func_meta_name_ident(func, modifiers); + Some(ResolverError::UnnecessaryPub { ident, position: PubPosition::ReturnType }) } else { None } @@ -252,3 +259,7 @@ pub(crate) fn overflowing_int( errors } + +fn func_meta_name_ident(func: &FuncMeta, modifiers: &FunctionModifiers) -> Ident { + Ident(Spanned::from(func.name.location.span, modifiers.name.clone())) +} 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 161742029f6..6871152edb5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -26,7 +26,8 @@ use crate::{ SecondaryAttribute, StructId, }, node_interner::{ - DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId, + DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, ReferenceId, + TraitId, TypeAliasId, }, token::CustomAtrribute, Shared, Type, TypeVariable, @@ -157,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 @@ -167,9 +172,6 @@ pub struct Elaborator<'context> { /// they are elaborated (e.g. in a function's type or another global's RHS). unresolved_globals: BTreeMap, - /// Temporary flag to enable the experimental arithmetic generics feature - enable_arithmetic_generics: bool, - pub(crate) interpreter_call_stack: im::Vector, } @@ -193,7 +195,6 @@ impl<'context> Elaborator<'context> { def_maps: &'context mut DefMaps, crate_id: CrateId, debug_comptime_in_file: Option, - enable_arithmetic_generics: bool, interpreter_call_stack: im::Vector, ) -> Self { Self { @@ -216,9 +217,9 @@ impl<'context> Elaborator<'context> { current_trait_impl: None, debug_comptime_in_file, unresolved_globals: BTreeMap::new(), - enable_arithmetic_generics, current_trait: None, interpreter_call_stack, + in_comptime_context: false, } } @@ -226,14 +227,12 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, debug_comptime_in_file: Option, - enable_arithmetic_generics: bool, ) -> Self { Self::new( &mut context.def_interner, &mut context.def_maps, crate_id, debug_comptime_in_file, - enable_arithmetic_generics, im::Vector::new(), ) } @@ -243,16 +242,8 @@ impl<'context> Elaborator<'context> { crate_id: CrateId, items: CollectedItems, debug_comptime_in_file: Option, - enable_arithmetic_generics: bool, ) -> Vec<(CompilationError, FileId)> { - Self::elaborate_and_return_self( - context, - crate_id, - items, - debug_comptime_in_file, - enable_arithmetic_generics, - ) - .errors + Self::elaborate_and_return_self(context, crate_id, items, debug_comptime_in_file).errors } pub fn elaborate_and_return_self( @@ -260,20 +251,14 @@ impl<'context> Elaborator<'context> { crate_id: CrateId, items: CollectedItems, debug_comptime_in_file: Option, - enable_arithmetic_generics: bool, ) -> Self { - let mut this = Self::from_context( - context, - crate_id, - debug_comptime_in_file, - enable_arithmetic_generics, - ); + let mut this = Self::from_context(context, crate_id, debug_comptime_in_file); this.elaborate_items(items); this.check_and_pop_function_context(); this } - fn elaborate_items(&mut self, mut items: CollectedItems) { + pub(crate) fn elaborate_items(&mut self, mut items: CollectedItems) { // We must first resolve and intern the globals before we can resolve any stmts inside each function. // Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope // @@ -417,6 +402,10 @@ impl<'context> Elaborator<'context> { self.trait_bounds = func_meta.trait_constraints.clone(); self.function_context.push(FunctionContext::default()); + let modifiers = self.interner.function_modifiers(&id).clone(); + + self.run_function_lints(&func_meta, &modifiers); + self.introduce_generics_into_scope(func_meta.all_generics.clone()); // The DefinitionIds for each parameter were already created in define_function_meta @@ -731,20 +720,6 @@ impl<'context> Elaborator<'context> { let is_entry_point = self.is_entry_point_function(func, in_contract); - self.run_lint(|_| lints::inlining_attributes(func).map(Into::into)); - self.run_lint(|_| lints::missing_pub(func, is_entry_point).map(Into::into)); - self.run_lint(|elaborator| { - lints::unnecessary_pub_return(func, elaborator.pub_allowed(func, in_contract)) - .map(Into::into) - }); - self.run_lint(|_| lints::oracle_not_marked_unconstrained(func).map(Into::into)); - self.run_lint(|elaborator| { - lints::low_level_function_outside_stdlib(func, elaborator.crate_id).map(Into::into) - }); - self.run_lint(|_| { - lints::recursive_non_entrypoint_function(func, is_entry_point).map(Into::into) - }); - // Both the #[fold] and #[no_predicates] alter a function's inline type and code generation in similar ways. // In certain cases such as type checking (for which the following flag will be used) both attributes // indicate we should code generate in the same way. Thus, we unify the attributes into one flag here. @@ -858,6 +833,23 @@ impl<'context> Elaborator<'context> { self.current_item = None; } + fn run_function_lints(&mut self, func: &FuncMeta, modifiers: &FunctionModifiers) { + self.run_lint(|_| lints::inlining_attributes(func, modifiers).map(Into::into)); + self.run_lint(|_| lints::missing_pub(func, modifiers).map(Into::into)); + self.run_lint(|_| { + let pub_allowed = func.is_entry_point || modifiers.attributes.is_foldable(); + lints::unnecessary_pub_return(func, modifiers, pub_allowed).map(Into::into) + }); + self.run_lint(|_| lints::oracle_not_marked_unconstrained(func, modifiers).map(Into::into)); + self.run_lint(|elaborator| { + lints::low_level_function_outside_stdlib(func, modifiers, elaborator.crate_id) + .map(Into::into) + }); + self.run_lint(|_| { + lints::recursive_non_entrypoint_function(func, modifiers).map(Into::into) + }); + } + /// Only sized types are valid to be used as main's parameters or the parameters to a contract /// function. If the given type is not sized (e.g. contains a slice or NamedGeneric type), an /// error is issued. @@ -1261,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())) }); @@ -1300,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); @@ -1335,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); } @@ -1434,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 06c153d4c10..f738657fd23 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 d7d330f891a..9e29978a9d5 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 f651630baa2..d6bfd3aa647 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/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index 8dccd5f0344..39ef4e0bb8e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -450,7 +450,6 @@ impl<'context> Elaborator<'context> { } UnresolvedTypeExpression::Constant(int, _) => Type::Constant(int), UnresolvedTypeExpression::BinaryOperation(lhs, op, rhs, span) => { - let (lhs_span, rhs_span) = (lhs.span(), rhs.span()); let lhs = self.convert_expression_type(*lhs); let rhs = self.convert_expression_type(*rhs); @@ -463,15 +462,7 @@ impl<'context> Elaborator<'context> { Type::Error } } - (lhs, rhs) => { - if !self.enable_arithmetic_generics { - let span = - if !matches!(lhs, Type::Constant(_)) { lhs_span } else { rhs_span }; - self.push_err(ResolverError::InvalidArrayLengthExpr { span }); - } - - Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)).canonicalize() - } + (lhs, rhs) => Type::InfixExpr(Box::new(lhs), op, Box::new(rhs)).canonicalize(), } } UnresolvedTypeExpression::AsTraitPath(path) => self.resolve_as_trait_path(*path), 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 452aef74b36..094594a50ac 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 f6585786eeb..9c4761f3156 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 @@ -194,7 +194,6 @@ pub enum InterpreterError { candidates: Vec, location: Location, }, - Unimplemented { item: String, location: Location, @@ -207,6 +206,28 @@ pub enum InterpreterError { index: usize, location: Location, }, + InvalidAttribute { + attribute: String, + location: Location, + }, + GenericNameShouldBeAnIdent { + name: Rc, + location: Location, + }, + DuplicateGeneric { + name: Rc, + struct_name: String, + 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. @@ -275,7 +296,12 @@ impl InterpreterError { | InterpreterError::FunctionAlreadyResolved { location, .. } | InterpreterError::MultipleMatchingImpls { location, .. } | InterpreterError::ExpectedIdentForStructField { location, .. } - | InterpreterError::TypeAnnotationsNeededForMethodCall { location } => *location, + | InterpreterError::InvalidAttribute { location, .. } + | InterpreterError::GenericNameShouldBeAnIdent { location, .. } + | InterpreterError::DuplicateGeneric { duplicate_location: location, .. } + | InterpreterError::TypeAnnotationsNeededForMethodCall { location } + | InterpreterError::CannotResolveExpression { location, .. } + | InterpreterError::CannotSetFunctionBody { location, .. } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) @@ -579,6 +605,45 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let secondary = format!("`{value}` is not a valid field name for `set_fields`"); CustomDiagnostic::simple_error(msg, secondary, location.span) } + InterpreterError::InvalidAttribute { attribute, location } => { + let msg = format!("`{attribute}` is not a valid attribute"); + let secondary = "Note that this method expects attribute contents, without the leading `#[` or trailing `]`".to_string(); + CustomDiagnostic::simple_error(msg, secondary, location.span) + } + InterpreterError::GenericNameShouldBeAnIdent { name, location } => { + let msg = + "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) + } + InterpreterError::DuplicateGeneric { + name, + struct_name, + duplicate_location, + existing_location, + } => { + let msg = format!("`{struct_name}` already has a generic named `{name}`"); + let secondary = format!("`{name}` added here a second time"); + let mut error = + CustomDiagnostic::simple_error(msg, secondary, duplicate_location.span); + + let existing_msg = format!("`{name}` was previously defined here"); + error.add_secondary_with_file( + existing_msg, + existing_location.span, + existing_location.file, + ); + 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.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 9f559b7c5e6..5f58c18d66e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -2,6 +2,7 @@ use std::collections::VecDeque; use std::{collections::hash_map::Entry, rc::Rc}; use acvm::{acir::AcirField, FieldElement}; +use fm::FileId; use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; @@ -10,6 +11,7 @@ use rustc_hash::FxHashMap as HashMap; use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness}; use crate::elaborator::Elaborator; use crate::graph::CrateId; +use crate::hir::def_map::ModuleId; use crate::hir_def::expr::ImplKind; use crate::hir_def::function::FunctionBody; use crate::macros_api::UnaryOp; @@ -170,7 +172,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Some(body) => Ok(body), None => { if matches!(&meta.function_body, FunctionBody::Unresolved(..)) { - self.elaborate_item(None, |elaborator| { + self.elaborate_in_function(None, |elaborator| { elaborator.elaborate_function(function); }); @@ -183,13 +185,25 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } } - fn elaborate_item( + fn elaborate_in_function( &mut self, function: Option, f: impl FnOnce(&mut Elaborator) -> T, ) -> T { self.unbind_generics_from_previous_function(); - let result = self.elaborator.elaborate_item_from_comptime(function, f); + let result = self.elaborator.elaborate_item_from_comptime_in_function(function, f); + self.rebind_generics_from_previous_function(); + result + } + + fn elaborate_in_module( + &mut self, + module: ModuleId, + file: FileId, + f: impl FnOnce(&mut Elaborator) -> T, + ) -> T { + self.unbind_generics_from_previous_function(); + let result = self.elaborator.elaborate_item_from_comptime_in_module(module, file, f); self.rebind_generics_from_previous_function(); result } @@ -1244,7 +1258,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut result = self.call_function(function_id, arguments, bindings, location)?; if call.is_macro_call { let expr = result.into_expression(self.elaborator.interner, location)?; - let expr = self.elaborate_item(self.current_function, |elaborator| { + let expr = self.elaborate_in_function(self.current_function, |elaborator| { elaborator.elaborate_expression(expr).0 }); result = self.evaluate(expr)?; 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 65c9c3f018d..899d62ecb61 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -6,13 +6,13 @@ use std::{ use acvm::{AcirField, FieldElement}; use builtin_helpers::{ block_expression_to_value, check_argument_count, check_function_not_yet_resolved, - check_one_argument, check_three_arguments, check_two_arguments, get_expr, get_field, + check_one_argument, check_three_arguments, check_two_arguments, get_bool, get_expr, get_field, get_format_string, get_function_def, get_module, get_quoted, get_slice, get_struct, get_trait_constraint, get_trait_def, get_trait_impl, get_tuple, get_type, get_typed_expr, - get_u32, get_unresolved_type, hir_pattern_to_tokens, mutate_func_meta_type, parse, - replace_func_meta_parameters, replace_func_meta_return_type, + get_u32, get_unresolved_type, has_named_attribute, hir_pattern_to_tokens, + mutate_func_meta_type, parse, replace_func_meta_parameters, replace_func_meta_return_type, }; -use chumsky::{prelude::choice, Parser}; +use chumsky::{chain::Chain, prelude::choice, Parser}; use im::Vector; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; @@ -22,22 +22,25 @@ 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, }, - elaborator::Elaborator, - hir::comptime::{ - errors::IResult, - value::{ExprValue, TypedExpr}, - InterpreterError, Value, + hir::def_collector::dc_crate::CollectedItems, + hir::{ + comptime::{ + errors::IResult, + value::{ExprValue, TypedExpr}, + InterpreterError, Value, + }, + def_map::ModuleId, }, hir_def::function::FunctionBody, lexer::Lexer, macros_api::{HirExpression, HirLiteral, Ident, ModuleDefId, NodeInterner, Signedness}, node_interner::{DefinitionKind, TraitImplKind}, parser::{self}, - token::Token, - QuotedType, Shared, Type, + token::{Attribute, SecondaryAttribute, Token}, + Kind, QuotedType, ResolvedGeneric, Shared, Type, TypeVariable, }; use self::builtin_helpers::{get_array, get_str, get_u8}; @@ -75,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) } @@ -97,10 +101,16 @@ impl<'local, 'context> Interpreter<'local, 'context> { "expr_resolve" => expr_resolve(self, arguments, location), "is_unconstrained" => Ok(Value::Bool(true)), "fmtstr_quoted_contents" => fmtstr_quoted_contents(interner, arguments, location), + "fresh_type_variable" => fresh_type_variable(interner), + "function_def_add_attribute" => function_def_add_attribute(self, arguments, location), "function_def_body" => function_def_body(interner, arguments, location), "function_def_has_named_attribute" => { function_def_has_named_attribute(interner, arguments, location) } + "function_def_is_unconstrained" => { + function_def_is_unconstrained(interner, arguments, location) + } + "function_def_module" => function_def_module(interner, arguments, location), "function_def_name" => function_def_name(interner, arguments, location), "function_def_parameters" => function_def_parameters(interner, arguments, location), "function_def_return_type" => function_def_return_type(interner, arguments, location), @@ -109,6 +119,13 @@ impl<'local, 'context> Interpreter<'local, 'context> { "function_def_set_return_type" => { function_def_set_return_type(self, arguments, location) } + "function_def_set_return_public" => { + function_def_set_return_public(self, arguments, location) + } + "function_def_set_unconstrained" => { + function_def_set_unconstrained(self, arguments, location) + } + "module_add_item" => module_add_item(self, arguments, location), "module_functions" => module_functions(self, arguments, location), "module_has_named_attribute" => module_has_named_attribute(self, arguments, location), "module_is_contract" => module_is_contract(self, arguments, location), @@ -123,6 +140,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location), "quoted_as_type" => quoted_as_type(self, arguments, location), "quoted_eq" => quoted_eq(arguments, location), + "quoted_tokens" => quoted_tokens(arguments, location), "slice_insert" => slice_insert(interner, arguments, location), "slice_pop_back" => slice_pop_back(interner, arguments, location, call_stack), "slice_pop_front" => slice_pop_front(interner, arguments, location, call_stack), @@ -130,9 +148,16 @@ impl<'local, 'context> Interpreter<'local, 'context> { "slice_push_front" => slice_push_front(interner, arguments, location), "slice_remove" => slice_remove(interner, arguments, location, call_stack), "str_as_bytes" => str_as_bytes(interner, arguments, location), + "struct_def_add_attribute" => struct_def_add_attribute(interner, arguments, location), + "struct_def_add_generic" => struct_def_add_generic(interner, arguments, location), "struct_def_as_type" => struct_def_as_type(interner, arguments, location), "struct_def_fields" => struct_def_fields(interner, arguments, location), "struct_def_generics" => struct_def_generics(interner, arguments, location), + "struct_def_has_named_attribute" => { + struct_def_has_named_attribute(interner, arguments, location) + } + "struct_def_module" => struct_def_module(self, arguments, location), + "struct_def_name" => struct_def_name(interner, arguments, location), "struct_def_set_fields" => struct_def_set_fields(interner, arguments, location), "to_le_radix" => to_le_radix(arguments, return_type, location), "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), @@ -263,6 +288,105 @@ fn str_as_bytes( Ok(Value::Array(bytes, byte_array_type)) } +// fn add_attribute(self, attribute: str) +fn struct_def_add_attribute( + interner: &mut NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, attribute) = check_two_arguments(arguments, location)?; + let attribute_location = attribute.1; + let attribute = get_str(interner, attribute)?; + + let mut tokens = Lexer::lex(&format!("#[{}]", attribute)).0 .0; + if let Some(Token::EOF) = tokens.last().map(|token| token.token()) { + tokens.pop(); + } + if tokens.len() != 1 { + return Err(InterpreterError::InvalidAttribute { + attribute: attribute.to_string(), + location: attribute_location, + }); + } + + let token = tokens.into_iter().next().unwrap().into_token(); + let Token::Attribute(attribute) = token else { + return Err(InterpreterError::InvalidAttribute { + attribute: attribute.to_string(), + location: attribute_location, + }); + }; + + let Attribute::Secondary(attribute) = attribute else { + return Err(InterpreterError::InvalidAttribute { + attribute: attribute.to_string(), + location: attribute_location, + }); + }; + + let struct_id = get_struct(self_argument)?; + interner.update_struct_attributes(struct_id, |attributes| { + attributes.push(attribute.clone()); + }); + + Ok(Value::Unit) +} + +// fn add_generic(self, generic_name: str) +fn struct_def_add_generic( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, generic) = check_two_arguments(arguments, location)?; + let generic_location = generic.1; + let generic = get_str(interner, generic)?; + + let mut tokens = Lexer::lex(&generic).0 .0; + if let Some(Token::EOF) = tokens.last().map(|token| token.token()) { + tokens.pop(); + } + + if tokens.len() != 1 { + return Err(InterpreterError::GenericNameShouldBeAnIdent { + name: generic, + location: generic_location, + }); + } + + let Token::Ident(generic_name) = tokens.pop().unwrap().into_token() else { + return Err(InterpreterError::GenericNameShouldBeAnIdent { + name: generic, + location: generic_location, + }); + }; + + let struct_id = get_struct(self_argument)?; + let the_struct = interner.get_struct(struct_id); + let mut the_struct = the_struct.borrow_mut(); + let name = Rc::new(generic_name); + + for generic in &the_struct.generics { + if generic.name == name { + return Err(InterpreterError::DuplicateGeneric { + name, + struct_name: the_struct.name.to_string(), + existing_location: Location::new(generic.span, the_struct.location.file), + duplicate_location: generic_location, + }); + } + } + + let type_var = TypeVariable::unbound(interner.next_type_variable_id()); + let span = generic_location.span; + let kind = Kind::Normal; + let typ = Type::NamedGeneric(type_var.clone(), name.clone(), kind.clone()); + let new_generic = ResolvedGeneric { name, type_var, span, kind }; + the_struct.generics.push(new_generic); + + Ok(Value::Type(typ)) +} + /// fn as_type(self) -> Type fn struct_def_as_type( interner: &NodeInterner, @@ -300,6 +424,25 @@ fn struct_def_generics( Ok(Value::Slice(generics.collect(), typ)) } +// fn has_named_attribute(self, name: Quoted) -> bool +fn struct_def_has_named_attribute( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, name) = check_two_arguments(arguments, location)?; + let struct_id = get_struct(self_argument)?; + + let name = get_quoted(name)?; + let name = name.iter().map(|token| token.to_string()).collect::>().join(""); + + let attributes = interner.struct_attributes(&struct_id); + let attributes = attributes.iter().filter_map(|attribute| attribute.as_custom()); + let attributes = attributes.map(|attribute| &attribute.contents); + + Ok(Value::Bool(has_named_attribute(&name, attributes, location))) +} + /// fn fields(self) -> [(Quoted, Type)] /// Returns (name, type) pairs of each field of this StructDefinition fn struct_def_fields( @@ -327,6 +470,39 @@ fn struct_def_fields( Ok(Value::Slice(fields, typ)) } +// fn module(self) -> Module +fn struct_def_module( + interpreter: &Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let struct_id = get_struct(self_argument)?; + let struct_module_id = struct_id.module_id(); + + // A struct's module is its own module. To get the module where its defined we need + // to look for its parent. + let module_data = interpreter.elaborator.get_module(struct_module_id); + let parent_local_id = module_data.parent.expect("Expected struct module parent to exist"); + let parent = ModuleId { krate: struct_module_id.krate, local_id: parent_local_id }; + + Ok(Value::ModuleDefinition(parent)) +} + +// fn name(self) -> Quoted +fn struct_def_name( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let struct_id = get_struct(self_argument)?; + let the_struct = interner.get_struct(struct_id); + + let name = Token::Ident(the_struct.borrow().name.to_string()); + Ok(Value::Quoted(Rc::new(vec![name]))) +} + /// fn set_fields(self, new_fields: [(Quoted, Type)]) {} /// Returns (name, type) pairs of each field of this StructDefinition fn struct_def_set_fields( @@ -496,9 +672,10 @@ fn quoted_as_module( let path = parse(argument, parser::path_no_turbofish(), "a path").ok(); let option_value = path.and_then(|path| { - let module = interpreter.elaborate_item(interpreter.current_function, |elaborator| { - elaborator.resolve_module_by_path(path) - }); + let module = interpreter + .elaborate_in_function(interpreter.current_function, |elaborator| { + elaborator.resolve_module_by_path(path) + }); module.map(Value::ModuleDefinition) }); @@ -514,7 +691,7 @@ fn quoted_as_trait_constraint( let argument = check_one_argument(arguments, location)?; let trait_bound = parse(argument, parser::trait_bound(), "a trait constraint")?; let bound = interpreter - .elaborate_item(interpreter.current_function, |elaborator| { + .elaborate_in_function(interpreter.current_function, |elaborator| { elaborator.resolve_trait_bound(&trait_bound, Type::Unit) }) .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; @@ -530,11 +707,22 @@ fn quoted_as_type( ) -> IResult { let argument = check_one_argument(arguments, location)?; let typ = parse(argument, parser::parse_type(), "a type")?; - let typ = - interpreter.elaborate_item(interpreter.current_function, |elab| elab.resolve_type(typ)); + let typ = interpreter + .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); Ok(Value::Type(typ)) } +// fn tokens(quoted: Quoted) -> [Quoted] +fn quoted_tokens(arguments: Vec<(Value, Location)>, location: Location) -> IResult { + let argument = check_one_argument(arguments, location)?; + let value = get_quoted(argument)?; + + Ok(Value::Slice( + value.iter().map(|token| Value::Quoted(Rc::new(vec![token.clone()]))).collect(), + Type::Slice(Box::new(Type::Quoted(QuotedType::Quoted))), + )) +} + fn to_le_radix( arguments: Vec<(Value, Location)>, return_type: Type, @@ -598,11 +786,10 @@ fn type_as_constant( location: Location, ) -> IResult { type_as(arguments, return_type, location, |typ| { - if let Type::Constant(n) = typ { - Some(Value::U32(n)) - } else { - None - } + // Prefer to use `evaluate_to_u32` over matching on `Type::Constant` + // since arithmetic generics may be `Type::InfixExpr`s which evaluate to + // constants but are not actually the `Type::Constant` variant. + typ.evaluate_to_u32().map(Value::U32) }) } @@ -704,7 +891,7 @@ where F: FnOnce(Type) -> Option, { let value = check_one_argument(arguments, location)?; - let typ = get_type(value)?; + let typ = get_type(value)?.follow_bindings(); let option_value = f(typ); @@ -733,13 +920,13 @@ fn type_get_trait_impl( let typ = get_type(typ)?; let (trait_id, generics) = get_trait_constraint(constraint)?; - let option_value = match interner.try_lookup_trait_implementation( + let option_value = match interner.lookup_trait_implementation( &typ, trait_id, &generics.ordered, &generics.named, ) { - Ok((TraitImplKind::Normal(trait_impl_id), _)) => Some(Value::TraitImpl(trait_impl_id)), + Ok(TraitImplKind::Normal(trait_impl_id)) => Some(Value::TraitImpl(trait_impl_id)), _ => None, }; @@ -758,7 +945,7 @@ fn type_implements( let (trait_id, generics) = get_trait_constraint(constraint)?; let implements = interner - .try_lookup_trait_implementation(&typ, trait_id, &generics.ordered, &generics.named) + .lookup_trait_implementation(&typ, trait_id, &generics.ordered, &generics.named) .is_ok(); Ok(Value::Bool(implements)) } @@ -1314,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, @@ -1591,25 +1813,33 @@ fn expr_resolve( interpreter.current_function }; - let value = interpreter.elaborate_item(function_to_resolve_in, |elaborator| match expr_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)) + 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); - Value::TypedExpr(TypedExpr::StmtId(stmt_id)) + Ok(Value::TypedExpr(TypedExpr::StmtId(stmt_id))) } ExprValue::LValue(lvalue) => { let expr = lvalue.as_expression(); let (expr_id, _) = elaborator.elaborate_expression(expr); - Value::TypedExpr(TypedExpr::ExprId(expr_id)) + Ok(Value::TypedExpr(TypedExpr::ExprId(expr_id))) } - }); - - Ok(value) + 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 }) + } + } + }) } fn unwrap_expr_value(interner: &NodeInterner, mut expr_value: ExprValue) -> ExprValue { @@ -1631,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, } } @@ -1654,6 +1887,62 @@ fn fmtstr_quoted_contents( Ok(Value::Quoted(Rc::new(tokens))) } +// fn fresh_type_variable() -> Type +fn fresh_type_variable(interner: &NodeInterner) -> IResult { + Ok(Value::Type(interner.next_type_variable())) +} + +// fn add_attribute(self, attribute: str) +fn function_def_add_attribute( + interpreter: &mut Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, attribute) = check_two_arguments(arguments, location)?; + let attribute_location = attribute.1; + let attribute = get_str(interpreter.elaborator.interner, attribute)?; + + let mut tokens = Lexer::lex(&format!("#[{}]", attribute)).0 .0; + if let Some(Token::EOF) = tokens.last().map(|token| token.token()) { + tokens.pop(); + } + if tokens.len() != 1 { + return Err(InterpreterError::InvalidAttribute { + attribute: attribute.to_string(), + location: attribute_location, + }); + } + + let token = tokens.into_iter().next().unwrap().into_token(); + let Token::Attribute(attribute) = token else { + return Err(InterpreterError::InvalidAttribute { + attribute: attribute.to_string(), + location: attribute_location, + }); + }; + + let func_id = get_function_def(self_argument)?; + check_function_not_yet_resolved(interpreter, func_id, location)?; + + let function_modifiers = interpreter.elaborator.interner.function_modifiers_mut(&func_id); + + match &attribute { + Attribute::Function(attribute) => { + function_modifiers.attributes.function = Some(attribute.clone()); + } + Attribute::Secondary(attribute) => { + function_modifiers.attributes.secondary.push(attribute.clone()); + } + } + + if let Attribute::Secondary(SecondaryAttribute::Custom(attribute)) = attribute { + let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id); + func_meta.custom_attributes.push(attribute); + } + + Ok(Value::Unit) +} + // fn body(self) -> Expr fn function_def_body( interner: &NodeInterner, @@ -1678,31 +1967,39 @@ fn function_def_has_named_attribute( ) -> IResult { let (self_argument, name) = check_two_arguments(arguments, location)?; let func_id = get_function_def(self_argument)?; - let name = get_quoted(name)?; let func_meta = interner.function_meta(&func_id); - let attributes = &func_meta.custom_attributes; - if attributes.is_empty() { - return Ok(Value::Bool(false)); - }; + let name = get_quoted(name)?; let name = name.iter().map(|token| token.to_string()).collect::>().join(""); - for attribute in attributes { - let parse_result = Elaborator::parse_attribute(&attribute.contents, location); - let Ok(Some((function, _arguments))) = parse_result else { - continue; - }; + let attributes = &func_meta.custom_attributes; + let attributes = attributes.iter().map(|attribute| &attribute.contents); - let ExpressionKind::Variable(path) = function.kind else { - continue; - }; + Ok(Value::Bool(has_named_attribute(&name, attributes, location))) +} - if path.last_name() == name { - return Ok(Value::Bool(true)); - } - } +// fn is_unconstrained(self) -> bool +fn function_def_is_unconstrained( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let func_id = get_function_def(self_argument)?; + let is_unconstrained = interner.function_modifiers(&func_id).is_unconstrained; + Ok(Value::Bool(is_unconstrained)) +} - Ok(Value::Bool(false)) +// fn module(self) -> Module +fn function_def_module( + interner: &NodeInterner, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let self_argument = check_one_argument(arguments, location)?; + let func_id = get_function_def(self_argument)?; + let module = interner.function_module(func_id); + Ok(Value::ModuleDefinition(module)) } // fn name(self) -> Quoted @@ -1779,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 }; @@ -1823,7 +2130,7 @@ fn function_def_set_parameters( "a pattern", )?; - let hir_pattern = interpreter.elaborate_item(Some(func_id), |elaborator| { + let hir_pattern = interpreter.elaborate_in_function(Some(func_id), |elaborator| { elaborator.elaborate_pattern_and_store_ids( parameter_pattern, parameter_type.clone(), @@ -1871,6 +2178,72 @@ fn function_def_set_return_type( Ok(Value::Unit) } +// fn set_return_public(self, public: bool) +fn function_def_set_return_public( + interpreter: &mut Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, public) = check_two_arguments(arguments, location)?; + + let func_id = get_function_def(self_argument)?; + check_function_not_yet_resolved(interpreter, func_id, location)?; + + let public = get_bool(public)?; + + let func_meta = interpreter.elaborator.interner.function_meta_mut(&func_id); + func_meta.return_visibility = if public { Visibility::Public } else { Visibility::Private }; + + Ok(Value::Unit) +} + +// fn set_unconstrained(self, value: bool) +fn function_def_set_unconstrained( + interpreter: &mut Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, unconstrained) = check_two_arguments(arguments, location)?; + + let func_id = get_function_def(self_argument)?; + check_function_not_yet_resolved(interpreter, func_id, location)?; + + let unconstrained = get_bool(unconstrained)?; + + let modifiers = interpreter.elaborator.interner.function_modifiers_mut(&func_id); + modifiers.is_unconstrained = unconstrained; + + Ok(Value::Unit) +} + +// fn add_item(self, item: Quoted) +fn module_add_item( + interpreter: &mut Interpreter, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + let (self_argument, item) = check_two_arguments(arguments, location)?; + let module_id = get_module(self_argument)?; + let module_data = interpreter.elaborator.get_module(module_id); + + let parser = parser::top_level_items(); + let top_level_statements = parse(item, parser, "a top-level item")?; + + interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { + let mut generated_items = CollectedItems::default(); + + for top_level_statement in top_level_statements { + elaborator.add_item(top_level_statement, &mut generated_items, location); + } + + if !generated_items.is_empty() { + elaborator.elaborate_items(generated_items); + } + }); + + Ok(Value::Unit) +} + // fn functions(self) -> [FunctionDefinition] fn module_functions( interpreter: &Interpreter, @@ -1904,27 +2277,13 @@ fn module_has_named_attribute( let (self_argument, name) = check_two_arguments(arguments, location)?; let module_id = get_module(self_argument)?; let module_data = interpreter.elaborator.get_module(module_id); - let name = get_quoted(name)?; + let name = get_quoted(name)?; let name = name.iter().map(|token| token.to_string()).collect::>().join(""); let attributes = module_data.outer_attributes.iter().chain(&module_data.inner_attributes); - for attribute in attributes { - let parse_result = Elaborator::parse_attribute(attribute, location); - let Ok(Some((function, _arguments))) = parse_result else { - continue; - }; - - let ExpressionKind::Variable(path) = function.kind else { - continue; - }; - - if path.last_name() == name { - return Ok(Value::Bool(true)); - } - } - Ok(Value::Bool(false)) + Ok(Value::Bool(has_named_attribute(&name, attributes, location))) } // fn is_contract(self) -> bool 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 ff3da6d253f..6e72866bec0 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,9 +5,10 @@ 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::{ comptime::{ errors::IResult, @@ -90,6 +91,13 @@ pub(crate) fn get_array( } } +pub(crate) fn get_bool((value, location): (Value, Location)) -> IResult { + match value { + Value::Bool(value) => Ok(value), + value => type_mismatch(value, Type::Bool, location), + } +} + pub(crate) fn get_slice( interner: &NodeInterner, (value, location): (Value, Location), @@ -183,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), @@ -437,3 +448,26 @@ pub(super) fn block_expression_to_value(block_expr: BlockExpression) -> Value { Value::Slice(statements, typ) } + +pub(super) fn has_named_attribute<'a>( + name: &'a str, + attributes: impl Iterator, + location: Location, +) -> bool { + for attribute in attributes { + let parse_result = Elaborator::parse_attribute(attribute, location); + let Ok(Some((function, _arguments))) = parse_result else { + continue; + }; + + let ExpressionKind::Variable(path) = function.kind else { + continue; + }; + + if path.last_name() == name { + return true; + } + } + + false +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index 64b489422a0..a47dbeace50 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -51,7 +51,7 @@ fn interpret_helper(src: &str) -> Result { let main = context.get_main_function(&krate).expect("Expected 'main' function"); let mut elaborator = - Elaborator::elaborate_and_return_self(&mut context, krate, collector.items, None, false); + Elaborator::elaborate_and_return_self(&mut context, krate, collector.items, None); assert_eq!(elaborator.errors.len(), 0); let mut interpreter = elaborator.setup_interpreter(); 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 7d6e4475c7b..f6450175955 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)) } @@ -653,9 +662,15 @@ impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { Value::FunctionDefinition(function_id) => { write!(f, "{}", self.interner.function_name(function_id)) } - Value::ModuleDefinition(_) => write!(f, "(module)"), + Value::ModuleDefinition(module_id) => { + if let Some(attributes) = self.interner.try_module_attributes(module_id) { + write!(f, "{}", &attributes.name) + } else { + write!(f, "(crate root)") + } + } Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), - Value::Type(typ) => write!(f, "{}", typ), + Value::Type(typ) => write!(f, "{:?}", typ), Value::Expr(ExprValue::Expression(expr)) => { write!(f, "{}", remove_interned_in_expression_kind(self.interner, expr.clone())) } @@ -665,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()); @@ -676,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())) } } } @@ -723,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) @@ -895,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( @@ -960,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 3cfa0989d7d..6265d0e65f2 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 @@ -271,7 +271,6 @@ impl DefCollector { ast: SortedModule, root_file_id: FileId, debug_comptime_in_file: Option<&str>, - enable_arithmetic_generics: bool, error_on_unused_items: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { @@ -291,7 +290,6 @@ impl DefCollector { dep.crate_id, context, debug_comptime_in_file, - enable_arithmetic_generics, error_on_usage_tracker, macro_processors, )); @@ -315,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 @@ -471,13 +474,8 @@ impl DefCollector { }) }); - let mut more_errors = Elaborator::elaborate( - context, - crate_id, - def_collector.items, - debug_comptime_in_file, - enable_arithmetic_generics, - ); + let mut more_errors = + Elaborator::elaborate(context, crate_id, def_collector.items, debug_comptime_in_file); errors.append(&mut more_errors); 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 6c1b7632a2e..d93b708c91d 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/def_map/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs index a1c4d04cb30..75b860bf2c6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -76,7 +76,6 @@ impl CrateDefMap { crate_id: CrateId, context: &mut Context, debug_comptime_in_file: Option<&str>, - enable_arithmetic_generics: bool, error_on_unused_imports: bool, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { @@ -133,7 +132,6 @@ impl CrateDefMap { ast, root_file_id, debug_comptime_in_file, - enable_arithmetic_generics, error_on_unused_imports, macro_processors, )); 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 e74468bdf18..5abc94b89a2 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/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 113a4fb3888..d47e6522756 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -1099,13 +1099,19 @@ impl Type { | Type::Unit | Type::Constant(_) | Type::Slice(_) - | Type::TypeVariable(_, _) - | Type::NamedGeneric(_, _, _) | Type::Function(_, _, _, _) | Type::FmtString(_, _) | Type::InfixExpr(..) | Type::Error => true, + Type::TypeVariable(type_var, _) | Type::NamedGeneric(type_var, _, _) => { + if let TypeBinding::Bound(typ) = &*type_var.borrow() { + typ.is_valid_for_unconstrained_boundary() + } else { + true + } + } + // Quoted objects only exist at compile-time where the only execution // environment is the interpreter. In this environment, they are valid. Type::Quoted(_) => true, 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 b7492396c90..7fbbb4fccef 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 7b805b5fd8d..f5f7f0458d7 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 ec09f680bc2..9f7a0564789 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 ce8ef3572e6..66db72eef55 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 87b55540bbd..9357cc65c14 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 aa51779d24b..f298559e65c 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 01b8be8f721..00000000000 --- 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 596d15176bc..968af82a8b3 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}; @@ -26,11 +26,18 @@ use noirc_errors::Span; pub use parser::path::path_no_turbofish; pub use parser::traits::trait_bound; pub use parser::{ - block, expression, fresh_statement, lvalue, parse_program, parse_type, pattern, top_level_items, + block, expression, fresh_statement, lvalue, module, parse_program, parse_type, pattern, + top_level_items, top_level_statement, visibility, }; #[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), @@ -45,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, } } } @@ -221,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 @@ -236,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 { @@ -295,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 { @@ -304,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 } } @@ -325,6 +342,7 @@ impl ParsedModule { pub struct Item { pub kind: ItemKind, pub span: Span, + pub doc_comments: Vec, } #[derive(Clone, Debug)] @@ -384,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) { @@ -404,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)); } } @@ -502,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 2bc7a88c6c5..0ffeb691d35 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -27,37 +27,39 @@ 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::visibility_modifier; +use visibility::item_visibility; +pub use visibility::visibility; 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; @@ -68,9 +70,6 @@ pub(super) mod traits; mod types; mod visibility; -// synthesized by LALRPOP -lalrpop_mod!(pub noir_parser); - #[cfg(test)] mod test_helpers; @@ -94,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)) @@ -174,15 +103,26 @@ fn program() -> impl NoirParser { /// module: top_level_statement module /// | %empty -fn module() -> impl NoirParser { +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 }) }) @@ -193,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 @@ -201,11 +149,11 @@ pub fn top_level_items() -> impl NoirParser> { /// | module_declaration /// | use_statement /// | global_declaration -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(), @@ -216,7 +164,7 @@ 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()) } @@ -224,9 +172,15 @@ 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| { @@ -249,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, @@ -260,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()) @@ -284,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()) @@ -297,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()) @@ -316,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()); @@ -334,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 }) }) } @@ -449,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 { - visibility_modifier() +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> { @@ -636,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) } @@ -737,15 +703,6 @@ fn call_data() -> impl NoirParser { }) } -fn optional_visibility() -> impl NoirParser { - keyword(Keyword::Pub) - .map(|_| Visibility::Public) - .or(call_data()) - .or(keyword(Keyword::ReturnData).map(|_| Visibility::ReturnData)) - .or_not() - .map(|opt| opt.unwrap_or(Visibility::Private)) -} - pub fn expression() -> impl ExprParser { recursive(|expr| { expression_with_precedence( @@ -1588,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!(), @@ -1603,8 +1560,6 @@ mod test { assert!(result.is_err()); None }; - - prototype_parse_use_tree(expected_use_statement.as_ref(), &use_statement_str); } } @@ -1850,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 00000000000..151ff21017f --- /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/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs index 9328c882e54..05138bfffd9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/function.rs @@ -1,10 +1,10 @@ use super::{ attributes::{attributes, validate_attributes}, - block, fresh_statement, ident, keyword, maybe_comp_time, nothing, optional_visibility, - parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, + block, fresh_statement, ident, keyword, maybe_comp_time, nothing, parameter_name_recovery, + parameter_recovery, parenthesized, parse_type, pattern, primitives::token_kind, self_parameter, - visibility::visibility_modifier, + visibility::{item_visibility, visibility}, where_clause, NoirParser, }; use crate::token::{Keyword, Token, TokenKind}; @@ -79,13 +79,9 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser<(bool, ItemVisibility, bool)> { - keyword(Keyword::Unconstrained) - .or_not() - .then(visibility_modifier()) - .then(maybe_comp_time()) - .map(|((unconstrained, visibility), comptime)| { - (unconstrained.is_some(), visibility, comptime) - }) + keyword(Keyword::Unconstrained).or_not().then(item_visibility()).then(maybe_comp_time()).map( + |((unconstrained, visibility), comptime)| (unconstrained.is_some(), visibility, comptime), + ) } pub(super) fn numeric_generic() -> impl NoirParser { @@ -142,14 +138,12 @@ pub(super) fn generics() -> impl NoirParser { pub(super) fn function_return_type() -> impl NoirParser<(Visibility, FunctionReturnType)> { #[allow(deprecated)] - just(Token::Arrow) - .ignore_then(optional_visibility()) - .then(spanned(parse_type())) - .or_not() - .map_with_span(|ret, span| match ret { + just(Token::Arrow).ignore_then(visibility()).then(spanned(parse_type())).or_not().map_with_span( + |ret, span| match ret { Some((visibility, (ty, _))) => (visibility, FunctionReturnType::Ty(ty)), None => (Visibility::Private, FunctionReturnType::Default(span)), - }) + }, + ) } fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { @@ -158,7 +152,7 @@ fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a let full_parameter = pattern() .recover_via(parameter_name_recovery()) .then_ignore(just(Token::Colon)) - .then(optional_visibility()) + .then(visibility()) .then(typ) .map_with_span(|((pattern, visibility), typ), span| Param { visibility, 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 58bf1693eee..66d30a6f407 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 bf5a4b4d0b4..cb17bf2caf7 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/parser/parser/visibility.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/visibility.rs index d9c1abf2123..ea46becc932 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/visibility.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/visibility.rs @@ -4,15 +4,15 @@ use chumsky::{ }; use crate::{ - ast::ItemVisibility, + ast::{ItemVisibility, Visibility}, parser::NoirParser, token::{Keyword, Token}, }; -use super::primitives::keyword; +use super::{call_data, primitives::keyword}; /// visibility_modifier: 'pub(crate)'? 'pub'? '' -pub(crate) fn visibility_modifier() -> impl NoirParser { +pub(crate) fn item_visibility() -> impl NoirParser { let is_pub_crate = (keyword(Keyword::Pub) .then_ignore(just(Token::LeftParen)) .then_ignore(keyword(Keyword::Crate)) @@ -25,3 +25,12 @@ pub(crate) fn visibility_modifier() -> impl NoirParser { choice((is_pub_crate, is_pub, is_private)) } + +pub fn visibility() -> impl NoirParser { + keyword(Keyword::Pub) + .map(|_| Visibility::Public) + .or(call_data()) + .or(keyword(Keyword::ReturnData).map(|_| Visibility::ReturnData)) + .or_not() + .map(|opt| opt.unwrap_or(Visibility::Private)) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 04c4e414858..64c9b7471b4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -96,7 +96,6 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation }; let debug_comptime_in_file = None; - let enable_arithmetic_generics = false; let error_on_unused_imports = true; let macro_processors = &[]; @@ -107,7 +106,6 @@ pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec<(Compilation program.clone().into_sorted(), root_file_id, debug_comptime_in_file, - enable_arithmetic_generics, error_on_unused_imports, macro_processors, )); @@ -859,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); @@ -3366,7 +3364,7 @@ fn warns_on_re_export_of_item_with_less_visibility() { } #[test] -fn unoquted_integer_as_integer_token() { +fn unquoted_integer_as_integer_token() { let src = r#" trait Serialize { fn serialize() {} @@ -3375,9 +3373,9 @@ fn unoquted_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 unoqute $serialized_len, it's unquoted + // 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 // in the context of a generic argument. quote { @@ -3424,3 +3422,32 @@ fn errors_on_unused_function() { assert_eq!(ident.to_string(), "foo"); assert_eq!(*item_type, "function"); } + +#[test] +fn constrained_reference_to_unconstrained() { + let src = r#" + fn main(mut x: u32, y: pub u32) { + let x_ref = &mut x; + if x == 5 { + unsafe { + mut_ref_input(x_ref, y); + } + } + + assert(x == 10); + } + + unconstrained fn mut_ref_input(x: &mut u32, y: u32) { + *x = y; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = + &errors[0].0 + else { + panic!("Expected an error about passing a constrained reference to unconstrained"); + }; +} diff --git a/noir/noir-repo/docs/docs/noir/concepts/generics.md b/noir/noir-repo/docs/docs/noir/concepts/generics.md index 3e416eee093..f05540f9f55 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/generics.md +++ b/noir/noir-repo/docs/docs/noir/concepts/generics.md @@ -45,17 +45,20 @@ fn main() { The `print` function will print `Hello!` an arbitrary number of times, twice in this case. +## Numeric Generics + If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks just like using regular generics, but these generics can resolve to -integers at compile-time, rather than resolving to types. Here's an example of a struct that is -generic over the size of the array it contains internally: +generics. Using these looks similar to using regular generics, but introducing them into scope +requires declaring them with `let MyGenericName: IntegerType`. This can be done anywhere a normal +generic is declared. Instead of types, these generics resolve to integers at compile-time. +Here's an example of a struct that is generic over the size of the array it contains internally: ```rust -struct BigInt { +struct BigInt { limbs: [u32; N], } -impl BigInt { +impl BigInt { // `N` is in scope of all methods in the impl fn first(first: BigInt, second: BigInt) -> Self { assert(first.limbs != second.limbs); @@ -77,7 +80,7 @@ This is what [traits](../concepts/traits.md) are for in Noir. Here's an example any type `T` that implements the `Eq` trait for equality: ```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool where T: Eq { if (array1.len() == 0) | (array2.len() == 0) { @@ -161,3 +164,47 @@ fn example() { assert(10 as u32 == foo.generic_method::()); } ``` + +## Arithmetic Generics + +In addition to numeric generics, Noir also allows a limited form of arithmetic on generics. +When you have a numeric generic such as `N`, you can use the following operators on it in a +type position: `+`, `-`, `*`, `/`, and `%`. + +Note that type checking arithmetic generics is a best effort guess from the compiler and there +are many cases of types that are equal that the compiler may not see as such. For example, +we know that `T * (N + M)` should be equal to `T*N + T*M` but the compiler does not currently +apply the distributive law and thus sees these as different types. + +Even with this limitation though, the compiler can handle common cases decently well: + +```rust +trait Serialize { + fn serialize(self) -> [Field; N]; +} + +impl Serialize<1> for Field { + fn serialize(self) -> [Field; 1] { + [self] + } +} + +impl Serialize for [T; N] + where T: Serialize { .. } + +impl Serialize for (T, U) + where T: Serialize, U: Serialize { .. } + +fn main() { + let data = (1, [2, 3, 4]); + assert(data.serialize().len(), 4); +} +``` + +Note that if there is any over or underflow the types will fail to unify: + +#include_code underflow-example test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr rust + +This also applies if there is underflow in an intermediate calculation: + +#include_code intermediate-underflow-example test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr rust 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 651e7f5723b..70590ad30d8 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 3a3c61b41f5..7ee33027354 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 5657e05fff7..7fae5fc381a 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/function_def.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/function_def.md @@ -7,6 +7,14 @@ a function definition in the source program. ## Methods +### add_attribute + +#include_code add_attribute noir_stdlib/src/meta/function_def.nr rust + +Adds an attribute to the function. 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. + ### body #include_code body noir_stdlib/src/meta/function_def.nr rust @@ -21,6 +29,18 @@ This means any functions called at compile-time are invalid targets for this met Returns true if this function has a custom attribute with the given name. +### is_unconstrained + +#include_code is_unconstrained noir_stdlib/src/meta/function_def.nr rust + +Returns true if this function is unconstrained. + +### module + +#include_code module noir_stdlib/src/meta/function_def.nr rust + +Returns the module where the function is defined. + ### name #include_code name noir_stdlib/src/meta/function_def.nr rust @@ -65,3 +85,19 @@ each parameter pattern to be a syntactically valid parameter. Mutates the function's return type to a new type. 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. + +### set_return_public + +#include_code set_return_public noir_stdlib/src/meta/function_def.nr rust + +Mutates the function's return visibility to public (if `true` is given) or private (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. + +### set_unconstrained + +#include_code set_unconstrained noir_stdlib/src/meta/function_def.nr rust + +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 diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/module.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/module.md index 870e366461c..de042760d51 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/module.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/module.md @@ -8,6 +8,14 @@ declarations in the source program. ## Methods +### add_item + +#include_code add_item noir_stdlib/src/meta/module.nr rust + +Adds a top-level item (a function, a struct, a global, etc.) to the module. +Adding multiple items in one go is also valid if the `Quoted` value has multiple items in it. +Note that the items are type-checked as if they are inside the module they are being added to. + ### name #include_code name noir_stdlib/src/meta/module.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/quoted.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/quoted.md index bf79f2e5d9f..ac075d96648 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/quoted.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/quoted.md @@ -50,6 +50,12 @@ stream doesn't parse to a type or if the type isn't a valid type in scope. #include_code implements_example test_programs/compile_success_empty/comptime_type/src/main.nr rust +### tokens + +#include_code tokens noir_stdlib/src/meta/quoted.nr rust + +Returns a slice of the individual tokens that form this token stream. + ## Trait Implementations ```rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/struct_def.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/struct_def.md index 95e377dffd4..c088e538fc9 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/struct_def.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/struct_def.md @@ -7,6 +7,30 @@ This type corresponds to `struct Name { field1: Type1, ... }` items in the sourc ## Methods +### add_attribute + +#include_code add_attribute noir_stdlib/src/meta/struct_def.nr rust + +Adds an attribute to the struct. + +### add_generic + +#include_code add_generic noir_stdlib/src/meta/struct_def.nr rust + +Adds an generic to the struct. Returns the new generic type. +Errors if the given generic name isn't a single identifier or if +the struct already has a generic with the same name. + +This method should be used carefully, if there is existing code referring +to the struct type it may be checked before this function is called and +see the struct with the original number of generics. This method should +thus be preferred to use on code generated from other macros and structs +that are not used in function signatures. + +Example: + +#include_code add-generic-example test_programs/compile_success_empty/comptime_struct_definition/src/main.nr rust + ### as_type #include_code as_type noir_stdlib/src/meta/struct_def.nr rust @@ -44,6 +68,27 @@ comptime fn example(foo: StructDefinition) { Returns each field of this struct as a pair of (field name, field type). +### has_named_attribute + +#include_code has_named_attribute noir_stdlib/src/meta/struct_def.nr rust + +Returns true if this struct has a custom attribute with the given name. + +### module + +#include_code module noir_stdlib/src/meta/struct_def.nr rust + +Returns the module where the struct is defined. + +### name + +#include_code name noir_stdlib/src/meta/struct_def.nr rust + +Returns the name of this struct + +Note that the returned quoted value will be just the struct name, it will +not be the full path to the struct, nor will it include any generics. + ### set_fields #include_code set_fields noir_stdlib/src/meta/struct_def.nr rust diff --git a/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md b/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md index 0b6f8d5f77d..1334092a9fa 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/meta/typ.md @@ -5,6 +5,30 @@ title: Type `std::meta::typ` contains methods on the built-in `Type` type used for representing a type in the source program. +## Functions + +#include_code fresh_type_variable noir_stdlib/src/meta/typ.nr rust + +Creates and returns an unbound type variable. This is a special kind of type internal +to type checking which will type check with any other type. When it is type checked +against another type it will also be set to that type. For example, if `a` is a type +variable and we have the type equality `(a, i32) = (u8, i32)`, the compiler will set +`a` equal to `u8`. + +Unbound type variables will often be rendered as `_` while printing them. Bound type +variables will appear as the type they are bound to. + +This can be used in conjunction with functions which internally perform type checks +such as `Type::implements` or `Type::get_trait_impl` to potentially grab some of the types used. + +Note that calling `Type::implements` or `Type::get_trait_impl` on a type variable will always +fail. + +Example: + +#include_code serialize-setup test_programs/compile_success_empty/comptime_type/src/main.nr rust +#include_code fresh-type-variable-example test_programs/compile_success_empty/comptime_type/src/main.nr rust + ## Methods ### as_array diff --git a/noir/noir-repo/noir_stdlib/src/append.nr b/noir/noir-repo/noir_stdlib/src/append.nr index 4577ae199b8..22baa9205a0 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/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index b7f473429a7..521604a4e20 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -11,7 +11,13 @@ trait Eq { comptime fn derive_eq(s: StructDefinition) -> Quoted { let signature = quote { fn eq(_self: Self, _other: Self) -> bool }; let for_each_field = |name| quote { (_self.$name == _other.$name) }; - let body = |fields| fields; + let body = |fields| { + if s.fields().len() == 0 { + quote { true } + } else { + fields + } + }; crate::meta::make_trait_impl(s, quote { Eq }, signature, for_each_field, quote { & }, body) } // docs:end:derive_eq 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 a4c0f642a82..fede1b17c07 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 27a7d0d3550..e84103aafc5 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 81d78377ce8..3b28ced5835 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 963808f6053..cf9b6187c02 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 81c408c01bc..714079d306a 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 43638ad791b..642dbecc36b 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/function_def.nr b/noir/noir-repo/noir_stdlib/src/meta/function_def.nr index cbbbfb2f901..5ce3dbeabff 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/function_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/function_def.nr @@ -1,4 +1,9 @@ impl FunctionDefinition { + #[builtin(function_def_add_attribute)] + // docs:start:add_attribute + fn add_attribute(self, attribute: str) {} + // docs:end:add_attribute + #[builtin(function_def_body)] // docs:start:body fn body(self) -> Expr {} @@ -9,6 +14,16 @@ impl FunctionDefinition { fn has_named_attribute(self, name: Quoted) -> bool {} // docs:end:has_named_attribute + #[builtin(function_def_is_unconstrained)] + // docs:start:is_unconstrained + fn is_unconstrained(self) -> bool {} + // docs:end:is_unconstrained + + #[builtin(function_def_module)] + // docs:start:module + fn module(self) -> Module {} + // docs:end:module + #[builtin(function_def_name)] // docs:start:name fn name(self) -> Quoted {} @@ -38,4 +53,14 @@ impl FunctionDefinition { // docs:start:set_return_type fn set_return_type(self, return_type: Type) {} // docs:end:set_return_type + + #[builtin(function_def_set_return_public)] + // docs:start:set_return_public + fn set_return_public(self, public: bool) {} + // docs:end:set_return_public + + #[builtin(function_def_set_unconstrained)] + // docs:start:set_unconstrained + fn set_unconstrained(self, value: bool) {} + // docs:end:set_unconstrained } diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 9fc399ddbf9..1079cc6013a 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/module.nr b/noir/noir-repo/noir_stdlib/src/meta/module.nr index b3f76812b8a..bee6612e1bf 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/module.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/module.nr @@ -1,4 +1,9 @@ impl Module { + #[builtin(module_add_item)] + // docs:start:add_item + fn add_item(self, item: Quoted) {} + // docs:end:add_item + #[builtin(module_has_named_attribute)] // docs:start:has_named_attribute fn has_named_attribute(self, name: Quoted) -> bool {} diff --git a/noir/noir-repo/noir_stdlib/src/meta/op.nr b/noir/noir-repo/noir_stdlib/src/meta/op.nr index f3060a1648b..4b104486201 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/meta/quoted.nr b/noir/noir-repo/noir_stdlib/src/meta/quoted.nr index 9fd1e9026ba..3fab43359c1 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/quoted.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/quoted.nr @@ -3,24 +3,29 @@ use crate::option::Option; impl Quoted { #[builtin(quoted_as_expr)] -// docs:start:as_expr + // docs:start:as_expr fn as_expr(self) -> Option {} // docs:end:as_expr #[builtin(quoted_as_module)] -// docs:start:as_module + // docs:start:as_module fn as_module(self) -> Option {} // docs:end:as_module #[builtin(quoted_as_trait_constraint)] -// docs:start:as_trait_constraint + // docs:start:as_trait_constraint fn as_trait_constraint(self) -> TraitConstraint {} // docs:end:as_trait_constraint #[builtin(quoted_as_type)] -// docs:start:as_type + // docs:start:as_type fn as_type(self) -> Type {} // docs:end:as_type + + #[builtin(quoted_tokens)] + // docs:start:tokens + fn tokens(self) -> [Quoted] {} + // docs:end:tokens } impl Eq for Quoted { diff --git a/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr b/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr index 1ca1b6a3925..5db720b91d3 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/struct_def.nr @@ -1,24 +1,49 @@ impl StructDefinition { + #[builtin(struct_def_add_attribute)] + // docs:start:add_attribute + fn add_attribute(self, attribute: str) {} + // docs:end:add_attribute + + #[builtin(struct_def_add_generic)] + // docs:start:add_generic + fn add_generic(self, generic_name: str) -> Type {} + // docs:end:add_generic + /// Return a syntactic version of this struct definition as a type. /// For example, `as_type(quote { type Foo { ... } })` would return `Foo` #[builtin(struct_def_as_type)] -// docs:start:as_type + // docs:start:as_type fn as_type(self) -> Type {} // docs:end:as_type + #[builtin(struct_def_has_named_attribute)] + // docs:start:has_named_attribute + fn has_named_attribute(self, name: Quoted) -> bool {} + // docs:end:has_named_attribute + /// Return each generic on this struct. #[builtin(struct_def_generics)] -// docs:start:generics + // docs:start:generics fn generics(self) -> [Type] {} // docs:end:generics /// Returns (name, type) pairs of each field in this struct. Each type is as-is /// with any generic arguments unchanged. #[builtin(struct_def_fields)] -// docs:start:fields + // docs:start:fields fn fields(self) -> [(Quoted, Type)] {} // docs:end:fields + #[builtin(struct_def_module)] + // docs:start:module + fn module(self) -> Module {} + // docs:end:module + + #[builtin(struct_def_name)] + // docs:start:name + fn name(self) -> Quoted {} + // docs:end:name + /// Sets the fields of this struct to the given fields list. /// All existing fields of the struct will be overridden with the given fields. /// Each element of the fields list corresponds to the name and type of a field. diff --git a/noir/noir-repo/noir_stdlib/src/meta/typ.nr b/noir/noir-repo/noir_stdlib/src/meta/typ.nr index 12dc91a4925..71bd6fd7f1c 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/typ.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/typ.nr @@ -1,6 +1,11 @@ use crate::cmp::Eq; use crate::option::Option; +#[builtin(fresh_type_variable)] +// docs:start:fresh_type_variable +pub fn fresh_type_variable() -> Type {} +// docs:end:fresh_type_variable + impl Type { #[builtin(type_as_array)] // docs:start:as_array 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 00000000000..833c1f3dcfc --- /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 b14f38bdf55..b6e54eaae60 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/arithmetic_generics_intermediate_underflow/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_intermediate_underflow/Nargo.toml new file mode 100644 index 00000000000..0c5d98628a1 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_intermediate_underflow/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "arithmetic_generics_intermediate_underflow" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr new file mode 100644 index 00000000000..58cf2f648e5 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_intermediate_underflow/src/main.nr @@ -0,0 +1,32 @@ +// docs:start:intermediate-underflow-example +fn main() { + // From main it looks like there's nothing sketchy going on + seems_fine([]); +} + +// Since `seems_fine` says it can receive and return any length N +fn seems_fine(array: [Field; N]) -> [Field; N] { + // But inside `seems_fine` we pop from the array which + // requires the length to be greater than zero. + + // error: Could not determine array length `(0 - 1)` + push_zero(pop(array)) +} + +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N { + result[i] = array[i]; + } + result +} + +fn push_zero(array: [Field; N]) -> [Field; N + 1] { + let mut result: [Field; N + 1] = std::mem::zeroed(); + for i in 0..N { + result[i] = array[i]; + } + // index N is already zeroed + result +} +// docs:end:intermediate-underflow-example diff --git a/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_underflow/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_underflow/Nargo.toml new file mode 100644 index 00000000000..f024f4c3b59 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_underflow/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "arithmetic_generics_underflow" +type = "bin" +authors = [""] +compiler_version = ">=0.33.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr new file mode 100644 index 00000000000..4df83ac56e0 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/arithmetic_generics_underflow/src/main.nr @@ -0,0 +1,14 @@ +// docs:start:underflow-example +fn pop(array: [Field; N]) -> [Field; N - 1] { + let mut result: [Field; N - 1] = std::mem::zeroed(); + for i in 0..N { + result[i] = array[i]; + } + result +} + +fn main() { + // error: Could not determine array length `(0 - 1)` + pop([]); +} +// docs:end:underflow-example 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 00000000000..3ce62412b32 --- /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 00000000000..5da409682f6 --- /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 0bad42aee57..79bf3e29533 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 @@ -6,3 +6,16 @@ struct SomeStruct { } fn main() {} + +// Check that add_attribute and has_named_attribute work well + +#[add_attribute] +struct Foo { + +} + +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 8528bbce153..6bfd8ef9699 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 @@ -3,7 +3,7 @@ use std::meta::type_of; struct Foo { x: Field, field: Field } #[function_attr] -fn foo(w: i32, y: Field, Foo { x, field: some_field }: Foo, mut a: bool, (b, c): (i32, i32)) -> i32 { +pub fn foo(w: i32, y: Field, Foo { x, field: some_field }: Foo, mut a: bool, (b, c): (i32, i32)) -> i32 { let _ = (w, y, x, some_field, a, b, c); 1 } @@ -57,3 +57,32 @@ comptime fn mutate_add_one(f: FunctionDefinition) { fn main() { assert_eq(add_one(41), 42); } + +contract some_contract { + // No pub on the return type is an error + #[super::set_pub_return] + pub fn bar() -> Field { + 1 + } +} + +fn set_pub_return(f: FunctionDefinition) { + f.set_return_public(true); +} + +mod foo { + #[attr] + pub fn some() {} + + comptime fn attr(f: FunctionDefinition) { + assert_eq(f.module().name(), quote { foo }); + + assert(!f.is_unconstrained()); + + f.set_unconstrained(true); + assert(f.is_unconstrained()); + + f.set_unconstrained(false); + 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 00000000000..2de6e4d149e --- /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 00000000000..a1a2c4b125a --- /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 5722d42ca26..c3ba7ba4643 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 @@ -1,8 +1,8 @@ #[outer_attribute] mod foo { #![some_attribute] - fn x() {} - fn y() {} + pub fn x() {} + pub fn y() {} } contract bar {} @@ -13,7 +13,7 @@ mod another_module {} #[outer_attribute_func] mod yet_another_module { #![super::inner_attribute_func] - fn foo() {} + pub fn foo() {} } #[outer_attribute_separate_module] @@ -21,25 +21,43 @@ mod separate_module; comptime mut global counter = 0; -fn increment_counter() { +comptime fn increment_counter() { counter += 1; } -fn outer_attribute_func(m: Module) { +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) { +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(); } +struct Foo {} + +#[add_function] +mod add_to_me { + fn add_to_me_function() {} +} + +comptime fn add_function(m: Module) { + m.add_item( + quote { pub fn added_function() -> super::Foo { + add_to_me_function(); + super::Foo {} + } } + ); +} + fn main() { comptime { @@ -68,6 +86,11 @@ fn main() { } assert_eq(counter, 4); + + yet_another_module::generated_outer_function(); + yet_another_module::generated_inner_function(); + + let _ = add_to_me::added_function(); } // docs:start:as_module_example 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 53784101507..c1aeac4622c 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/comptime_type_definition/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/Nargo.toml similarity index 69% rename from noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/Nargo.toml index 099545a9e71..4495d27e028 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "comptime_type_definition" +name = "comptime_struct_definition" type = "bin" authors = [""] compiler_version = ">=0.31.0" diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr new file mode 100644 index 00000000000..da2871a253d --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_struct_definition/src/main.nr @@ -0,0 +1,50 @@ +#[my_comptime_fn] +struct MyType { + field1: [A; 10], + field2: (B, C), +} + +#[mutate_struct_fields] +struct I32AndField { + z: i8, +} + +comptime fn my_comptime_fn(typ: StructDefinition) { + let _ = typ.as_type(); + assert_eq(typ.generics().len(), 3); + assert_eq(typ.fields().len(), 2); + assert_eq(typ.name(), quote { MyType }); +} + +comptime fn mutate_struct_fields(s: StructDefinition) { + let fields = &[ + (quote[x], quote[i32].as_type()), + (quote[y], quote[Field].as_type()) + ]; + s.set_fields(fields); +} + +mod foo { + #[attr] + struct Foo {} + + comptime fn attr(s: StructDefinition) { + assert_eq(s.module().name(), quote { foo }); + } + + #[add_generic] + struct Bar {} + + // docs:start:add-generic-example + comptime fn add_generic(s: StructDefinition) { + assert_eq(s.generics().len(), 0); + let new_generic = s.add_generic("T"); + + let generics = s.generics(); + assert_eq(generics.len(), 1); + assert_eq(generics[0], new_generic); + } + // docs:end:add-generic-example +} + +fn main() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr index 0b15c5605b3..c9307570c87 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -19,6 +19,18 @@ struct StructDoesNotImplementSomeTrait { } +// docs:start:serialize-setup +trait Serialize {} + +impl Serialize<1> for Field {} + +impl Serialize for [T; N] + where T: Serialize {} + +impl Serialize for (T, U) + where T: Serialize, U: Serialize {} +// docs:end:serialize-setup + fn main() { comptime { @@ -115,6 +127,29 @@ fn main() { let str_type = quote { str<10> }.as_type(); let constant = str_type.as_str().unwrap(); assert_eq(constant.as_constant().unwrap(), 10); + + // Check std::meta::typ::fresh_type_variable + // docs:start:fresh-type-variable-example + let typevar1 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar1> }.as_trait_constraint(); + let field_type = quote { Field }.as_type(); + + // Search for a trait impl (binding typevar1 to 1 when the impl is found): + assert(field_type.implements(constraint)); + + // typevar1 should be bound to the "1" generic now: + assert_eq(typevar1.as_constant().unwrap(), 1); + + // If we want to do the same with a different type, we need to + // create a new type variable now that `typevar1` is bound + let typevar2 = std::meta::typ::fresh_type_variable(); + let constraint = quote { Serialize<$typevar2> }.as_trait_constraint(); + let array_type = quote { [(Field, Field); 5] }.as_type(); + assert(array_type.implements(constraint)); + + // Now typevar2 should be bound to the serialized pair size 2 times the array length 5 + assert_eq(typevar2.as_constant().unwrap(), 10); + // docs:end:fresh-type-variable-example } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr deleted file mode 100644 index aca8d067dde..00000000000 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_type_definition/src/main.nr +++ /dev/null @@ -1,26 +0,0 @@ -fn main() {} - -#[my_comptime_fn] -struct MyType { - field1: [A; 10], - field2: (B, C), -} - -#[mutate_struct_fields] -struct I32AndField { - z: i8, -} - -comptime fn my_comptime_fn(typ: StructDefinition) { - let _ = typ.as_type(); - assert_eq(typ.generics().len(), 3); - assert_eq(typ.fields().len(), 2); -} - -comptime fn mutate_struct_fields(s: StructDefinition) { - let fields = &[ - (quote[x], quote[i32].as_type()), - (quote[y], quote[Field].as_type()) - ]; - s.set_fields(fields); -} 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 26be3135133..9dc8cf1ed47 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/compile_success_empty/references_aliasing/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/references_aliasing/src/main.nr index 0d96bc2a734..d3e4257851b 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/references_aliasing/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/references_aliasing/src/main.nr @@ -5,6 +5,7 @@ fn main() { assert(*xref == 101); regression_2445(); + single_alias_inside_loop(); } fn increment(mut r: &mut Field) { @@ -26,3 +27,15 @@ fn regression_2445() { assert(**array[0] == 2); assert(**array[1] == 2); } + +fn single_alias_inside_loop() { + let mut var = 0; + let ref = &mut &mut var; + + for _ in 0..1 { + **ref = 2; + } + + assert(var == 2); + assert(**ref == 2); +} diff --git a/noir/noir-repo/test_programs/execution_success/derive/src/main.nr b/noir/noir-repo/test_programs/execution_success/derive/src/main.nr index 5ec2fb32a79..b32612831d7 100644 --- a/noir/noir-repo/test_programs/execution_success/derive/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/derive/src/main.nr @@ -35,6 +35,9 @@ struct MyOtherOtherStruct { x: T, } +#[derive(Eq, Default, Hash, Ord)] +struct EmptyStruct { } + fn main() { let s = MyStruct { my_field: 1 }; s.do_nothing(); @@ -53,6 +56,9 @@ fn main() { let mut hasher = TestHasher { result: 0 }; o1.hash(&mut hasher); assert_eq(hasher.finish(), 12 + 24 + 54); + + let empty = EmptyStruct {}; + assert_eq(empty, empty); } struct TestHasher { diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml deleted file mode 100644 index 8fce1bf44b6..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "verify_honk_proof" -type = "bin" -authors = [""] - -[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml deleted file mode 100644 index 5bdebb476be..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml +++ /dev/null @@ -1,575 +0,0 @@ -key_hash = "0x0000000000000000000000000000000000000000000000000000000000000000" -proof = [ - "0x0000000000000000000000000000000000000000000000000000000000000040", - "0x0000000000000000000000000000000000000000000000000000000000000011", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf", - "0x00000000000000000000000000000000000000000000000b75c020998797da78", - "0x0000000000000000000000000000000000000000000000005a107acb64952eca", - "0x000000000000000000000000000000000000000000000000000031e97a575e9d", - "0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4", - "0x00000000000000000000000000000000000000000000000c410db10a01750aeb", - "0x00000000000000000000000000000000000000000000000d722669117f9758a4", - "0x000000000000000000000000000000000000000000000000000178cbf4206471", - "0x000000000000000000000000000000000000000000000000e91b8a11e7842c38", - "0x000000000000000000000000000000000000000000000007fd51009034b3357f", - "0x000000000000000000000000000000000000000000000009889939f81e9c7402", - "0x0000000000000000000000000000000000000000000000000000f94656a2ca48", - "0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f", - "0x0000000000000000000000000000000000000000000000093fe27776f50224bd", - "0x000000000000000000000000000000000000000000000004a0c80c0da527a081", - "0x0000000000000000000000000000000000000000000000000001b52c2020d746", - "0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632", - "0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc", - "0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62", - "0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c", - "0x000000000000000000000000000000b0804efd6573805f991458295f510a2004", - "0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e", - "0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47", - "0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15", - "0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd", - "0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383", - "0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4", - "0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98", - "0x0000000000000000000000000000006cf7dd96d7636fda5953191b1ad776d491", - "0x00000000000000000000000000000000001633d881a08d136e834cb13a28fcc6", - "0x00000000000000000000000000000001254956cff6908b069fca0e6cf1c47eb1", - "0x000000000000000000000000000000000006f4d4dd3890e997e75e75886bf8f7", - "0x0000000000000000000000000000006cf7dd96d7636fda5953191b1ad776d491", - "0x00000000000000000000000000000000001633d881a08d136e834cb13a28fcc6", - "0x00000000000000000000000000000001254956cff6908b069fca0e6cf1c47eb1", - "0x000000000000000000000000000000000006f4d4dd3890e997e75e75886bf8f7", - "0x000000000000000000000000000000f968b227a358a305607f3efc933823d288", - "0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08", - "0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f", - "0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1", - "0x000000000000000000000000000000286fcda0e28617c86e195005b9f2efc555", - "0x00000000000000000000000000000000000dc409eb684b23f6a97175bcb9b486", - "0x000000000000000000000000000000e8de6a193cd36414f598bc7c48d67c3b59", - "0x00000000000000000000000000000000002a8a791544cad8c712de871e3de50a", - "0x000000000000000000000000000000d6f1e64b562df0f17ecc6aa46392f8d5a3", - "0x00000000000000000000000000000000000aac977763f33fd6a360ccc50a827a", - "0x000000000000000000000000000000899fa957f5597c6419e3ead9843d21d917", - "0x000000000000000000000000000000000016c4611846952bd6833c35fb11c0da", - "0x013dbfbfbfb2ae7d524edb15343e551d9510b3116223baaa67312d17652f2fb1", - "0x2f268eb3217ef1ac66016aa14d43033f932335371795b5e6dcb0c87c8ad0d050", - "0x2d5dbd52e00ae837e9868289fbe9057f16ea5b76c7e362603e8883f0de4b3e94", - "0x0e357b6a266c20d5e546c2931475eb044d7e75e08ec31b5e8623aec30f964323", - "0x0a9ace4dea44d0a2e8d12d495a683f508714356656aea3882436b729ead24165", - "0x0c17102a98ccb76faf0f78d669ee9cfb694849896787c985225d92e1af3cab35", - "0x09cc7cb719deb139c84fd9fa273e862a1b5d1cec2501c6cd8ba3c37ca06ac07f", - "0x15a0369f3f95d53687dfe79483baf75597d8b281fe0595caf1f7c9ccf99d985e", - "0x17fb53a42b3d1fa5d26ab19dfcc0d74d1781cee0be98dcc492c22e8f3442c4db", - "0x291d6810fc6afc5c2254fd283843a74c85a77275eee3049ea8ed9c88e02a99b8", - "0x0ad40d1627c31247dfb894584a71f8599cfcb85afe84b20186fc07fccae1aa4a", - "0x251cd908fb4e9fe88660f2303f8d7e4d7886da32fddc0319a842b99543659c0b", - "0x1885bdea3dd82085ca67502ebec8ad87213493e18a06cfa27e2c69810481b4a7", - "0x239ab5ba86866bc6705091f82a6a29444dc76b0e7d94cede7eb745cce36ab2cf", - "0x088d29a03baa491845d152124189dfb8bf70ba9bf1fb00c379199dbb0195c663", - "0x18c9fbe3227988d2da599eba82d60f4de25b442b663585fdc611e37305fa77fc", - "0x010242ae641a8cc4d06b5d24e38d9fa6254f981e28f238ccf6aad580f780d3f5", - "0x00128d34b122e84d7e23276b1f13f5789a562e82c727e9ffcfd7bbaccbe69e04", - "0x0776defaf478bfea4db2698542314e27213f63c96e41f98d4d82a47ed6fab55d", - "0x273014a360eaaa493e398df82f18d9cae37f4b6c0ead20100cad3f5491805298", - "0x2b13528eb9ab6fa705f2b48c9ec6ce054ac984e3adf17d4d73431e8456bf4a3c", - "0x22dafe1d63e39cd2effb236da2e131ee1c8cf4049ce504431dcaf98f75c47ad8", - "0x1afb5bc7eb8d30d807101357bb290f9c3113523f4aacc1154a27b075e46a4fa4", - "0x0782dd7df679163e5f0c126abc901d00f3d7d0856b4c02a199ab691ecd7566e6", - "0x2e556c722c99a84a09ffdcc719178277f8e6c9e31a4769270e3b522b944b8ea2", - "0x1be933a48dca8ef26202d3f135998ac8bed6947020b7447ffb6033b0e37f2065", - "0x2d8ebae210848de2464f5435f1fd4b5467ee938910d7779002614943060bbb32", - "0x2da854bbee38a94a6a9c2c85dd05bb4c879173720b67f93f78b9de93cdb427b0", - "0x0fa2649472af2e79489c466b58002f8f284f953085ac0a98dfabee85b78f63cf", - "0x304a09437636026ef0746c4b8ac1ca0ff250c5630fb5bd03ddafddd7cbde850e", - "0x0c83bb3c6ee0faa1646ee4d8dd83f67ec98e5d63ac802f7bdebfcdf21dee62f1", - "0x229d7e4524b30c18a6b94f0054e6d2ea8eb2396f58f9c808d2c9f991e2be2399", - "0x1265bf5e1aaddeae09242b1435e2f8f9e7487bf76a0461752777f6ea1ff75ad6", - "0x2f32f53281b7a363d6bec84ca21c71c3206d906b036e8b36b0702780e3b1b870", - "0x017fb18c9aef4d6d2bc99f5d7f9a002c8921fcd7c7ba69bf05930b55c2829cb7", - "0x2ec761c02ef6f2eefb7c9b2d6df71795d0ce0820f86797e2e11415cb5b122f22", - "0x2b1722960f42a1b40ffae3e4b9419fc8ff5cb8139a2c7e89af332ba2e95c1b5f", - "0x2dafa15594da2318245475c77eae3712429226b3005852e70f567efff0a7d79a", - "0x2ed44d7e3d5f44ac8f7c144ee0ba9d369c82428827c19b010384708bbc52a3f9", - "0x2777eedda697c7f90aee44fa97cfe62596d16c43fa3545b48d622023ca7a446a", - "0x1a47a5c1b0f41905aa0bad6248be8c7887ddea3ad9dfc8462b23a95b073c8a49", - "0x093656d571e84ac676a265dd509c98513039552b7a24e001b003ca618cc4ea5c", - "0x15c901e8a7ff0f1ae1989b5cfb687975c16716a8014a4052d527d4db4ecbaeb4", - "0x08bfa20e83096b0be58e4c96232510c8ef9824c0a62b91ffcc4592b217753a72", - "0x021913efbdfbc73aa5f4a97c79f352ac61f71248947f5eb5713c1b107c632703", - "0x00df89625aef270fab2a8c33ba742e1375423f4cfb3f63514ae748e004bb8cf4", - "0x2455f76c8ee59e93cbe7fe192cf0f766e1399617cabfa230cf27ca2a18cd58d5", - "0x150c3e56ea4f6442ed6b11030c98682a8f5e3c9cd6fd18949254a7c79b3cb5b6", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x01e89c6fe644aea7f63301278dbdb4ea29cf4d33f8b0cdcd79cb106e0bf0a753", - "0x2d49d23421e253903b8a5d0911642b9ce218cef4e350cf8689704eb1f3ae38d4", - "0x072956ca447343d788791fee1ef222f280048ad4aefb6cb7bc96b538f482f525", - "0x168176bf15c8ca63457acf05efbe54af452ea41f935ab82c2a96fedde10ba52f", - "0x20a13690f13491f7f3b756a1dc3b69a3f96d78355c70289583032a593bfc87bc", - "0x273e0a32ab3ef0d3f179b62520b31015ccfc8b53c76a1bb323b41e40ff954596", - "0x28019d4b05546b44e35d5dc74375b75dabb6fae49a07381605c60423c6163d26", - "0x10beda0b8dd484c63f0937820e2c7e9be832a0031efe3557631608895255ca5f", - "0x095a8f04a901526e4d70b1560bfff29b5a3c30347725d1e420c1b30ee2bf8a1c", - "0x1fb742e863a5c76262ffec93b3351405b0840b326fa5fffd73f40abcd5f05f05", - "0x11fa63cfcb2e603fe4e4668d75f05a2cf22650b84a91d1753e83f0e7ae83b4ad", - "0x2872e3d3c431a8b7ee4cec1c2a999a42c40ae33382fbba80a6d4c1a39b2d57a3", - "0x17e8c2a5f809f9935d7c6d7cb2f8859a513864b53f53de3d2a14c74cd690bd1a", - "0x20a552298d691393ae401382b3015689231ad988d3eb0521d414dcd2e8781053", - "0x183eb6bca59a141b4e8136179a258272ec9c25ec80bdb0458b6880c711707a28", - "0x03cd147a2a4c8dc272f3e240b8b0090d45e994e5fd40e07a54f6765795cd5ef8", - "0x082b135b3a20da4c766242b4258e27dbc050e4b8958bb15431626f2eeed9bd2b", - "0x28c894a6a719a32fe8d78ded46bc685ba035e5579c88fbc5bcbc0f09d8c5268b", - "0x06418cceff50837f923e63a37c2c534d13d9f59793c3aa6274813baa64d1899e", - "0x2b4a27b672f85c4fc697605da213de8b950a629602c5b8c6403e6c1c1065388a", - "0x0e2b817c6a79d6d1027f0376fb26ec81a140a4402e2dcdff6152cf01f2f4dbf9", - "0x2ae0fbce87dc53f0ff5473117e1c49a8197a14f8eaaec00cb5b10f94e844111f", - "0x2368004a1dee06f505e75ada3e9f8cc4c801f6a2068620da51ba11f537453835", - "0x2009df8e6f49f67dcaecb93e4a9ef81aaff096136d26f0fe691e14cd580c47da", - "0x2e512617136e8da2817856e57f13087a75fcc512faefc6d4b2eedd73c58a9b35", - "0x2848fcd535bd7c8017ca331a14919aa492ed05b04e9d0745480d291205eac8dc", - "0x19bb0990cb37f3a8f6c3db78219b07d6accd08e889586660e92dd6000755f09a", - "0x15520c8158b2e36c40c5fa46d5281c45d3df2c7f5d974a1f9549bfca6cbceaea", - "0x0e285f4df658d99922c286c5a253d6f6f37aa6c52d7a0fc1a20f3e6da9df23e1", - "0x0f9cd4667f4c1e86f83eda9e752a05c0cc630b0827a93a68322fa258dffb0f24", - "0x12d8b0dbbea3dccfe5d2dd090daf8ab4d2fac74fada9c49875b0c9122663a8ad", - "0x2e8c814d93f027ecff08c4e58555aadfc0f9ec3889eff2150f2b5bb6c557add0", - "0x013516a1456c5831aba87e4057878f6f3f18471e0674fd1e89be3e18351ec394", - "0x14418aa79dc84fd791d5638bdc103786ef8181a714ee8e022d3a1e792cbc7959", - "0x14418aa79dc84fd791d5638bdc103786ef8181a714ee8e022d3a1e792cbc7959", - "0x25c5e6c96a39bb36e19106d4049b675f0279084cc757c4e2acf6e497c61056a2", - "0x231aaafcf2a4c6fd8da18ce5ae5b33790f2c306a2692c6383c9a0787c50ac269", - "0x0a5f7665f0997081f9b38ec64e9a18542ac3a9648060f8cc720fc04669224730", - "0x0f1c9d9d1ac6f62825c6038117ed30540be434e8fd2d88150dcd4fece39b335a", - "0x1308871c8fcb09f07e5257f5cc5678d98842a8d18b2af09b5132d9af3cb1893e", - "0x28801985290dac4eba72ed01ee06fe88f6fc533dc1a46bd86e2d35be8021b042", - "0x14407f38cfba3cc61fca173b41133ab05a1c176caf8bb597588b01817e9eeaa3", - "0x0ea1a9f6f95f6193e512a7bd3db0c147f66687662934aed53cb657935b1e4eb9", - "0x1bc4ab6eacd61b5fd9e414b0186ef5deaadaf59aa9e53cb8d8812255baa28109", - "0x00000000000000000000000000000093a4da68a2fac0ee94841efdfc57eb748c", - "0x00000000000000000000000000000000001c22f1f5f927bee6adb649cc132391", - "0x0000000000000000000000000000003d0c2acea76c551f58876b3c35f19f345a", - "0x00000000000000000000000000000000002e94fded0a0b7f4fd1c882fd2a4e52", - "0x00000000000000000000000000000022e23b6fa0f72844bf8f60ea140cca5663", - "0x000000000000000000000000000000000013380f284bf3cb98b9a7cbae7d702b", - "0x000000000000000000000000000000942a13cf93056815c3f7439c9eed0a103e", - "0x00000000000000000000000000000000002be14bec02c6dae4625d32866de4fc", - "0x000000000000000000000000000000e2a2c75dc664c12695b4f7795c61f92669", - "0x000000000000000000000000000000000000725da448f376bde6cf63bcf79463", - "0x000000000000000000000000000000f54eee585f8ab367dc66a587e1d4cdbd8c", - "0x0000000000000000000000000000000000071106624ae5623a070f0addc18433", - "0x000000000000000000000000000000d60352bea3b2adb311b1a3beb25acb8aed", - "0x00000000000000000000000000000000001965b7c781e33f94e90c743c7881ed", - "0x0000000000000000000000000000006458a2aa57539e2b192f9c3ed69f9fb674", - "0x00000000000000000000000000000000001fc9c667723a4e66d752c6b426d444", - "0x0000000000000000000000000000008d1ff1c5d59a463c5b46bcf52f41ad3c63", - "0x00000000000000000000000000000000001b3e73df070a35c49a03fab1c76e9b", - "0x0000000000000000000000000000001c17a62b6c0a7ab14de83391e06f780adb", - "0x000000000000000000000000000000000012c7fbe2591b9ae72dd526e4ed1d7f", - "0x000000000000000000000000000000a758fa0c72d6a93155cb18b3fcc7defd34", - "0x00000000000000000000000000000000000cea12961770ce7cb6f2a4aed009fe", - "0x000000000000000000000000000000ef6e9647803aac315fa6d287e0e66f4767", - "0x0000000000000000000000000000000000259a82b8d6c6015cc51d2681f26ad4", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000008152b373c87004bef7d2c55ec8c540b67f", - "0x00000000000000000000000000000000000a55be5fdcb0a0dce4976d7bb78b0c", - "0x000000000000000000000000000000f749ea03f04ac964706139b9d1db595ecb", - "0x000000000000000000000000000000000013218e14dae80c066b4e46e9309fb2", - "0x0000000000000000000000000000004bbd7f950c36ce69db39e2b234a9e3f9b0", - "0x00000000000000000000000000000000002a0c3994d892ca5ea26984abbb30fb", - "0x0000000000000000000000000000006c1b39306846620bd546ac2c897834f259", - "0x000000000000000000000000000000000020350b9f507d6e25961a11be3e494b", -] -public_inputs = [ - "0x0000000000000000000000000000000000000000000000000000000000000003", -] -verification_key = [ - "0x0000000000000000000000000000000000000000000000000000000000000040", - "0x0000000000000000000000000000000000000000000000000000000000000011", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000003", - "0x0000000000000000000000000000000000000000000000000000000000000004", - "0x0000000000000000000000000000000000000000000000000000000000000005", - "0x0000000000000000000000000000000000000000000000000000000000000006", - "0x0000000000000000000000000000000000000000000000000000000000000007", - "0x0000000000000000000000000000000000000000000000000000000000000008", - "0x0000000000000000000000000000000000000000000000000000000000000009", - "0x000000000000000000000000000000000000000000000000000000000000000a", - "0x000000000000000000000000000000000000000000000000000000000000000b", - "0x000000000000000000000000000000000000000000000000000000000000000c", - "0x000000000000000000000000000000000000000000000000000000000000000d", - "0x000000000000000000000000000000000000000000000000000000000000000e", - "0x000000000000000000000000000000000000000000000000000000000000000f", - "0x0000000000000000000000000000000000000000000000000000000000000010", - "0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84", - "0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae", - "0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16", - "0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1", - "0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c", - "0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7", - "0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8", - "0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c", - "0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5", - "0x00000000000000000000000000000000002002681bb417184b2df070a16a3858", - "0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511", - "0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223", - "0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7", - "0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c", - "0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130", - "0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f", - "0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3", - "0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592", - "0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3", - "0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1", - "0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0", - "0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c", - "0x0000000000000000000000000000009f825dde88092070747180d581c342444a", - "0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01", - "0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff", - "0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9", - "0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1", - "0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b", - "0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2", - "0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f", - "0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0", - "0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349", - "0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8", - "0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2", - "0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556", - "0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d", - "0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb", - "0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d", - "0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8", - "0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862", - "0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e", - "0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830", - "0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f", - "0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe", - "0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb", - "0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56", - "0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc", - "0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4", - "0x00000000000000000000000000000029a17181c7934fc3fdbd352eac5cb521b9", - "0x00000000000000000000000000000000001f497cbf5284ff29a2d336e5991999", - "0x000000000000000000000000000000072bd9c0c6beda1fdee6d4ff0432ba9e1b", - "0x000000000000000000000000000000000013ea38a0bd2aa751a490a724fac818", - "0x000000000000000000000000000000c599f63dcd3edd49f08ae5c3141c1e3493", - "0x00000000000000000000000000000000002bdb36be0bea09950dd32a8ccf6fbc", - "0x00000000000000000000000000000047f27f29724e7f19eba0340256a0bd4b7d", - "0x00000000000000000000000000000000001c1c5ccf87a962129ca785f8f35120", - "0x000000000000000000000000000000c5c71efdae00679bbe4a95096e012b1817", - "0x000000000000000000000000000000000017a365de041e317817d0135f2b48e0", - "0x0000000000000000000000000000008ae711ac402f7848d719c93a89ba8d39f1", - "0x00000000000000000000000000000000002b6fb40ed8a1935226f4f9786a0499", - "0x0000000000000000000000000000002f03a71501d83de1da5715a4e9462d6198", - "0x00000000000000000000000000000000001644064443b8546f48eae693af47b8", - "0x00000000000000000000000000000083763ab1b6e8fe269b2fe4c7b9c448c08d", - "0x000000000000000000000000000000000021d7cc18c59676a8eeb47c0111c251", - "0x000000000000000000000000000000b5f937153073e03ea7d51a996e0ebc2e6b", - "0x000000000000000000000000000000000011ddd0e26457373eb06e0493177672", - "0x000000000000000000000000000000c5f6eb9f6fc8fa99811a4a88c74a6d018b", - "0x000000000000000000000000000000000025bcd07a0732c123567834f5109558", - "0x000000000000000000000000000000aeb08a0b1a4442189448b4e97490568146", - "0x000000000000000000000000000000000002a1744e4771705536a88f07e0f90f", - "0x000000000000000000000000000000b938568293bd0724b0ea76c2ec34c4a829", - "0x0000000000000000000000000000000000053296e8f3b9ad3af877dfa9c7c2a7", - "0x000000000000000000000000000000f0ca1db6323996eba26bdc86dafef9d10b", - "0x00000000000000000000000000000000001441a46c58af03d5645d52721d956a", - "0x0000000000000000000000000000008bbf8f884013c66c28ba09c2fbd573b656", - "0x0000000000000000000000000000000000206c391ca06fac27d1908e94570243", - "0x0000000000000000000000000000002d4f5aaed88ba4f79612d53b804ca8f194", - "0x00000000000000000000000000000000001674011c96392df08970fa6b7b4cb8", - "0x0000000000000000000000000000009f88297c1729d76c4d9306853598c91325", - "0x0000000000000000000000000000000000256f51adfcacc3c1e340be4d32d3e9", - "0x0000000000000000000000000000000ab9955eec0d74eb799afed2a802b24d75", - "0x00000000000000000000000000000000001fcbe43ea105b30d36ed0b21b03411", - "0x000000000000000000000000000000d66b1d5433f1aa5305cd1edce7c22de466", - "0x00000000000000000000000000000000002331546a256b8a3b751956806680d4", - "0x000000000000000000000000000000e97954ad6cd6f45fb15c91434121db4304", - "0x00000000000000000000000000000000002e20a97e09d50f227ced47e7a98250", - "0x0000000000000000000000000000001ebbc27eb9ebededefba79522eb58ae89b", - "0x0000000000000000000000000000000000090efa4974e566e81d1177b85a30be", - "0x0000000000000000000000000000005eafa070b9c9632404052642e3bc14f9fd", - "0x00000000000000000000000000000000001489068864102daca6a6b8bc4d448b", - "0x0000000000000000000000000000009ebc91aaaac036a6477cadbe54e8556dfd", - "0x00000000000000000000000000000000000ef6d835e2ed3343b95c82c8c54037", - "0x00000000000000000000000000000033b28b529dff46e93af4e7422530478e4a", - "0x000000000000000000000000000000000020a86c2f8591bf190bcddcc03c42fb", - "0x000000000000000000000000000000a9679d0acc088f7dc27bf6d866bcd2dda2", - "0x00000000000000000000000000000000002fb9d0d2d4099402bed74f738f64cc", - "0x00000000000000000000000000000023b09f876a29a061582848a8b9a5870c12", - "0x00000000000000000000000000000000001d5bb906f03f0d49e9c4791bc43af9", - "0x00000000000000000000000000000017aac9854ea240d8ec97bf760c4d4ba870", - "0x00000000000000000000000000000000000b227a556c414ada0dc75bb303e30e", - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000009b624fa65d1a24b7f14a8f25f3789622af", - "0x000000000000000000000000000000000013d47bff8c630e847b70e2732fd3f0", - "0x00000000000000000000000000000061d21663e93132f32921075f4c936a84df", - "0x00000000000000000000000000000000001a74ca4e118fb480b9b999902989a3", -] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr deleted file mode 100644 index b60a47ccc7f..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr +++ /dev/null @@ -1,17 +0,0 @@ - -// This circuit aggregates a single Honk proof from `assert_statement_recursive`. -global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 439; -global HONK_IDENTIFIER : u32 = 1; -fn main( - verification_key: [Field; 128], - // This is the proof without public inputs attached. - // - // This means: the size of this does not change with the number of public inputs. - proof: [Field; SIZE_OF_PROOF_IF_LOGN_IS_28], - public_inputs: pub [Field; 1], - // This is currently not public. It is fine given that the vk is a part of the circuit definition. - // I believe we want to eventually make it public too though. - key_hash: Field -) { - std::verify_proof(verification_key, proof, public_inputs, key_hash); -} 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 e5aa5f88a94..7b3e63df072 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 c082c1dde33..c1f70e7acee 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 d78da15a8ff..cce7f324a2e 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 87e7bea8c3b..96e339ee212 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 59758f4b972..de5c36bc59f 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, - 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}; @@ -15,11 +12,11 @@ use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, Completion use noirc_errors::{Location, Span}; use noirc_frontend::{ ast::{ - AsTraitPath, BlockExpression, CallExpression, ConstructorExpression, Expression, - ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, ItemVisibility, - Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, NoirFunction, - NoirStruct, NoirTraitImpl, Path, PathKind, Pattern, Statement, TypeImpl, UnresolvedGeneric, - UnresolvedGenerics, UnresolvedType, UseTree, UseTreeKind, Visitor, + AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression, + Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, + ItemVisibility, Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, + NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, Pattern, Statement, TypeImpl, + UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UseTree, UseTreeKind, Visitor, }, graph::{CrateId, Dependency}, hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}, @@ -27,6 +24,7 @@ use noirc_frontend::{ macros_api::{ModuleDefId, NodeInterner}, node_interner::ReferenceId, parser::{Item, ItemKind, ParsedSubModule}, + token::CustomAtrribute, ParsedModule, StructType, Type, }; use sort_text::underscore_sort_text; @@ -106,6 +104,7 @@ struct NodeFinder<'a> { nesting: usize, /// The line where an auto_import must be inserted auto_import_line: usize, + self_type: Option, } impl<'a> NodeFinder<'a> { @@ -147,6 +146,7 @@ impl<'a> NodeFinder<'a> { suggested_module_def_ids: HashSet::new(), nesting: 0, auto_import_line: 0, + self_type: None, } } @@ -180,19 +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); } - for (field, typ) in fields { - self.completion_items.push(struct_field_completion_item(field, typ)); + let self_prefix = false; + 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, + )); } } @@ -293,6 +297,7 @@ impl<'a> NodeFinder<'a> { &prefix, FunctionKind::Any, function_completion_kind, + false, // self_prefix ); return; } @@ -308,6 +313,7 @@ impl<'a> NodeFinder<'a> { &prefix, FunctionKind::Any, function_completion_kind, + false, // self_prefix ); return; } @@ -340,11 +346,21 @@ impl<'a> NodeFinder<'a> { self.local_variables_completion(&prefix); self.builtin_functions_completion(&prefix, function_completion_kind); self.builtin_values_completion(&prefix); + if let Some(self_type) = &self.self_type { + let self_prefix = true; + self.complete_type_fields_and_methods( + &self_type.clone(), + &prefix, + function_completion_kind, + self_prefix, + ); + } } RequestedItems::OnlyTypes => { self.builtin_types_completion(&prefix); self.type_parameters_completion(&prefix); } + RequestedItems::OnlyAttributeFunctions(..) => (), } self.complete_auto_imports(&prefix, requested_items, function_completion_kind); } @@ -492,6 +508,7 @@ impl<'a> NodeFinder<'a> { self.collect_local_variables(pattern); } } + Pattern::Interned(..) => (), } } @@ -518,16 +535,18 @@ impl<'a> NodeFinder<'a> { typ: &Type, prefix: &str, function_completion_kind: FunctionCompletionKind, + self_prefix: bool, ) { match typ { Type::Struct(struct_type, generics) => { - self.complete_struct_fields(&struct_type.borrow(), generics, prefix); + self.complete_struct_fields(&struct_type.borrow(), generics, prefix, self_prefix); } Type::MutableReference(typ) => { return self.complete_type_fields_and_methods( typ, prefix, function_completion_kind, + self_prefix, ); } Type::Alias(type_alias, _) => { @@ -536,10 +555,11 @@ impl<'a> NodeFinder<'a> { &type_alias.typ, prefix, function_completion_kind, + self_prefix, ); } Type::Tuple(types) => { - self.complete_tuple_fields(types); + self.complete_tuple_fields(types, self_prefix); } Type::FieldElement | Type::Array(_, _) @@ -565,6 +585,7 @@ impl<'a> NodeFinder<'a> { prefix, FunctionKind::SelfType(typ), function_completion_kind, + self_prefix, ); } @@ -574,6 +595,7 @@ impl<'a> NodeFinder<'a> { prefix: &str, function_kind: FunctionKind, function_completion_kind: FunctionCompletionKind, + self_prefix: bool, ) { let Some(methods_by_name) = self.interner.get_type_methods(typ) else { return; @@ -587,6 +609,8 @@ impl<'a> NodeFinder<'a> { func_id, function_completion_kind, function_kind, + None, // attribute first type + self_prefix, ) { self.completion_items.push(completion_item); self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(func_id)); @@ -603,6 +627,8 @@ impl<'a> NodeFinder<'a> { function_kind: FunctionKind, function_completion_kind: FunctionCompletionKind, ) { + let self_prefix = false; + for (name, func_id) in &trait_.method_ids { if name_matches(name, prefix) { if let Some(completion_item) = self.function_completion_item( @@ -610,6 +636,8 @@ impl<'a> NodeFinder<'a> { *func_id, function_completion_kind, function_kind, + None, // attribute first type + self_prefix, ) { self.completion_items.push(completion_item); self.suggested_module_def_ids.insert(ModuleDefId::FunctionId(*func_id)); @@ -623,17 +651,25 @@ impl<'a> NodeFinder<'a> { struct_type: &StructType, generics: &[Type], 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.completion_items.push(self.struct_field_completion_item( + name, + typ, + struct_type.id, + field_index, + self_prefix, + )); } } } - fn complete_tuple_fields(&mut self, types: &[Type]) { + fn complete_tuple_fields(&mut self, types: &[Type], self_prefix: bool) { for (index, typ) in types.iter().enumerate() { - self.completion_items.push(field_completion_item(&index.to_string(), typ.to_string())); + let name = index.to_string(); + self.completion_items.push(field_completion_item(&name, typ.to_string(), self_prefix)); } } @@ -717,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)); } } @@ -761,6 +800,66 @@ impl<'a> NodeFinder<'a> { None } + fn suggest_attributes(&mut self, prefix: &str, target: AttributeTarget) { + self.suggest_builtin_attributes(prefix, target); + + let function_completion_kind = FunctionCompletionKind::NameAndParameters; + let requested_items = RequestedItems::OnlyAttributeFunctions(target); + + self.complete_in_module( + self.module_id, + prefix, + PathKind::Plain, + true, + function_completion_kind, + requested_items, + ); + + self.complete_auto_imports(prefix, requested_items, function_completion_kind); + } + + fn suggest_no_arguments_attributes(&mut self, prefix: &str, attributes: &[&str]) { + for name in attributes { + if name_matches(name, prefix) { + self.completion_items.push(simple_completion_item( + *name, + CompletionItemKind::METHOD, + None, + )); + } + } + } + + fn suggest_one_argument_attributes(&mut self, prefix: &str, attributes: &[&str]) { + for name in attributes { + if name_matches(name, prefix) { + self.completion_items.push(snippet_completion_item( + format!("{}(…)", name), + CompletionItemKind::METHOD, + format!("{}(${{1:name}})", name), + None, + )); + } + } + } + + fn try_set_self_type(&mut self, pattern: &Pattern) { + match pattern { + Pattern::Identifier(ident) => { + if ident.0.contents == "self" { + let location = Location::new(ident.span(), self.file); + if let Some(ReferenceId::Local(definition_id)) = + self.interner.find_referenced(location) + { + self.self_type = Some(self.interner.definition_type(definition_id)); + } + } + } + Pattern::Mutable(pattern, ..) => self.try_set_self_type(pattern), + Pattern::Tuple(..) | Pattern::Struct(..) | Pattern::Interned(..) => (), + } + } + fn includes_span(&self, span: Span) -> bool { span.start() as usize <= self.byte_index && self.byte_index <= span.end() as usize } @@ -813,10 +912,15 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_noir_function(&mut self, noir_function: &NoirFunction, span: Span) -> bool { + for attribute in noir_function.secondary_attributes() { + attribute.accept(AttributeTarget::Function, self); + } + let old_type_parameters = self.type_parameters.clone(); self.collect_type_parameters_in_generics(&noir_function.def.generics); for param in &noir_function.def.parameters { + self.try_set_self_type(¶m.pattern); param.typ.accept(self); } @@ -830,6 +934,7 @@ impl<'a> Visitor for NodeFinder<'a> { noir_function.def.body.accept(Some(span), self); self.type_parameters = old_type_parameters; + self.self_type = None; false } @@ -842,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(); @@ -857,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 { @@ -871,11 +976,15 @@ impl<'a> Visitor for NodeFinder<'a> { } fn visit_noir_struct(&mut self, noir_struct: &NoirStruct, _: Span) -> bool { + for attribute in &noir_struct.attributes { + attribute.accept(AttributeTarget::Struct, self); + } + 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(); @@ -945,7 +1054,13 @@ impl<'a> Visitor for NodeFinder<'a> { if let Some(typ) = self.interner.type_at_location(location) { let typ = typ.follow_bindings(); let prefix = ""; - self.complete_type_fields_and_methods(&typ, prefix, FunctionCompletionKind::Name); + let self_prefix = false; + self.complete_type_fields_and_methods( + &typ, + prefix, + FunctionCompletionKind::Name, + self_prefix, + ); return false; } } @@ -973,7 +1088,13 @@ impl<'a> Visitor for NodeFinder<'a> { let offset = self.byte_index - method_call_expression.method_name.span().start() as usize; let prefix = prefix[0..offset].to_string(); - self.complete_type_fields_and_methods(&typ, &prefix, FunctionCompletionKind::Name); + let self_prefix = false; + self.complete_type_fields_and_methods( + &typ, + &prefix, + FunctionCompletionKind::Name, + self_prefix, + ); return false; } } @@ -1042,10 +1163,12 @@ impl<'a> Visitor for NodeFinder<'a> { { let typ = self.interner.definition_type(definition_id); let prefix = ""; + let self_prefix = false; self.complete_type_fields_and_methods( &typ, prefix, FunctionCompletionKind::NameAndParameters, + self_prefix, ); } } @@ -1072,10 +1195,12 @@ impl<'a> Visitor for NodeFinder<'a> { if let Some(typ) = self.interner.type_at_location(location) { let typ = typ.follow_bindings(); let prefix = ""; + let self_prefix = false; self.complete_type_fields_and_methods( &typ, prefix, FunctionCompletionKind::NameAndParameters, + self_prefix, ); } } @@ -1136,10 +1261,12 @@ impl<'a> Visitor for NodeFinder<'a> { if let Some(typ) = self.interner.type_at_location(location) { let typ = typ.follow_bindings(); let prefix = ident.to_string().to_case(Case::Snake); + let self_prefix = false; self.complete_type_fields_and_methods( &typ, &prefix, FunctionCompletionKind::NameAndParameters, + self_prefix, ); return false; } @@ -1201,6 +1328,14 @@ impl<'a> Visitor for NodeFinder<'a> { unresolved_types.accept(self); false } + + fn visit_custom_attribute(&mut self, attribute: &CustomAtrribute, target: AttributeTarget) { + if self.byte_index != attribute.contents_span.end() as usize { + return; + } + + self.suggest_attributes(&attribute.contents, target); + } } /// Returns true if name matches a prefix written in code. 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 54340075b15..520c158d260 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/builtins.rs @@ -1,5 +1,5 @@ use lsp_types::CompletionItemKind; -use noirc_frontend::token::Keyword; +use noirc_frontend::{ast::AttributeTarget, token::Keyword}; use strum::IntoEnumIterator; use super::{ @@ -84,6 +84,40 @@ impl<'a> NodeFinder<'a> { } } } + + pub(super) fn suggest_builtin_attributes(&mut self, prefix: &str, target: AttributeTarget) { + match target { + AttributeTarget::Module | AttributeTarget::Trait => (), + AttributeTarget::Struct => { + self.suggest_one_argument_attributes(prefix, &["abi"]); + } + AttributeTarget::Function => { + let no_arguments_attributes = &[ + "contract_library_method", + "deprecated", + "export", + "fold", + "no_predicates", + "recursive", + "test", + "varargs", + ]; + self.suggest_no_arguments_attributes(prefix, no_arguments_attributes); + + let one_argument_attributes = &["abi", "field", "foreign", "oracle"]; + self.suggest_one_argument_attributes(prefix, one_argument_attributes); + + if name_matches("test", prefix) || name_matches("should_fail_with", prefix) { + self.completion_items.push(snippet_completion_item( + "test(should_fail_with=\"...\")", + CompletionItemKind::METHOD, + "test(should_fail_with=\"${1:message}\")", + None, + )); + } + } + } + } } pub(super) fn builtin_integer_types() -> [&'static str; 8] { @@ -97,6 +131,7 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { Keyword::Expr => Some("Expr"), Keyword::Field => Some("Field"), Keyword::FunctionDefinition => Some("FunctionDefinition"), + Keyword::Module => Some("Module"), Keyword::Quoted => Some("Quoted"), Keyword::StructDefinition => Some("StructDefinition"), Keyword::TraitConstraint => Some("TraitConstraint"), @@ -128,7 +163,6 @@ pub(super) fn keyword_builtin_type(keyword: &Keyword) -> Option<&'static str> { | Keyword::In | Keyword::Let | Keyword::Mod - | Keyword::Module | Keyword::Mut | Keyword::Pub | Keyword::Return 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 21c3a607b18..7e7511cdaa3 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,14 @@ 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}, - Type, + macros_api::{ModuleDefId, StructId}, + node_interner::{FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, + QuotedType, Type, }; use super::{ @@ -33,34 +36,91 @@ impl<'a> NodeFinder<'a> { | ModuleDefId::TypeAliasId(_) | ModuleDefId::TraitId(_) => (), }, + RequestedItems::OnlyAttributeFunctions(..) => { + if !matches!(module_def_id, ModuleDefId::FunctionId(..)) { + return None; + } + } RequestedItems::AnyItems => (), } + let attribute_first_type = + if let RequestedItems::OnlyAttributeFunctions(target) = requested_items { + 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 { + None + }; + 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, function_completion_kind, function_kind, + 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) } - fn type_alias_completion_item(&self, name: String) -> CompletionItem { - simple_completion_item(name.clone(), CompletionItemKind::STRUCT, Some(name)) + 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 trait_completion_item(&self, name: String) -> CompletionItem { - simple_completion_item(name.clone(), CompletionItemKind::INTERFACE, Some(name)) + 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) + } + + 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, 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 { @@ -68,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( @@ -77,6 +139,8 @@ impl<'a> NodeFinder<'a> { func_id: FuncId, function_completion_kind: FunctionCompletionKind, function_kind: FunctionKind, + attribute_first_type: Option<&Type>, + self_prefix: bool, ) -> Option { let func_meta = self.interner.function_meta(&func_id); @@ -95,6 +159,17 @@ impl<'a> NodeFinder<'a> { None }; + if let Some(attribute_first_type) = attribute_first_type { + if func_meta.parameters.is_empty() { + return None; + } + + let (_, typ, _) = &func_meta.parameters.0[0]; + if typ != attribute_first_type { + return None; + } + } + match function_kind { FunctionKind::Any => (), FunctionKind::SelfType(mut self_type) => { @@ -135,6 +210,8 @@ impl<'a> NodeFinder<'a> { } else { false }; + let name = if self_prefix { format!("self.{}", name) } else { name.clone() }; + let name = &name; let description = func_meta_type_to_string(func_meta, func_self_type.is_some()); let completion_item = match function_completion_kind { @@ -143,11 +220,21 @@ impl<'a> NodeFinder<'a> { } FunctionCompletionKind::NameAndParameters => { let kind = CompletionItemKind::FUNCTION; - let insert_text = self.compute_function_insert_text(func_meta, name, function_kind); - let label = if insert_text.ends_with("()") { - format!("{}()", name) + let skip_first_argument = attribute_first_type.is_some(); + let insert_text = self.compute_function_insert_text( + func_meta, + name, + function_kind, + skip_first_argument, + ); + 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)) @@ -170,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) } @@ -179,13 +267,19 @@ impl<'a> NodeFinder<'a> { func_meta: &FuncMeta, name: &str, function_kind: FunctionKind, + skip_first_argument: bool, ) -> String { let mut text = String::new(); text.push_str(name); text.push('('); + let mut parameters = func_meta.parameters.0.iter(); + if skip_first_argument { + parameters.next(); + } + let mut index = 1; - for (pattern, _, _) in &func_meta.parameters.0 { + for (pattern, _, _) in parameters { if index == 1 { match function_kind { FunctionKind::SelfType(_) => { @@ -213,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) => { @@ -242,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 { @@ -294,12 +400,24 @@ fn type_to_self_string(typ: &Type, string: &mut String) { } } -pub(super) fn struct_field_completion_item(field: &str, typ: &Type) -> CompletionItem { - field_completion_item(field, typ.to_string()) +pub(super) fn struct_field_completion_item( + field: &str, + typ: &Type, + self_type: bool, +) -> CompletionItem { + field_completion_item(field, typ.to_string(), self_type) } -pub(super) fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { - simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) +pub(super) fn field_completion_item( + field: &str, + typ: impl Into, + self_type: bool, +) -> CompletionItem { + if self_type { + simple_completion_item(format!("self.{field}"), CompletionItemKind::FIELD, Some(typ.into())) + } else { + simple_completion_item(field, CompletionItemKind::FIELD, Some(typ.into())) + } } pub(super) fn simple_completion_item( diff --git a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs index 2fe039ba331..6fa74ffdb1a 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/kinds.rs @@ -1,4 +1,4 @@ -use noirc_frontend::Type; +use noirc_frontend::{ast::AttributeTarget, Type}; /// When suggest a function as a result of completion, whether to autocomplete its name or its name and parameters. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -27,4 +27,6 @@ pub(super) enum RequestedItems { AnyItems, // Only suggest types. OnlyTypes, + // Only attribute functions + OnlyAttributeFunctions(AttributeTarget), } 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 a7cfa77a73d..97cb145eb22 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/completion/tests.rs @@ -6,9 +6,8 @@ mod completion_tests { completion::{ completion_items::{ completion_item_with_sort_text, - completion_item_with_trigger_parameter_hints_command, crate_completion_item, - field_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}, }, @@ -116,6 +115,10 @@ mod completion_tests { )) } + fn field_completion_item(field: &str, typ: impl Into) -> CompletionItem { + crate::requests::completion::field_completion_item(field, typ, false) + } + #[test] async fn test_use_first_segment() { let src = r#" @@ -205,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#" @@ -281,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), ], ) @@ -1888,4 +1891,101 @@ mod completion_tests { Some("(use super::barbaz)".to_string()), ); } + + #[test] + async fn test_suggests_self_fields_and_methods() { + let src = r#" + struct Foo { + foobar: Field, + } + + impl Foo { + fn foobarbaz(self) {} + + fn some_method(self) { + foob>|< + } + } + "#; + + assert_completion_excluding_auto_import( + src, + vec![ + field_completion_item("self.foobar", "Field"), + function_completion_item("self.foobarbaz()", "self.foobarbaz()", "fn(self)"), + ], + ) + .await; + } + + #[test] + async fn test_suggests_built_in_function_attribute() { + let src = r#" + #[dep>|<] + fn foo() {} + "#; + + assert_completion_excluding_auto_import( + src, + vec![simple_completion_item("deprecated", CompletionItemKind::METHOD, None)], + ) + .await; + } + + #[test] + async fn test_suggests_function_attribute() { + let src = r#" + #[some>|<] + fn foo() {} + + fn some_attr(f: FunctionDefinition, x: Field) {} + fn some_other_function(x: Field) {} + "#; + + assert_completion_excluding_auto_import( + src, + vec![function_completion_item( + "some_attr(…)", + "some_attr(${1:x})", + "fn(FunctionDefinition, Field)", + )], + ) + .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 e06a3451681..7ab0a45ad0a 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 ae1e57f5ecc..dab6ddd0fc6 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 af58396550d..fea758e0e52 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 d3b7743557a..a7362300adc 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 5f4f9cd98d0..081eb815c8b 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 7203aca7f09..81910bebedb 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 39b2c7fff12..a88a6a21e1f 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 0795ffd9304..810a9edb7e1 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/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index 4dcfccdf085..7469c8be0f8 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -218,7 +218,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa &test_dir, &format!( r#" - nargo.arg("info").arg("--arithmetic-generics").arg("--json").arg("--force"); + nargo.arg("info").arg("--json").arg("--force"); {assert_zero_opcodes}"#, ), diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index 5239070b4d2..65a09dcdd11 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 85faf574a0a..ae96f6436e2 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 19add7f30dc..c3752db7fbd 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 8f66a0a328f..66496db517c 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 2f9390d72e0..8b3c0e29c7d 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 9e556e0fcbe..ba9a8214702 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()); diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index a5593cc284c..4ec715e27eb 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -41,7 +41,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "portal:../../../../barretenberg/ts", + "@aztec/bb.js": "0.51.1", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index c07d2d8a4c1..16fb26e55db 100755 --- a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh +++ b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index f77e9f7e72e..5d1d41f58fd 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,18 +221,19 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" +"@aztec/bb.js@npm:0.51.1": + version: 0.51.1 + resolution: "@aztec/bb.js@npm:0.51.1" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: ./dest/node/main.js + bb.js: dest/node/main.js + checksum: 246953d4d2becc86001b8e6d49ab8c0b4fa4833a9bf1d2177d0fb4e271e66f9dc65e3b02b69b00fbfcb935a11e2f90b5ac229b8c8938c34604fe54b29b3accfb languageName: node - linkType: soft + linkType: hard "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4160,7 +4161,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.51.1 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3