diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 973bfd1534b..664b993cd82 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -82f8cf5eba9deacdab43ad4ef95dbf27dd1c11c7 +b2df97907cb63446e9336e87f40f9dbd7a710845 diff --git a/Cargo.lock b/Cargo.lock index 3b7c1b6e56e..593347089ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,6 +439,7 @@ dependencies = [ "iter-extended", "noirc_errors", "noirc_frontend", + "regex", ] [[package]] @@ -2655,9 +2656,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -3822,14 +3823,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.3", - "regex-syntax 0.7.4", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", ] [[package]] @@ -3846,10 +3847,16 @@ name = "regex-automata" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.8.2", ] [[package]] @@ -3864,6 +3871,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "region" version = "3.0.0" diff --git a/aztec_macros/Cargo.toml b/aztec_macros/Cargo.toml index ed9821fabcf..355036d28a7 100644 --- a/aztec_macros/Cargo.toml +++ b/aztec_macros/Cargo.toml @@ -14,3 +14,5 @@ noirc_frontend.workspace = true noirc_errors.workspace = true iter-extended.workspace = true convert_case = "0.6.0" +regex = "1.10" + diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index e0100977eee..0fe450e6cb1 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -5,22 +5,27 @@ use transforms::{ compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, events::{generate_selector_impl, transform_events}, functions::{transform_function, transform_unconstrained, transform_vm_function}, + note_interface::generate_note_interface_impl, storage::{ assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, generate_storage_implementation, }, }; -use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; - -use noirc_frontend::macros_api::SortedModule; -use noirc_frontend::macros_api::{CrateId, MacroError}; -use noirc_frontend::macros_api::{FileId, MacroProcessor}; -use noirc_frontend::macros_api::{HirContext, SecondaryAttribute, Span}; +use noirc_frontend::{ + hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, + macros_api::{ + CrateId, FileId, HirContext, MacroError, MacroProcessor, SecondaryAttribute, SortedModule, + Span, + }, +}; -use utils::ast_utils::is_custom_attribute; -use utils::checks::{check_for_aztec_dependency, has_aztec_dependency}; -use utils::{constants::MAX_CONTRACT_PRIVATE_FUNCTIONS, errors::AztecMacroError}; +use utils::{ + ast_utils::is_custom_attribute, + checks::{check_for_aztec_dependency, has_aztec_dependency}, + constants::MAX_CONTRACT_PRIVATE_FUNCTIONS, + errors::AztecMacroError, +}; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -28,9 +33,10 @@ impl MacroProcessor for AztecMacro { &self, ast: SortedModule, crate_id: &CrateId, + file_id: FileId, context: &HirContext, ) -> Result { - transform(ast, crate_id, context) + transform(ast, crate_id, file_id, context) } fn process_collected_defs( @@ -61,38 +67,34 @@ impl MacroProcessor for AztecMacro { fn transform( mut ast: SortedModule, crate_id: &CrateId, + file_id: FileId, context: &HirContext, ) -> Result { // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&mut submodule.contents, crate_id, context) - .map_err(|(err, file_id)| (err.into(), file_id))? - { + if transform_module(&mut submodule.contents).map_err(|err| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; } } + + generate_note_interface_impl(&mut ast).map_err(|err| (err.into(), file_id))?; + Ok(ast) } /// Determines if ast nodes are annotated with aztec attributes. /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise -fn transform_module( - module: &mut SortedModule, - crate_id: &CrateId, - context: &HirContext, -) -> Result { +fn transform_module(module: &mut SortedModule) -> Result { let mut has_transformed_module = false; // Check for a user defined storage struct let storage_defined = check_for_storage_definition(module); let storage_implemented = check_for_storage_implementation(module); - let crate_graph = &context.crate_graph[crate_id]; - if storage_defined && !storage_implemented { - generate_storage_implementation(module).map_err(|err| (err, crate_graph.root_file_id))?; + generate_storage_implementation(module)?; } for structure in module.types.iter() { @@ -144,12 +146,10 @@ fn transform_module( is_initializer, insert_init_check, is_internal, - ) - .map_err(|err| (err, crate_graph.root_file_id))?; + )?; has_transformed_module = true; } else if is_public_vm { - transform_vm_function(func, storage_defined) - .map_err(|err| (err, crate_graph.root_file_id))?; + transform_vm_function(func, storage_defined)?; has_transformed_module = true; } else if storage_defined && func.def.is_unconstrained { transform_unconstrained(func); @@ -173,11 +173,9 @@ fn transform_module( .count(); if private_functions_count > MAX_CONTRACT_PRIVATE_FUNCTIONS { - let crate_graph = &context.crate_graph[crate_id]; - return Err(( - AztecMacroError::ContractHasTooManyPrivateFunctions { span: Span::default() }, - crate_graph.root_file_id, - )); + return Err(AztecMacroError::ContractHasTooManyPrivateFunctions { + span: Span::default(), + }); } } diff --git a/aztec_macros/src/transforms/mod.rs b/aztec_macros/src/transforms/mod.rs index 144ffc3efc3..5a454c75148 100644 --- a/aztec_macros/src/transforms/mod.rs +++ b/aztec_macros/src/transforms/mod.rs @@ -1,4 +1,5 @@ pub mod compute_note_hash_and_nullifier; pub mod events; pub mod functions; +pub mod note_interface; pub mod storage; diff --git a/aztec_macros/src/transforms/note_interface.rs b/aztec_macros/src/transforms/note_interface.rs new file mode 100644 index 00000000000..01d0272088b --- /dev/null +++ b/aztec_macros/src/transforms/note_interface.rs @@ -0,0 +1,583 @@ +use noirc_errors::Span; +use noirc_frontend::{ + parse_program, parser::SortedModule, ItemVisibility, NoirFunction, NoirStruct, PathKind, + TraitImplItem, TypeImpl, UnresolvedTypeData, UnresolvedTypeExpression, +}; +use regex::Regex; + +use crate::{ + chained_dep, + utils::{ + ast_utils::{ + check_trait_method_implemented, ident, ident_path, is_custom_attribute, make_type, + }, + errors::AztecMacroError, + }, +}; + +// Automatic implementation of most of the methods in the NoteInterface trait, guiding the user with meaningful error messages in case some +// methods must be implemented manually. +pub fn generate_note_interface_impl(module: &mut SortedModule) -> 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 mut note_properties_structs = vec![]; + + for note_struct in annotated_note_structs { + // Look for the NoteInterface trait implementation for the note + let trait_impl = module + .trait_impls + .iter_mut() + .find(|trait_impl| { + if let UnresolvedTypeData::Named(struct_path, _, _) = &trait_impl.object_type.typ { + struct_path.last_segment() == note_struct.name + && trait_impl.trait_name.last_segment().0.contents == "NoteInterface" + } else { + false + } + }) + .ok_or(AztecMacroError::CouldNotImplementNoteInterface { + span: Some(note_struct.name.span()), + secondary_message: Some(format!( + "Could not find NoteInterface trait implementation for note: {}", + note_struct.name.0.contents + )), + })?; + let note_interface_impl_span: Option = trait_impl.object_type.span; + // Look for the note struct implementation, generate a default one if it doesn't exist (in order to append methods to it) + let existing_impl = module.impls.iter_mut().find(|r#impl| match &r#impl.object_type.typ { + UnresolvedTypeData::Named(path, _, _) => path.last_segment().eq(¬e_struct.name), + _ => false, + }); + let note_impl = if let Some(note_impl) = existing_impl { + note_impl + } else { + let default_impl = TypeImpl { + object_type: trait_impl.object_type.clone(), + type_span: note_struct.name.span(), + generics: vec![], + methods: vec![], + }; + module.impls.push(default_impl.clone()); + module.impls.last_mut().unwrap() + }; + // Identify the note type (struct name), its fields and its serialized length (generic param of NoteInterface trait impl) + let note_type = note_struct.name.0.contents.to_string(); + let mut note_fields = vec![]; + let note_serialized_len = match &trait_impl.trait_generics[0].typ { + UnresolvedTypeData::Named(path, _, _) => Ok(path.last_segment().0.contents.to_string()), + UnresolvedTypeData::Expression(UnresolvedTypeExpression::Constant(val, _)) => { + Ok(val.to_string()) + } + _ => Err(AztecMacroError::CouldNotImplementNoteInterface { + span: trait_impl.object_type.span, + secondary_message: Some(format!( + "Cannot find note serialization length for: {}", + note_type + )), + }), + }?; + + // 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 { + UnresolvedTypeData::Named(path, _, _) => { + path.last_segment().0.contents == "NoteHeader" + } + _ => false, + }) { + existing_header.clone() + } else { + let generated_header = ( + ident("header"), + make_type(UnresolvedTypeData::Named( + chained_dep!("aztec", "note", "note_header", "NoteHeader"), + vec![], + false, + )), + ); + note_struct.fields.push(generated_header.clone()); + generated_header + }; + + for (field_ident, field_type) in note_struct.fields.iter() { + note_fields.push(( + field_ident.0.contents.to_string(), + field_type.typ.to_string().replace("plain::", ""), + )); + } + + 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") + { + let note_serialize_content_fn = generate_note_serialize_content( + ¬e_type, + ¬e_fields, + ¬e_serialized_len, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(note_serialize_content_fn)); + + let note_deserialize_content_fn = generate_note_deserialize_content( + ¬e_type, + ¬e_fields, + ¬e_serialized_len, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(note_deserialize_content_fn)); + + let note_properties_struct = generate_note_properties_struct( + ¬e_type, + ¬e_fields, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + note_properties_structs.push(note_properties_struct); + let note_properties_fn = generate_note_properties_fn( + ¬e_type, + ¬e_fields, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + note_impl.methods.push((note_properties_fn, note_impl.type_span)); + } + + if !check_trait_method_implemented(trait_impl, "get_header") { + let get_header_fn = generate_note_get_header( + ¬e_type, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + } + if !check_trait_method_implemented(trait_impl, "set_header") { + let set_header_fn = generate_note_set_header( + ¬e_type, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(set_header_fn)); + } + + if !check_trait_method_implemented(trait_impl, "get_note_type_id") { + let get_note_type_id_fn = + generate_note_get_type_id(¬e_type, note_interface_impl_span)?; + trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); + } + + if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { + let get_header_fn = + generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + } + } + + module.types.extend(note_properties_structs); + Ok(()) +} + +fn generate_note_get_header( + note_type: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn get_header(note: {}) -> dep::aztec::note::note_header::NoteHeader {{ + note.{} + }} + ", + note_type, note_header_field_name + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn get_header). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +fn generate_note_set_header( + note_type: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn set_header(self: &mut {}, header: dep::aztec::note::note_header::NoteHeader) {{ + self.{} = header; + }} + ", + note_type, note_header_field_name + ); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn set_header). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate the note type id getter method. The id itself its calculated as the concatenation +// of the conversion of the characters in the note's struct name to unsigned integers. +fn generate_note_get_type_id( + note_type: &str, + impl_span: Option, +) -> Result { + // TODO(#4519) Improve automatic note id generation and assignment + let note_id = + note_type.chars().map(|c| (c as u32).to_string()).collect::>().join(""); + let function_source = format!( + " + fn get_note_type_id() -> Field {{ + {} + }} + ", + note_id + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn get_note_type_id). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate a struct that represents the note's serialization metadata, as +// +// NoteTypeFields { +// field1: PropertySelector { index: 0, offset: 0, length: 32 }, +// field2: PropertySelector { index: 1, offset: 0, length: 32 }, +// ... +// } +// +// It assumes each field occupies an entire field and its serialized in definition order +fn generate_note_properties_struct( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let struct_source = + generate_note_properties_struct_source(note_type, note_fields, note_header_field_name); + + let (struct_ast, errors) = parse_program(&struct_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (struct {}Properties). This is either a bug in the compiler or the Noir macro code", note_type)), + span: impl_span + }); + } + + let mut struct_ast = struct_ast.into_sorted(); + Ok(struct_ast.types.remove(0)) +} + +// Generate the deserialize_content method as +// +// fn deserialize_content(serialized_note: [Field; NOTE_SERILIZED_LEN]) -> Self { +// NoteType { +// note_field1: serialized_note[0] as Field, +// note_field2: NoteFieldType2::from_field(serialized_note[1])... +// } +// } +// It assumes every note field is stored in an individual serialized field, +// and can be converted to the original type via the from_field() trait (structs) or cast as Field (integers) +fn generate_note_deserialize_content( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = generate_note_deserialize_content_source( + note_type, + note_fields, + note_serialize_len, + note_header_field_name, + ); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn deserialize_content). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Generate the serialize_content method as +// +// fn serialize_content(self: {}) -> [Field; NOTE_SERIALIZED_LEN] { +// [self.note_field1 as Field, self.note_field2.to_field()...] +// } +// +// It assumes every struct field can be converted either via the to_field() trait (structs) or cast as Field (integers) +fn generate_note_serialize_content( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = generate_note_serialize_content_source( + note_type, + note_fields, + note_serialize_len, + note_header_field_name, + ); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn serialize_content). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate a function in the Note's impl that returns the note's fields metadata +fn generate_note_properties_fn( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = + generate_note_properties_fn_source(note_type, note_fields, note_header_field_name); + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn properties). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate the method to compute the note's content hash as: +// fn compute_note_content_hash(self: NoteType) -> Field { +// // TODO(#1205) Should use a non-zero generator index. +// dep::aztec::hash::pedersen_hash(self.serialize_content(), 0) +// } +// +fn generate_compute_note_content_hash( + note_type: &String, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn compute_note_content_hash(self: {}) -> Field {{ + // TODO(#1205) Should use a non-zero generator index. + dep::aztec::hash::pedersen_hash(self.serialize_content(), 0) + }} + ", + note_type + ); + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn compute_note_content_hash). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Source code generator functions. These utility methods produce Noir code as strings, that are then parsed and added to the AST. + +fn generate_note_properties_struct_source( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, +) -> String { + let note_property_selectors = note_fields + .iter() + .filter_map(|(field_name, _)| { + if field_name != note_header_field_name { + Some(format!( + "{}: dep::aztec::note::note_getter_options::PropertySelector", + field_name + )) + } else { + None + } + }) + .collect::>() + .join(",\n"); + format!( + " + struct {}Properties {{ + {} + }}", + note_type, note_property_selectors + ) + .to_string() +} + +fn generate_note_properties_fn_source( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, +) -> String { + let note_property_selectors = note_fields + .iter() + .enumerate() + .filter_map(|(index, (field_name, _))| { + if field_name != note_header_field_name { + Some(format!( + "{}: dep::aztec::note::note_getter_options::PropertySelector {{ index: {}, offset: 0, length: 32 }}", + field_name, + index + )) + } else { + None + } + }) + .collect::>() + .join(", "); + format!( + " + pub fn properties() -> {}Properties {{ + {}Properties {{ + {} + }} + }}", + note_type, note_type, note_property_selectors + ) + .to_string() +} + +fn generate_note_serialize_content_source( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, +) -> String { + let note_fields = note_fields + .iter() + .filter_map(|(field_name, field_type)| { + if field_name != note_header_field_name { + if field_type == "Field" { + Some(format!("self.{}", field_name)) + } else { + Some(format!("self.{}.to_field()", field_name)) + } + } else { + None + } + }) + .collect::>() + .join(", "); + format!( + " + fn serialize_content(self: {}) -> [Field; {}] {{ + [{}] + }}", + note_type, note_serialize_len, note_fields + ) + .to_string() +} + +fn generate_note_deserialize_content_source( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, +) -> String { + let note_fields = note_fields + .iter() + .enumerate() + .map(|(index, (field_name, field_type))| { + if field_name != note_header_field_name { + // TODO: Simplify this when https://github.com/noir-lang/noir/issues/4463 is fixed + if field_type.eq("Field") + || Regex::new(r"u[0-9]+").unwrap().is_match(field_type) + || field_type.eq("bool") + { + format!("{}: serialized_note[{}] as {},", field_name, index, field_type) + } else { + format!( + "{}: {}::from_field(serialized_note[{}]),", + field_name, field_type, index + ) + } + } else { + format!( + "{}: dep::aztec::note::note_header::NoteHeader::empty()", + note_header_field_name + ) + } + }) + .collect::>() + .join("\n"); + format!( + " + fn deserialize_content(serialized_note: [Field; {}]) -> Self {{ + {} {{ + {} + }} + }}", + note_serialize_len, note_type, note_fields + ) + .to_string() +} diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 71c6a93f388..bdcbad646c2 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -2,8 +2,9 @@ use noirc_errors::{Span, Spanned}; use noirc_frontend::{ token::SecondaryAttribute, BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, - LetStatement, MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, - Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, + LetStatement, MemberAccessExpression, MethodCallExpression, NoirTraitImpl, Path, Pattern, + PrefixExpression, Statement, StatementKind, TraitImplItem, UnaryOp, UnresolvedType, + UnresolvedTypeData, }; // @@ -173,6 +174,13 @@ pub fn index_array_variable(array: Expression, index: &str) -> Expression { }))) } +pub fn check_trait_method_implemented(trait_impl: &NoirTraitImpl, method_name: &str) -> bool { + trait_impl.items.iter().any(|item| match item { + TraitImplItem::Function(func) => func.def.name.0.contents == method_name, + _ => false, + }) +} + /// Checks if an attribute is a custom attribute with a specific name pub fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool { if let SecondaryAttribute::Custom(custom_attr) = attr { diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 199473baec6..48186555eff 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -10,7 +10,7 @@ pub enum AztecMacroError { UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, - CouldNotImplementNoteSerialization { span: Option, typ: UnresolvedTypeData }, + CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, EventError { span: Span, message: String }, UnsupportedAttributes { span: Span, secondary_message: Option }, } @@ -43,9 +43,9 @@ impl From for MacroError { secondary_message, span: None, }, - AztecMacroError::CouldNotImplementNoteSerialization { span, typ } => MacroError { - primary_message: format!("Could not implement serialization methods for note `{typ:?}`, please provide a serialize_content and deserialize_content methods"), - secondary_message: None, + AztecMacroError::CouldNotImplementNoteInterface { span, secondary_message } => MacroError { + primary_message: "Could not implement automatic methods for note, please provide an implementation of the NoteInterface trait".to_string(), + secondary_message, span, }, AztecMacroError::EventError { span, message } => MacroError { @@ -53,7 +53,7 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, -AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { + AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { primary_message: "Unsupported attributes in contract function".to_string(), secondary_message, span: Some(span), diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 06259e06248..40f4336e0b5 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -48,8 +48,6 @@ pub enum RuntimeError { BigIntModulus { call_stack: CallStack }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, - #[error("All `oracle` methods should be wrapped in an unconstrained fn")] - UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, } // We avoid showing the actual lhs and rhs since most of the time they are just 0 @@ -141,7 +139,6 @@ impl RuntimeError { | RuntimeError::NestedSlice { call_stack, .. } | RuntimeError::BigIntModulus { call_stack, .. } | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } => call_stack, - RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } => call_stack, } } } diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 808cf7533c9..56cb76adbe4 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -48,7 +48,6 @@ pub(crate) fn optimize_into_acir( let ssa_gen_span_guard = ssa_gen_span.enter(); let ssa = SsaBuilder::new(program, print_ssa_passes, force_brillig_output)? .run_pass(Ssa::defunctionalize, "After Defunctionalization:") - .run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:") .run_pass(Ssa::inline_functions, "After Inlining:") // Run mem2reg with the CFG separated into blocks .run_pass(Ssa::mem2reg, "After Mem2Reg:") @@ -60,7 +59,10 @@ pub(crate) fn optimize_into_acir( // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") - .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") + .run_pass( + Ssa::fold_constants_using_constraints, + "After Constant Folding With Constraint Info:", + ) .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") .finish(); diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 8390f480e3a..5a4fa021f1f 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -612,11 +612,9 @@ impl Context { self.ssa_values.insert(*result, output); } } - Value::ForeignFunction(_) => { - return Err(RuntimeError::UnconstrainedOracleReturnToConstrained { - call_stack: self.acir_context.get_call_stack(), - }) - } + Value::ForeignFunction(_) => unreachable!( + "All `oracle` methods should be wrapped in an unconstrained fn" + ), _ => unreachable!("expected calling a function but got {function_value:?}"), } } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index aa5a7fedd92..2c39c83b342 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -195,9 +195,12 @@ impl FunctionBuilder { self.call_stack.clone() } - /// Insert a Load instruction at the end of the current block, loading from the given address - /// which should point to a previous Allocate instruction. Note that this is limited to loading - /// a single value. Loading multiple values (such as a tuple) will require multiple loads. + /// Insert a Load instruction at the end of the current block, loading from the given offset + /// of the given address which should point to a previous Allocate instruction. Note that + /// this is limited to loading a single value. Loading multiple values (such as a tuple) + /// will require multiple loads. + /// 'offset' is in units of FieldElements here. So loading the fourth FieldElement stored in + /// an array will have an offset of 3. /// Returns the element that was loaded. pub(crate) fn insert_load(&mut self, address: ValueId, type_to_load: Type) -> ValueId { self.insert_instruction(Instruction::Load { address }, Some(vec![type_to_load])).first() @@ -218,9 +221,11 @@ impl FunctionBuilder { operator: BinaryOp, rhs: ValueId, ) -> ValueId { - let lhs_type = self.type_of_value(lhs); - let rhs_type = self.type_of_value(rhs); - assert_eq!(lhs_type, rhs_type, "ICE - Binary instruction operands must have the same type"); + assert_eq!( + self.type_of_value(lhs), + self.type_of_value(rhs), + "ICE - Binary instruction operands must have the same type" + ); let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); self.insert_instruction(instruction, None).first() } @@ -304,18 +309,6 @@ impl FunctionBuilder { self.insert_instruction(Instruction::ArraySet { array, index, value }, None).first() } - /// Insert an instruction to increment an array's reference count. This only has an effect - /// in unconstrained code where arrays are reference counted and copy on write. - pub(crate) fn insert_inc_rc(&mut self, value: ValueId) { - self.insert_instruction(Instruction::IncrementRc { value }, None); - } - - /// Insert an instruction to decrement an array's reference count. This only has an effect - /// in unconstrained code where arrays are reference counted and copy on write. - pub(crate) fn insert_dec_rc(&mut self, value: ValueId) { - self.insert_instruction(Instruction::DecrementRc { value }, None); - } - /// Terminates the current block with the given terminator instruction fn terminate_block_with(&mut self, terminator: TerminatorInstruction) { self.current_function.dfg.set_block_terminator(self.current_block, terminator); @@ -391,65 +384,51 @@ impl FunctionBuilder { /// within the given value. If the given value is not an array and does not contain /// any arrays, this does nothing. pub(crate) fn increment_array_reference_count(&mut self, value: ValueId) { - self.update_array_reference_count(value, true, None); + self.update_array_reference_count(value, true); } /// Insert instructions to decrement the reference count of any array(s) stored /// within the given value. If the given value is not an array and does not contain /// any arrays, this does nothing. pub(crate) fn decrement_array_reference_count(&mut self, value: ValueId) { - self.update_array_reference_count(value, false, None); + self.update_array_reference_count(value, false); } /// Increment or decrement the given value's reference count if it is an array. /// If it is not an array, this does nothing. Note that inc_rc and dec_rc instructions /// are ignored outside of unconstrained code. - fn update_array_reference_count( - &mut self, - value: ValueId, - increment: bool, - load_address: Option, - ) { + pub(crate) fn update_array_reference_count(&mut self, value: ValueId, increment: bool) { match self.type_of_value(value) { Type::Numeric(_) => (), Type::Function => (), Type::Reference(element) => { if element.contains_an_array() { - let reference = value; - let value = self.insert_load(reference, element.as_ref().clone()); - self.update_array_reference_count(value, increment, Some(reference)); + let value = self.insert_load(value, element.as_ref().clone()); + self.increment_array_reference_count(value); } } typ @ Type::Array(..) | typ @ Type::Slice(..) => { // If there are nested arrays or slices, we wait until ArrayGet // is issued to increment the count of that array. - let update_rc = |this: &mut Self, value| { - if increment { - this.insert_inc_rc(value); - } else { - this.insert_dec_rc(value); - } + let instruction = if increment { + Instruction::IncrementRc { value } + } else { + Instruction::DecrementRc { value } }; - - update_rc(self, value); - let dfg = &self.current_function.dfg; + self.insert_instruction(instruction, None); // This is a bit odd, but in brillig the inc_rc instruction operates on // a copy of the array's metadata, so we need to re-store a loaded array // even if there have been no other changes to it. - if let Some(address) = load_address { - // If we already have a load from the Type::Reference case, avoid inserting - // another load and rc update. - self.insert_store(address, value); - } else if let Value::Instruction { instruction, .. } = &dfg[value] { - let instruction = &dfg[*instruction]; + if let Value::Instruction { instruction, .. } = &self.current_function.dfg[value] { + let instruction = &self.current_function.dfg[*instruction]; if let Instruction::Load { address } = instruction { // We can't re-use `value` in case the original address was stored // to again in the meantime. So introduce another load. let address = *address; - let new_load = self.insert_load(address, typ); - update_rc(self, new_load); - self.insert_store(address, new_load); + let value = self.insert_load(address, typ); + self.insert_instruction(Instruction::IncrementRc { value }, None); + self.insert_store(address, value); } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 8f98b3fb17f..a315695f7db 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -12,7 +12,6 @@ mod die; pub(crate) mod flatten_cfg; mod inlining; mod mem2reg; -mod rc; mod remove_bit_shifts; mod simplify_cfg; mod unrolling; diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs deleted file mode 100644 index 4766bc3e8d2..00000000000 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ /dev/null @@ -1,327 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use crate::ssa::{ - ir::{ - basic_block::BasicBlockId, - function::Function, - instruction::{Instruction, InstructionId, TerminatorInstruction}, - types::Type, - value::ValueId, - }, - ssa_gen::Ssa, -}; - -impl Ssa { - /// This pass removes `inc_rc` and `dec_rc` instructions - /// as long as there are no `array_set` instructions to an array - /// of the same type in between. - /// - /// Note that this pass is very conservative since the array_set - /// instruction does not need to be to the same array. This is because - /// the given array may alias another array (e.g. function parameters or - /// a `load`ed array from a reference). - #[tracing::instrument(level = "trace", skip(self))] - pub(crate) fn remove_paired_rc(mut self) -> Ssa { - for function in self.functions.values_mut() { - remove_paired_rc(function); - } - self - } -} - -#[derive(Default)] -struct Context { - // All inc_rc instructions encountered without a corresponding dec_rc. - // These are only searched for in the first block of a function. - // - // The type of the array being operated on is recorded. - // If an array_set to that array type is encountered, that is also recorded. - inc_rcs: HashMap>, -} - -struct IncRc { - id: InstructionId, - array: ValueId, - possibly_mutated: bool, -} - -/// This function is very simplistic for now. It takes advantage of the fact that dec_rc -/// instructions are currently issued only at the end of a function for parameters and will -/// only check the first and last block for inc & dec rc instructions to be removed. The rest -/// of the function is still checked for array_set instructions. -/// -/// This restriction lets this function largely ignore merging intermediate results from other -/// blocks and handling loops. -fn remove_paired_rc(function: &mut Function) { - // `dec_rc` is only issued for parameters currently so we can speed things - // up a bit by skipping any functions without them. - if !contains_array_parameter(function) { - return; - } - - let mut context = Context::default(); - - context.find_rcs_in_entry_block(function); - context.scan_for_array_sets(function); - let to_remove = context.find_rcs_to_remove(function); - remove_instructions(to_remove, function); -} - -fn contains_array_parameter(function: &mut Function) -> bool { - let mut parameters = function.parameters().iter(); - parameters.any(|parameter| function.dfg.type_of_value(*parameter).contains_an_array()) -} - -impl Context { - fn find_rcs_in_entry_block(&mut self, function: &Function) { - let entry = function.entry_block(); - - for instruction in function.dfg[entry].instructions() { - if let Instruction::IncrementRc { value } = &function.dfg[*instruction] { - let typ = function.dfg.type_of_value(*value); - - // We assume arrays aren't mutated until we find an array_set - let inc_rc = IncRc { id: *instruction, array: *value, possibly_mutated: false }; - self.inc_rcs.entry(typ).or_default().push(inc_rc); - } - } - } - - /// Find each array_set instruction in the function and mark any arrays used - /// by the inc_rc instructions as possibly mutated if they're the same type. - fn scan_for_array_sets(&mut self, function: &Function) { - for block in function.reachable_blocks() { - for instruction in function.dfg[block].instructions() { - if let Instruction::ArraySet { array, .. } = function.dfg[*instruction] { - let typ = function.dfg.type_of_value(array); - if let Some(inc_rcs) = self.inc_rcs.get_mut(&typ) { - for inc_rc in inc_rcs { - inc_rc.possibly_mutated = true; - } - } - } - } - } - } - - /// Find each dec_rc instruction and if the most recent inc_rc instruction for the same value - /// is not possibly mutated, then we can remove them both. Returns each such pair. - fn find_rcs_to_remove(&mut self, function: &Function) -> HashSet { - let last_block = Self::find_last_block(function); - let mut to_remove = HashSet::new(); - - for instruction in function.dfg[last_block].instructions() { - if let Instruction::DecrementRc { value } = &function.dfg[*instruction] { - if let Some(inc_rc) = self.pop_rc_for(*value, function) { - if !inc_rc.possibly_mutated { - to_remove.insert(inc_rc.id); - to_remove.insert(*instruction); - } - } - } - } - - to_remove - } - - /// Finds the block of the function with the Return instruction - fn find_last_block(function: &Function) -> BasicBlockId { - for block in function.reachable_blocks() { - if matches!( - function.dfg[block].terminator(), - Some(TerminatorInstruction::Return { .. }) - ) { - return block; - } - } - - unreachable!("SSA Function {} has no reachable return instruction!", function.id()) - } - - /// Finds and pops the IncRc for the given array value if possible. - fn pop_rc_for(&mut self, value: ValueId, function: &Function) -> Option { - let typ = function.dfg.type_of_value(value); - - let rcs = self.inc_rcs.get_mut(&typ)?; - let position = rcs.iter().position(|inc_rc| inc_rc.array == value)?; - - Some(rcs.remove(position)) - } -} - -fn remove_instructions(to_remove: HashSet, function: &mut Function) { - if !to_remove.is_empty() { - for block in function.reachable_blocks() { - function.dfg[block] - .instructions_mut() - .retain(|instruction| !to_remove.contains(instruction)); - } - } -} - -#[cfg(test)] -mod test { - use std::rc::Rc; - - use crate::ssa::{ - function_builder::FunctionBuilder, - ir::{ - basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::Instruction, map::Id, types::Type, - }, - }; - - fn count_inc_rcs(block: BasicBlockId, dfg: &DataFlowGraph) -> usize { - dfg[block] - .instructions() - .iter() - .filter(|instruction_id| { - matches!(dfg[**instruction_id], Instruction::IncrementRc { .. }) - }) - .count() - } - - fn count_dec_rcs(block: BasicBlockId, dfg: &DataFlowGraph) -> usize { - dfg[block] - .instructions() - .iter() - .filter(|instruction_id| { - matches!(dfg[**instruction_id], Instruction::DecrementRc { .. }) - }) - .count() - } - - #[test] - fn single_block_fn_return_array() { - // This is the output for the program with a function: - // unconstrained fn foo(x: [Field; 2]) -> [[Field; 2]; 1] { - // [array] - // } - // - // fn foo { - // b0(v0: [Field; 2]): - // inc_rc v0 - // inc_rc v0 - // dec_rc v0 - // return [v0] - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("foo".into(), main_id, RuntimeType::Brillig); - - let inner_array_type = Type::Array(Rc::new(vec![Type::field()]), 2); - let v0 = builder.add_parameter(inner_array_type.clone()); - - builder.insert_inc_rc(v0); - builder.insert_inc_rc(v0); - builder.insert_dec_rc(v0); - - let outer_array_type = Type::Array(Rc::new(vec![inner_array_type]), 1); - let array = builder.array_constant(vec![v0].into(), outer_array_type); - builder.terminate_with_return(vec![array]); - - let ssa = builder.finish().remove_paired_rc(); - let main = ssa.main(); - let entry = main.entry_block(); - - assert_eq!(count_inc_rcs(entry, &main.dfg), 1); - assert_eq!(count_dec_rcs(entry, &main.dfg), 0); - } - - #[test] - fn single_block_mutation() { - // fn mutator(mut array: [Field; 2]) { - // array[0] = 5; - // } - // - // fn mutator { - // b0(v0: [Field; 2]): - // v1 = allocate - // store v0 at v1 - // inc_rc v0 - // v2 = load v1 - // v7 = array_set v2, index u64 0, value Field 5 - // store v7 at v1 - // dec_rc v0 - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("mutator".into(), main_id, RuntimeType::Acir); - - let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); - let v0 = builder.add_parameter(array_type.clone()); - - let v1 = builder.insert_allocate(array_type.clone()); - builder.insert_store(v1, v0); - builder.insert_inc_rc(v0); - let v2 = builder.insert_load(v1, array_type); - - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); - let five = builder.field_constant(5u128); - let v7 = builder.insert_array_set(v2, zero, five); - - builder.insert_store(v1, v7); - builder.insert_dec_rc(v0); - builder.terminate_with_return(vec![]); - - let ssa = builder.finish().remove_paired_rc(); - let main = ssa.main(); - let entry = main.entry_block(); - - // No changes, the array is possibly mutated - assert_eq!(count_inc_rcs(entry, &main.dfg), 1); - assert_eq!(count_dec_rcs(entry, &main.dfg), 1); - } - - // Similar to single_block_mutation but for a function which - // uses a mutable reference parameter. - #[test] - fn single_block_mutation_through_reference() { - // fn mutator2(array: &mut [Field; 2]) { - // array[0] = 5; - // } - // - // fn mutator2 { - // b0(v0: &mut [Field; 2]): - // v1 = load v0 - // inc_rc v1 - // store v1 at v0 - // v2 = load v0 - // v7 = array_set v2, index u64 0, value Field 5 - // store v7 at v0 - // v8 = load v0 - // dec_rc v8 - // store v8 at v0 - // return - // } - let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("mutator2".into(), main_id, RuntimeType::Acir); - - let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); - let reference_type = Type::Reference(Rc::new(array_type.clone())); - - let v0 = builder.add_parameter(reference_type); - - let v1 = builder.insert_load(v0, array_type.clone()); - builder.insert_inc_rc(v1); - builder.insert_store(v0, v1); - - let v2 = builder.insert_load(v1, array_type.clone()); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); - let five = builder.field_constant(5u128); - let v7 = builder.insert_array_set(v2, zero, five); - - builder.insert_store(v0, v7); - let v8 = builder.insert_load(v0, array_type); - builder.insert_dec_rc(v8); - builder.insert_store(v0, v8); - builder.terminate_with_return(vec![]); - - let ssa = builder.finish().remove_paired_rc(); - let main = ssa.main(); - let entry = main.entry_block(); - - // No changes, the array is possibly mutated - assert_eq!(count_inc_rcs(entry, &main.dfg), 1); - assert_eq!(count_dec_rcs(entry, &main.dfg), 1); - } -} diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 5adb9eb5b7e..fcb20c740c7 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -577,7 +577,12 @@ impl<'a> ModCollector<'a> { let mut ast = ast.into_sorted(); for macro_processor in macro_processors { - match macro_processor.process_untyped_ast(ast.clone(), &crate_id, context) { + match macro_processor.process_untyped_ast( + ast.clone(), + &crate_id, + child_file_id, + context, + ) { Ok(processed_ast) => { ast = processed_ast; } diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 1326ffca9f7..157227f763e 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -91,7 +91,8 @@ impl CrateDefMap { let mut ast = ast.into_sorted(); for macro_processor in macro_processors { - match macro_processor.process_untyped_ast(ast.clone(), &crate_id, context) { + match macro_processor.process_untyped_ast(ast.clone(), &crate_id, root_file_id, context) + { Ok(processed_ast) => { ast = processed_ast; } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 3d834128688..7eacc8eb2d1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -122,10 +122,6 @@ pub enum TypeCheckError { "Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime" )] ConstrainedReferenceToUnconstrained { span: Span }, - #[error( - "Cannot pass a mutable reference from a unconstrained runtime to an constrained runtime" - )] - UnconstrainedReferenceToConstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, #[error("Only sized types may be used in the entry point to a program")] @@ -233,7 +229,6 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } - | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 0b3dd022209..7219f4d09c6 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -184,18 +184,14 @@ impl<'interner> TypeChecker<'interner> { let return_type = self.bind_function_type(function, args, span); // Check that we are not passing a slice from an unconstrained runtime to a constrained runtime - if is_current_func_constrained && is_unconstrained_call { - if return_type.contains_slice() { - self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained { - span: self.interner.expr_span(expr_id), - }); - return Type::Error; - } else if matches!(&return_type.follow_bindings(), Type::MutableReference(_)) { - self.errors.push(TypeCheckError::UnconstrainedReferenceToConstrained { - span: self.interner.expr_span(expr_id), - }); - return Type::Error; - } + if is_current_func_constrained + && is_unconstrained_call + && return_type.contains_slice() + { + self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained { + span: self.interner.expr_span(expr_id), + }); + return Type::Error; } return_type diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 1871b594ae7..6ce6f4325e4 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -72,6 +72,7 @@ pub mod macros_api { &self, ast: SortedModule, crate_id: &CrateId, + file_id: FileId, context: &HirContext, ) -> Result; diff --git a/compiler/noirc_frontend/src/monomorphization/debug.rs b/compiler/noirc_frontend/src/monomorphization/debug.rs index 3a03177f8ec..cf4e0ab792e 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -195,8 +195,8 @@ fn element_type_at_index(ptype: &PrintableType, i: usize) -> &PrintableType { PrintableType::Tuple { types } => &types[i], PrintableType::Struct { name: _name, fields } => &fields[i].1, PrintableType::String { length: _length } => &PrintableType::UnsignedInteger { width: 8 }, - other => { - panic!["expected type with sub-fields, found terminal type: {other:?}"] + _ => { + panic!["expected type with sub-fields, found terminal type"] } } } diff --git a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml deleted file mode 100644 index 1081b5ab8e2..00000000000 --- a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "unconstrained_oracle" -type = "bin" -authors = [""] -[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_oracle/src/main.nr b/test_programs/compile_failure/unconstrained_oracle/src/main.nr deleted file mode 100644 index abc7bf51ab8..00000000000 --- a/test_programs/compile_failure/unconstrained_oracle/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -#[oracle(getNoun)] -unconstrained fn external_fn() -> Field { - 100 / 5 -} - -fn main() { - let x = anon(); - assert(x * 5 == 100); -} diff --git a/test_programs/compile_failure/unconstrained_ref/Nargo.toml b/test_programs/compile_failure/unconstrained_ref/Nargo.toml deleted file mode 100644 index b120a3b5532..00000000000 --- a/test_programs/compile_failure/unconstrained_ref/Nargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "unconstrained_ref" -type = "bin" -authors = [""] -[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_ref/src/main.nr b/test_programs/compile_failure/unconstrained_ref/src/main.nr deleted file mode 100644 index bb4b2090ddb..00000000000 --- a/test_programs/compile_failure/unconstrained_ref/src/main.nr +++ /dev/null @@ -1,8 +0,0 @@ -unconstrained fn uncon_ref() -> &mut Field { - let lr = &mut 7; - lr -} - -fn main() { - let e = uncon_ref(); -} \ No newline at end of file diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index 2eae9e1f07e..0bd2b1c076a 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.29.0"; +const VERSION: &str = "0.26.3"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 09673d7fd4e..d94999f324b 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.29.0", + "@aztec/bb.js": "0.26.3", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index dc3253de21a..49485193532 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.29.0": - version: 0.29.0 - resolution: "@aztec/bb.js@npm:0.29.0" +"@aztec/bb.js@npm:0.26.3": + version: 0.26.3 + resolution: "@aztec/bb.js@npm:0.26.3" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: ae2bae0eed1a64f79f4694d0b02a51b87bb00ef1d9a331193dc9ace69a47819d93b923114e95e77b9b4d3de28c94bf5bdd1e839b0236979aaa0cfcda7a04fa97 + checksum: 74c2b7ef5405f56472cf7c41d1c13261df07b1d5019e3ede9b63d218378e0fb73ccf5c52f1cc524505efad5799b347b497349d7c9b6fe82286014b1574f12309 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.29.0 + "@aztec/bb.js": 0.26.3 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3