diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ad4ef635e17..540b447693c 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -a18288d9b8f3057b9e79362d922da656dacf22a9 +bb719200034e3bc6db09fb56538dadca4203abf4 diff --git a/.github/Cross.toml b/.github/Cross.toml index 09e6316a59c..6520a288d5d 100644 --- a/.github/Cross.toml +++ b/.github/Cross.toml @@ -2,7 +2,8 @@ passthrough = [ "HOME", "RUST_BACKTRACE", - "BARRETENBERG_BIN_DIR" + "BARRETENBERG_BIN_DIR", + "BLNS_JSON_PATH" ] volumes = [ "HOME", diff --git a/Cargo.lock b/Cargo.lock index c42c2b5d74e..2f85b26f974 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1044,9 +1044,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "corosensei" @@ -3088,6 +3088,7 @@ version = "0.26.0" dependencies = [ "acvm", "arena", + "base64 0.21.2", "chumsky", "fm", "iter-extended", @@ -4555,9 +4556,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smawk" diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index 064a9d1244a..68d28b287e6 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -99,8 +99,8 @@ impl std::fmt::Display for Opcode { } Opcode::Call { id, inputs, outputs } => { write!(f, "CALL func {}: ", id)?; - writeln!(f, "inputs: {:?}", inputs)?; - writeln!(f, "outputs: {:?}", outputs) + write!(f, "inputs: {:?}, ", inputs)?; + write!(f, "outputs: {:?}", outputs) } } } diff --git a/acvm-repo/acir/src/native_types/witness_stack.rs b/acvm-repo/acir/src/native_types/witness_stack.rs index 9592d90b014..a9e8f219b3e 100644 --- a/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/acvm-repo/acir/src/native_types/witness_stack.rs @@ -32,6 +32,20 @@ pub struct StackItem { pub witness: WitnessMap, } +impl WitnessStack { + pub fn push(&mut self, index: u32, witness: WitnessMap) { + self.stack.push(StackItem { index, witness }); + } + + pub fn peek(&self) -> Option<&StackItem> { + self.stack.last() + } + + pub fn length(&self) -> usize { + self.stack.len() + } +} + impl From for WitnessStack { fn from(witness: WitnessMap) -> Self { let stack = vec![StackItem { index: 0, witness }]; diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index 18f49c154f1..1ba261b09a3 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -142,7 +142,12 @@ pub(super) fn transform_internal( new_acir_opcode_positions.push(acir_opcode_positions[index]); transformed_opcodes.push(opcode); } - Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"), + Opcode::Call { .. } => { + // `Call` does not write values to the `WitnessMap` + // A separate ACIR function should have its own respective `WitnessMap` + new_acir_opcode_positions.push(acir_opcode_positions[index]); + transformed_opcodes.push(opcode); + } } } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 0fd733a6336..3cedcfc0399 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -49,6 +49,12 @@ pub enum ACVMStatus { /// /// Once this is done, the ACVM can be restarted to solve the remaining opcodes. RequiresForeignCall(ForeignCallWaitInfo), + + /// The ACVM has encountered a request for an ACIR [call][acir::circuit::Opcode] + /// to execute a separate ACVM instance. The result of the ACIR call must be passd back to the ACVM. + /// + /// Once this is done, the ACVM can be restarted to solve the remaining opcodes. + RequiresAcirCall(AcirCallWaitInfo), } impl std::fmt::Display for ACVMStatus { @@ -58,6 +64,7 @@ impl std::fmt::Display for ACVMStatus { ACVMStatus::InProgress => write!(f, "In progress"), ACVMStatus::Failure(_) => write!(f, "Execution failure"), ACVMStatus::RequiresForeignCall(_) => write!(f, "Waiting on foreign call"), + ACVMStatus::RequiresAcirCall(_) => write!(f, "Waiting on acir call"), } } } @@ -117,6 +124,10 @@ pub enum OpcodeResolutionError { BlackBoxFunctionFailed(BlackBoxFunc, String), #[error("Failed to solve brillig function, reason: {message}")] BrilligFunctionFailed { message: String, call_stack: Vec }, + #[error("Attempted to call `main` with a `Call` opcode")] + AcirMainCallAttempted { opcode_location: ErrorLocation }, + #[error("{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen")] + AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 }, } impl From for OpcodeResolutionError { @@ -147,6 +158,13 @@ pub struct ACVM<'a, B: BlackBoxFunctionSolver> { witness_map: WitnessMap, brillig_solver: Option>, + + /// A counter maintained throughout an ACVM process that determines + /// whether the caller has resolved the results of an ACIR [call][Opcode::Call]. + acir_call_counter: usize, + /// Represents the outputs of all ACIR calls during an ACVM process + /// List is appended onto by the caller upon reaching a [ACVMStatus::RequiresAcirCall] + acir_call_results: Vec>, } impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { @@ -161,6 +179,8 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { instruction_pointer: 0, witness_map: initial_witness, brillig_solver: None, + acir_call_counter: 0, + acir_call_results: Vec::default(), } } @@ -244,6 +264,29 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { self.status(ACVMStatus::InProgress); } + /// Sets the status of the VM to `RequiresAcirCall` + /// Indicating that the VM is now waiting for an ACIR call to be resolved + fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo) -> ACVMStatus { + self.status(ACVMStatus::RequiresAcirCall(acir_call)) + } + + /// Resolves an ACIR call's result (simply a list of fields) using a result calculated by a separate ACVM instance. + /// + /// The current ACVM instance can then be restarted to solve the remaining ACIR opcodes. + pub fn resolve_pending_acir_call(&mut self, call_result: Vec) { + if !matches!(self.status, ACVMStatus::RequiresAcirCall(_)) { + panic!("ACVM is not expecting an ACIR call response as no call was made"); + } + + if self.acir_call_counter < self.acir_call_results.len() { + panic!("No unresolved ACIR calls"); + } + self.acir_call_results.push(call_result); + + // Now that the ACIR call has been resolved then we can resume execution. + self.status(ACVMStatus::InProgress); + } + /// Executes the ACVM's circuit until execution halts. /// /// Execution can halt due to three reasons: @@ -281,7 +324,10 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { Ok(Some(foreign_call)) => return self.wait_for_foreign_call(foreign_call), res => res.map(|_| ()), }, - Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"), + Opcode::Call { .. } => match self.solve_call_opcode() { + Ok(Some(input_values)) => return self.wait_for_acir_call(input_values), + res => res.map(|_| ()), + }, }; self.handle_opcode_resolution(resolution) } @@ -400,6 +446,46 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { self.brillig_solver = Some(solver); self.solve_opcode() } + + pub fn solve_call_opcode(&mut self) -> Result, OpcodeResolutionError> { + let Opcode::Call { id, inputs, outputs } = &self.opcodes[self.instruction_pointer] else { + unreachable!("Not executing a Call opcode"); + }; + if *id == 0 { + return Err(OpcodeResolutionError::AcirMainCallAttempted { + opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir( + self.instruction_pointer(), + )), + }); + } + + if self.acir_call_counter >= self.acir_call_results.len() { + let mut initial_witness = WitnessMap::default(); + for (i, input_witness) in inputs.iter().enumerate() { + let input_value = *witness_to_value(&self.witness_map, *input_witness)?; + initial_witness.insert(Witness(i as u32), input_value); + } + return Ok(Some(AcirCallWaitInfo { id: *id, initial_witness })); + } + + let result_values = &self.acir_call_results[self.acir_call_counter]; + if outputs.len() != result_values.len() { + return Err(OpcodeResolutionError::AcirCallOutputsMismatch { + opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir( + self.instruction_pointer(), + )), + results_size: result_values.len() as u32, + outputs_size: outputs.len() as u32, + }); + } + + for (output_witness, result_value) in outputs.iter().zip(result_values) { + insert_value(output_witness, *result_value, &mut self.witness_map)?; + } + + self.acir_call_counter += 1; + Ok(None) + } } // Returns the concrete value for a particular witness @@ -469,3 +555,11 @@ fn any_witness_from_expression(expr: &Expression) -> Option { Some(expr.linear_combinations[0].1) } } + +#[derive(Debug, Clone, PartialEq)] +pub struct AcirCallWaitInfo { + /// Index in the list of ACIR function's that should be called + pub id: u32, + /// Initial witness for the given circuit to be called + pub initial_witness: WitnessMap, +} diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index ac71a573e64..60d27a489e2 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -113,6 +113,9 @@ pub async fn execute_circuit_with_black_box_solver( acvm.resolve_pending_foreign_call(result); } + ACVMStatus::RequiresAcirCall(_) => { + todo!("Handle acir calls in acvm JS"); + } } } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 8b3c126dedb..86d4cd1510d 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -3,20 +3,22 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] -use acvm::acir::circuit::{ExpressionWidth, Program}; +use acvm::acir::circuit::ExpressionWidth; use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, ContractEvent}; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; -use noirc_evaluator::create_circuit; +use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; use noirc_frontend::debug::build_debug_crate_file; use noirc_frontend::graph::{CrateId, CrateName}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; use noirc_frontend::macros_api::MacroProcessor; -use noirc_frontend::monomorphization::{monomorphize, monomorphize_debug, MonomorphizationError}; +use noirc_frontend::monomorphization::{ + errors::MonomorphizationError, monomorphize, monomorphize_debug, +}; use noirc_frontend::node_interner::FuncId; use noirc_frontend::token::SecondaryAttribute; use std::path::Path; @@ -482,7 +484,8 @@ pub fn compile_no_check( return Ok(cached_program.expect("cache must exist for hashes to match")); } let visibility = program.return_visibility; - let (circuit, debug, input_witnesses, return_witnesses, warnings) = create_circuit( + + let (program, debug, warnings, input_witnesses, return_witnesses) = create_program( program, options.show_ssa, options.show_brillig, @@ -492,13 +495,17 @@ pub fn compile_no_check( let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses, visibility); - let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); + let file_map = filter_relevant_files(&debug, &context.file_manager); Ok(CompiledProgram { hash, // TODO(https://github.com/noir-lang/noir/issues/4428) - program: Program { functions: vec![circuit] }, - debug, + program, + // TODO(https://github.com/noir-lang/noir/issues/4428) + // Debug info is only relevant for errors at execution time which is not yet supported + // The CompileProgram `debug` field is used in multiple places and is better + // left to be updated once execution of multiple ACIR functions is enabled + debug: debug[0].clone(), abi, file_map, noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index e42b2787f73..b93693d9c79 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -373,8 +373,9 @@ mod tests { use crate::ssa::ssa_gen::Ssa; fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) { - let builder = - FunctionBuilder::new("main".to_string(), Id::test_new(0), RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("main".to_string(), Id::test_new(0)); + builder.set_runtime(RuntimeType::Brillig); + let ssa = builder.finish(); let mut brillig_context = create_context(); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs index 05978c2c6ab..c9f1cd1e247 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -323,7 +323,8 @@ mod test { // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("main".into(), main_id); + builder.set_runtime(RuntimeType::Brillig); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -425,7 +426,8 @@ mod test { // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("main".into(), main_id); + builder.set_runtime(RuntimeType::Brillig); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/lib.rs b/compiler/noirc_evaluator/src/lib.rs index 70751d3e541..54376963338 100644 --- a/compiler/noirc_evaluator/src/lib.rs +++ b/compiler/noirc_evaluator/src/lib.rs @@ -11,4 +11,4 @@ pub mod ssa; pub mod brillig; -pub use ssa::create_circuit; +pub use ssa::create_program; diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 01dc50a7119..59f2bdc6f84 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -14,11 +14,11 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::acir::{ - circuit::{Circuit, ExpressionWidth, PublicInputs}, + circuit::{Circuit, ExpressionWidth, Program as AcirProgram, PublicInputs}, native_types::Witness, }; -use noirc_errors::debug_info::DebugInfo; +use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVariables}; use noirc_frontend::{ hir_def::function::FunctionSignature, monomorphization::ast::Program, Visibility, @@ -42,7 +42,7 @@ pub(crate) fn optimize_into_acir( print_brillig_trace: bool, force_brillig_output: bool, print_timings: bool, -) -> Result { +) -> Result, RuntimeError> { let abi_distinctness = program.return_distinctness; let ssa_gen_span = span!(Level::TRACE, "ssa_generation"); @@ -72,7 +72,7 @@ pub(crate) fn optimize_into_acir( let last_array_uses = ssa.find_last_array_uses(); time("SSA to ACIR", print_timings, || { - ssa.into_acir(brillig, abi_distinctness, &last_array_uses) + ssa.into_acir(&brillig, abi_distinctness, &last_array_uses) }) } @@ -89,30 +89,80 @@ fn time(name: &str, print_timings: bool, f: impl FnOnce() -> T) -> T { result } -/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Circuit]. +/// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program]. /// /// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation. #[allow(clippy::type_complexity)] #[tracing::instrument(level = "trace", skip_all)] -pub fn create_circuit( +pub fn create_program( program: Program, enable_ssa_logging: bool, enable_brillig_logging: bool, force_brillig_output: bool, print_codegen_timings: bool, -) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { +) -> Result<(AcirProgram, Vec, Vec, Vec, Vec), RuntimeError> +{ let debug_variables = program.debug_variables.clone(); let debug_types = program.debug_types.clone(); let debug_functions = program.debug_functions.clone(); - let func_sig = program.main_function_signature.clone(); + + let func_sigs = program.function_signatures.clone(); + let recursive = program.recursive; - let mut generated_acir = optimize_into_acir( + let generated_acirs = optimize_into_acir( program, enable_ssa_logging, enable_brillig_logging, force_brillig_output, print_codegen_timings, )?; + assert_eq!( + generated_acirs.len(), + func_sigs.len(), + "The generated ACIRs should match the supplied function signatures" + ); + + let mut functions = vec![]; + let mut debug_infos = vec![]; + let mut warning_infos = vec![]; + let mut main_input_witnesses = Vec::new(); + let mut main_return_witnesses = Vec::new(); + // For setting up the ABI we need separately specify main's input and return witnesses + let mut is_main = true; + for (acir, func_sig) in generated_acirs.into_iter().zip(func_sigs) { + let (circuit, debug_info, warnings, input_witnesses, return_witnesses) = + convert_generated_acir_into_circuit( + acir, + func_sig, + recursive, + // TODO: get rid of these clones + debug_variables.clone(), + debug_functions.clone(), + debug_types.clone(), + ); + functions.push(circuit); + debug_infos.push(debug_info); + warning_infos.extend(warnings); + if is_main { + main_input_witnesses = input_witnesses; + main_return_witnesses = return_witnesses; + } + is_main = false; + } + + let program = AcirProgram { functions }; + + Ok((program, debug_infos, warning_infos, main_input_witnesses, main_return_witnesses)) +} + +fn convert_generated_acir_into_circuit( + mut generated_acir: GeneratedAcir, + func_sig: FunctionSignature, + recursive: bool, + debug_variables: DebugVariables, + debug_functions: DebugFunctions, + debug_types: DebugTypes, +) -> (Circuit, DebugInfo, Vec, Vec, Vec) { let opcodes = generated_acir.take_opcodes(); let current_witness_index = generated_acir.current_witness_index().0; let GeneratedAcir { @@ -124,6 +174,8 @@ pub fn create_circuit( .. } = generated_acir; + let locations = locations.clone(); + let (public_parameter_witnesses, private_parameters) = split_public_and_private_inputs(&func_sig, &input_witnesses); @@ -137,7 +189,7 @@ pub fn create_circuit( private_parameters, public_parameters, return_values, - assert_messages: assert_messages.into_iter().collect(), + assert_messages: assert_messages.clone().into_iter().collect(), recursive, }; @@ -153,7 +205,7 @@ pub fn create_circuit( let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit); debug_info.update_acir(transformation_map); - Ok((optimized_circuit, debug_info, input_witnesses, return_witnesses, warnings)) + (optimized_circuit, debug_info, warnings, input_witnesses, return_witnesses) } // Takes each function argument and partitions the circuit's inputs witnesses according to its visibility. diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 7e951cf4e00..bcd62e3b062 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1757,6 +1757,30 @@ impl AcirContext { } Ok(()) } + + pub(crate) fn call_acir_function( + &mut self, + id: u32, + inputs: Vec, + output_count: usize, + ) -> Result, RuntimeError> { + let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?; + let inputs = inputs + .iter() + .flat_map(|input| vecmap(input, |input| input.witness)) + .collect::>(); + let outputs = vecmap(0..output_count, |_| self.acir_ir.next_witness_index()); + + // Convert `Witness` values which are now constrained to be the output of the + // ACIR function call into `AcirVar`s. + // Similar to black box functions, we do not apply range information on the output of the function. + // See issue https://github.com/noir-lang/noir/issues/1439 + let results = + vecmap(&outputs, |witness_index| self.add_data(AcirVarData::Witness(*witness_index))); + + self.acir_ir.push_opcode(Opcode::Call { id, inputs, outputs }); + Ok(results) + } } /// Enum representing the possible values that a diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 8390f480e3a..96f959612af 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -25,6 +25,7 @@ use crate::brillig::brillig_ir::artifact::GeneratedBrillig; use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; +use crate::ssa::ir::function::InlineType; pub(crate) use acir_ir::generated_acir::GeneratedAcir; use acvm::acir::native_types::Witness; @@ -178,31 +179,45 @@ impl Ssa { #[tracing::instrument(level = "trace", skip_all)] pub(crate) fn into_acir( self, - brillig: Brillig, + brillig: &Brillig, abi_distinctness: Distinctness, last_array_uses: &HashMap, - ) -> Result { - let context = Context::new(); - let mut generated_acir = context.convert_ssa(self, brillig, last_array_uses)?; + ) -> Result, RuntimeError> { + let mut acirs = Vec::new(); + // TODO: can we parallelise this? + for function in self.functions.values() { + let context = Context::new(); + if let Some(generated_acir) = + context.convert_ssa_function(&self, function, brillig, last_array_uses)? + { + acirs.push(generated_acir); + } + } + // TODO: check whether doing this for a single circuit's return witnesses is correct. + // We probably need it for all foldable circuits, as any circuit being folded is essentially an entry point. However, I do not know how that + // plays a part when we potentially want not inlined functions normally as part of the compiler. + // Also at the moment we specify Distinctness as part of the ABI exclusively rather than the function itself + // so this will need to be updated. + let main_func_acir = &mut acirs[0]; match abi_distinctness { Distinctness::Distinct => { // Create a witness for each return witness we have // to guarantee that the return witnesses are distinct - let distinct_return_witness: Vec<_> = generated_acir + let distinct_return_witness: Vec<_> = main_func_acir .return_witnesses .clone() .into_iter() .map(|return_witness| { - generated_acir + main_func_acir .create_witness_for_expression(&Expression::from(return_witness)) }) .collect(); - generated_acir.return_witnesses = distinct_return_witness; - Ok(generated_acir) + main_func_acir.return_witnesses = distinct_return_witness; + Ok(acirs) } - Distinctness::DuplicationAllowed => Ok(generated_acir), + Distinctness::DuplicationAllowed => Ok(acirs), } } } @@ -225,17 +240,33 @@ impl Context { } } - /// Converts SSA into ACIR - fn convert_ssa( + fn convert_ssa_function( self, - ssa: Ssa, - brillig: Brillig, + ssa: &Ssa, + function: &Function, + brillig: &Brillig, last_array_uses: &HashMap, - ) -> Result { - let main_func = ssa.main(); - match main_func.runtime() { - RuntimeType::Acir => self.convert_acir_main(main_func, &ssa, brillig, last_array_uses), - RuntimeType::Brillig => self.convert_brillig_main(main_func, brillig), + ) -> Result, RuntimeError> { + match function.runtime() { + RuntimeType::Acir(inline_type) => { + match inline_type { + InlineType::Fold => {} + InlineType::Inline => { + if function.id() != ssa.main_id { + panic!("ACIR function should have been inlined earlier if not marked otherwise"); + } + } + } + // We only want to convert entry point functions. This being `main` and those marked with `#[fold]` + Ok(Some(self.convert_acir_main(function, ssa, brillig, last_array_uses)?)) + } + RuntimeType::Brillig => { + if function.id() == ssa.main_id { + Ok(Some(self.convert_brillig_main(function, brillig)?)) + } else { + Ok(None) + } + } } } @@ -243,7 +274,7 @@ impl Context { mut self, main_func: &Function, ssa: &Ssa, - brillig: Brillig, + brillig: &Brillig, last_array_uses: &HashMap, ) -> Result { let dfg = &main_func.dfg; @@ -257,7 +288,7 @@ impl Context { *instruction_id, dfg, ssa, - &brillig, + brillig, last_array_uses, )?); } @@ -269,7 +300,7 @@ impl Context { fn convert_brillig_main( mut self, main_func: &Function, - brillig: Brillig, + brillig: &Brillig, ) -> Result { let dfg = &main_func.dfg; @@ -282,7 +313,7 @@ impl Context { let outputs: Vec = vecmap(main_func.returns(), |result_id| dfg.type_of_value(*result_id).into()); - let code = self.gen_brillig_for(main_func, &brillig)?; + let code = self.gen_brillig_for(main_func, brillig)?; // We specifically do not attempt execution of the brillig code being generated as this can result in it being // replaced with constraints on witnesses to the program outputs. @@ -537,15 +568,40 @@ impl Context { Value::Function(id) => { let func = &ssa.functions[id]; match func.runtime() { - RuntimeType::Acir => unimplemented!( - "expected an intrinsic/brillig call, but found {func:?}. All ACIR methods should be inlined" - ), + RuntimeType::Acir(inline_type) => { + assert!(!matches!(inline_type, InlineType::Inline), "ICE: Got an ACIR function named {} that should have already been inlined", func.name()); + + let inputs = vecmap(arguments, |arg| self.convert_value(*arg, dfg)); + // TODO(https://github.com/noir-lang/noir/issues/4608): handle complex return types from ACIR functions + let output_count = + result_ids.iter().fold(0usize, |sum, result_id| { + sum + dfg.try_get_array_length(*result_id).unwrap_or(1) + }); + + let acir_program_id = ssa + .id_to_index + .get(id) + .expect("ICE: should have an associated final index"); + let output_vars = self.acir_context.call_acir_function( + *acir_program_id, + inputs, + output_count, + )?; + let output_values = + self.convert_vars_to_values(output_vars, dfg, result_ids); + + self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; + } RuntimeType::Brillig => { - // Check that we are not attempting to return a slice from + // Check that we are not attempting to return a slice from // an unconstrained runtime to a constrained runtime for result_id in result_ids { if dfg.type_of_value(*result_id).contains_slice_element() { - return Err(RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack: self.acir_context.get_call_stack() }) + return Err( + RuntimeError::UnconstrainedSliceReturnToConstrained { + call_stack: self.acir_context.get_call_stack(), + }, + ); } } @@ -553,22 +609,23 @@ impl Context { let code = self.gen_brillig_for(func, brillig)?; - let outputs: Vec = vecmap(result_ids, |result_id| dfg.type_of_value(*result_id).into()); + let outputs: Vec = vecmap(result_ids, |result_id| { + dfg.type_of_value(*result_id).into() + }); - let output_values = self.acir_context.brillig(self.current_side_effects_enabled_var, code, inputs, outputs, true, false)?; + let output_values = self.acir_context.brillig( + self.current_side_effects_enabled_var, + code, + inputs, + outputs, + true, + false, + )?; // Compiler sanity check assert_eq!(result_ids.len(), output_values.len(), "ICE: The number of Brillig output values should match the result ids in SSA"); - for result in result_ids.iter().zip(output_values) { - if let AcirValue::Array(_) = &result.1 { - let array_id = dfg.resolve(*result.0); - let block_id = self.block_id(&array_id); - let array_typ = dfg.type_of_value(array_id); - self.initialize_array(block_id, array_typ.flattened_size(), Some(result.1.clone()))?; - } - self.ssa_values.insert(*result.0, result.1); - } + self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; } } } @@ -587,30 +644,7 @@ impl Context { // Issue #1438 causes this check to fail with intrinsics that return 0 // results but the ssa form instead creates 1 unit result value. // assert_eq!(result_ids.len(), outputs.len()); - - for (result, output) in result_ids.iter().zip(outputs) { - match &output { - // We need to make sure we initialize arrays returned from intrinsic calls - // or else they will fail if accessed with a dynamic index - AcirValue::Array(_) => { - let block_id = self.block_id(result); - let array_typ = dfg.type_of_value(*result); - let len = if matches!(array_typ, Type::Array(_, _)) { - array_typ.flattened_size() - } else { - Self::flattened_value_size(&output) - }; - self.initialize_array(block_id, len, Some(output.clone()))?; - } - AcirValue::DynamicArray(_) => { - // Do nothing as a dynamic array returned from a slice intrinsic should already be initialized - } - AcirValue::Var(_, _) => { - // Do nothing - } - } - self.ssa_values.insert(*result, output); - } + self.handle_ssa_call_outputs(result_ids, outputs, dfg)?; } Value::ForeignFunction(_) => { return Err(RuntimeError::UnconstrainedOracleReturnToConstrained { @@ -625,6 +659,32 @@ impl Context { Ok(warnings) } + fn handle_ssa_call_outputs( + &mut self, + result_ids: &[ValueId], + output_values: Vec, + dfg: &DataFlowGraph, + ) -> Result<(), RuntimeError> { + for (result_id, output) in result_ids.iter().zip(output_values) { + if let AcirValue::Array(_) = &output { + let array_id = dfg.resolve(*result_id); + let block_id = self.block_id(&array_id); + let array_typ = dfg.type_of_value(array_id); + let len = if matches!(array_typ, Type::Array(_, _)) { + array_typ.flattened_size() + } else { + Self::flattened_value_size(&output) + }; + self.initialize_array(block_id, len, Some(output.clone()))?; + } + // Do nothing for AcirValue::DynamicArray and AcirValue::Var + // A dynamic array returned from a function call should already be initialized + // and a single variable does not require any extra initialization. + self.ssa_values.insert(*result_id, output); + } + Ok(()) + } + fn gen_brillig_for( &self, func: &Function, @@ -1373,6 +1433,7 @@ impl Context { TerminatorInstruction::Return { return_values, call_stack } => { (return_values, call_stack) } + // TODO(https://github.com/noir-lang/noir/issues/4616): Enable recursion on foldable/non-inlined ACIR functions _ => unreachable!("ICE: Program must have a singular return"), }; @@ -2327,3 +2388,338 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { !types.iter().any(|typ| typ.contains_an_array()) } + +#[cfg(test)] +mod test { + use acvm::{ + acir::{circuit::Opcode, native_types::Witness}, + FieldElement, + }; + + use crate::{ + brillig::Brillig, + ssa::{ + function_builder::FunctionBuilder, + ir::{ + function::{FunctionId, InlineType, RuntimeType}, + instruction::BinaryOp, + map::Id, + types::Type, + }, + }, + }; + use fxhash::FxHashMap as HashMap; + + fn build_basic_foo_with_return(builder: &mut FunctionBuilder, foo_id: FunctionId) { + // acir(fold) fn foo f1 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == u1 0 + // return v0 + // } + builder.new_function("foo".into(), foo_id, InlineType::Fold); + let foo_v0 = builder.add_parameter(Type::field()); + let foo_v1 = builder.add_parameter(Type::field()); + + let foo_equality_check = builder.insert_binary(foo_v0, BinaryOp::Eq, foo_v1); + let zero = builder.field_constant(0u128); + builder.insert_constrain(foo_equality_check, zero, None); + builder.terminate_with_return(vec![foo_v0]); + } + + #[test] + fn basic_call_with_outputs_assert() { + // acir(inline) fn main f0 { + // b0(v0: Field, v1: Field): + // v2 = call f1(v0, v1) + // v3 = call f1(v0, v1) + // constrain v2 == v3 + // return + // } + // acir(fold) fn foo f1 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == u1 0 + // return v0 + // } + let foo_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), foo_id); + let main_v0 = builder.add_parameter(Type::field()); + let main_v1 = builder.add_parameter(Type::field()); + + let foo_id = Id::test_new(1); + let foo = builder.import_function(foo_id); + let main_call1_results = + builder.insert_call(foo, vec![main_v0, main_v1], vec![Type::field()]).to_vec(); + let main_call2_results = + builder.insert_call(foo, vec![main_v0, main_v1], vec![Type::field()]).to_vec(); + builder.insert_constrain(main_call1_results[0], main_call2_results[0], None); + builder.terminate_with_return(vec![]); + + build_basic_foo_with_return(&mut builder, foo_id); + + let ssa = builder.finish(); + + let acir_functions = ssa + .into_acir( + &Brillig::default(), + noirc_frontend::Distinctness::Distinct, + &HashMap::default(), + ) + .expect("Should compile manually written SSA into ACIR"); + // Expected result: + // main f0 + // GeneratedAcir { + // ... + // opcodes: [ + // CALL func 1: inputs: [Witness(0), Witness(1)], outputs: [Witness(2)], + // CALL func 1: inputs: [Witness(0), Witness(1)], outputs: [Witness(3)], + // EXPR [ (1, _2) (-1, _3) 0 ], + // ], + // return_witnesses: [], + // input_witnesses: [ + // Witness( + // 0, + // ), + // Witness( + // 1, + // ), + // ], + // ... + // } + // foo f1 + // GeneratedAcir { + // ... + // opcodes: [ + // Same as opcodes as the expected result of `basic_call_codegen` + // ], + // return_witnesses: [ + // Witness( + // 0, + // ), + // ], + // input_witnesses: [ + // Witness( + // 0, + // ), + // Witness( + // 1, + // ), + // ], + // ... + // }, + + let main_acir = &acir_functions[0]; + let main_opcodes = main_acir.opcodes(); + assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo`"); + + check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + + match &main_opcodes[2] { + Opcode::AssertZero(expr) => { + assert_eq!(expr.linear_combinations[0].0, FieldElement::from(1u128)); + assert_eq!(expr.linear_combinations[0].1, Witness(2)); + + assert_eq!(expr.linear_combinations[1].0, FieldElement::from(-1i128)); + assert_eq!(expr.linear_combinations[1].1, Witness(3)); + assert_eq!(expr.q_c, FieldElement::from(0u128)); + } + _ => {} + } + } + + #[test] + fn call_output_as_next_call_input() { + // acir(inline) fn main f0 { + // b0(v0: Field, v1: Field): + // v3 = call f1(v0, v1) + // v4 = call f1(v3, v1) + // constrain v3 == v4 + // return + // } + // acir(fold) fn foo f1 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == u1 0 + // return v0 + // } + let foo_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), foo_id); + let main_v0 = builder.add_parameter(Type::field()); + let main_v1 = builder.add_parameter(Type::field()); + + let foo_id = Id::test_new(1); + let foo = builder.import_function(foo_id); + let main_call1_results = + builder.insert_call(foo, vec![main_v0, main_v1], vec![Type::field()]).to_vec(); + let main_call2_results = builder + .insert_call(foo, vec![main_call1_results[0], main_v1], vec![Type::field()]) + .to_vec(); + builder.insert_constrain(main_call1_results[0], main_call2_results[0], None); + builder.terminate_with_return(vec![]); + + build_basic_foo_with_return(&mut builder, foo_id); + + let ssa = builder.finish(); + + let acir_functions = ssa + .into_acir( + &Brillig::default(), + noirc_frontend::Distinctness::Distinct, + &HashMap::default(), + ) + .expect("Should compile manually written SSA into ACIR"); + // The expected result should look very similar to the abvoe test expect that the input witnesses of the `Call` + // opcodes will be different. The changes can discerned from the checks below. + + let main_acir = &acir_functions[0]; + let main_opcodes = main_acir.opcodes(); + assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); + + check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + // The output of the first call should be the input of the second call + check_call_opcode(&main_opcodes[1], 1, vec![Witness(2), Witness(1)], vec![Witness(3)]); + } + + #[test] + fn basic_nested_call() { + // SSA for the following Noir program: + // fn main(x: Field, y: pub Field) { + // let z = func_with_nested_foo_call(x, y); + // let z2 = func_with_nested_foo_call(x, y); + // assert(z == z2); + // } + // #[fold] + // fn func_with_nested_foo_call(x: Field, y: Field) -> Field { + // nested_call(x + 2, y) + // } + // #[fold] + // fn foo(x: Field, y: Field) -> Field { + // assert(x != y); + // x + // } + // + // SSA: + // acir(inline) fn main f0 { + // b0(v0: Field, v1: Field): + // v3 = call f1(v0, v1) + // v4 = call f1(v0, v1) + // constrain v3 == v4 + // return + // } + // acir(fold) fn func_with_nested_foo_call f1 { + // b0(v0: Field, v1: Field): + // v3 = add v0, Field 2 + // v5 = call f2(v3, v1) + // return v5 + // } + // acir(fold) fn foo f2 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == Field 0 + // return v0 + // } + let foo_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), foo_id); + let main_v0 = builder.add_parameter(Type::field()); + let main_v1 = builder.add_parameter(Type::field()); + + let func_with_nested_foo_call_id = Id::test_new(1); + let func_with_nested_foo_call = builder.import_function(func_with_nested_foo_call_id); + let main_call1_results = builder + .insert_call(func_with_nested_foo_call, vec![main_v0, main_v1], vec![Type::field()]) + .to_vec(); + let main_call2_results = builder + .insert_call(func_with_nested_foo_call, vec![main_v0, main_v1], vec![Type::field()]) + .to_vec(); + builder.insert_constrain(main_call1_results[0], main_call2_results[0], None); + builder.terminate_with_return(vec![]); + + builder.new_function( + "func_with_nested_foo_call".into(), + func_with_nested_foo_call_id, + InlineType::Fold, + ); + let func_with_nested_call_v0 = builder.add_parameter(Type::field()); + let func_with_nested_call_v1 = builder.add_parameter(Type::field()); + + let two = builder.field_constant(2u128); + let v0_plus_two = builder.insert_binary(func_with_nested_call_v0, BinaryOp::Add, two); + + let foo_id = Id::test_new(2); + let foo_call = builder.import_function(foo_id); + let foo_call = builder + .insert_call(foo_call, vec![v0_plus_two, func_with_nested_call_v1], vec![Type::field()]) + .to_vec(); + builder.terminate_with_return(vec![foo_call[0]]); + + build_basic_foo_with_return(&mut builder, foo_id); + + let ssa = builder.finish(); + + let acir_functions = ssa + .into_acir( + &Brillig::default(), + noirc_frontend::Distinctness::Distinct, + &HashMap::default(), + ) + .expect("Should compile manually written SSA into ACIR"); + + assert_eq!(acir_functions.len(), 3, "Should have three ACIR functions"); + + let main_acir = &acir_functions[0]; + let main_opcodes = main_acir.opcodes(); + assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); + + // Both of these should call func_with_nested_foo_call f1 + check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + // The output of the first call should be the input of the second call + check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + + let func_with_nested_call_acir = &acir_functions[1]; + let func_with_nested_call_opcodes = func_with_nested_call_acir.opcodes(); + assert_eq!( + func_with_nested_call_opcodes.len(), + 2, + "Should have an expression and a call to a nested `foo`" + ); + // Should call foo f2 + check_call_opcode( + &func_with_nested_call_opcodes[1], + 2, + vec![Witness(2), Witness(1)], + vec![Witness(3)], + ); + } + + fn check_call_opcode( + opcode: &Opcode, + expected_id: u32, + expected_inputs: Vec, + expected_outputs: Vec, + ) { + match opcode { + Opcode::Call { id, inputs, outputs } => { + assert_eq!( + *id, expected_id, + "Main was expected to call {expected_id} but got {}", + *id + ); + for (expected_input, input) in expected_inputs.iter().zip(inputs) { + assert_eq!( + expected_input, input, + "Expected witness {expected_input:?} but got {input:?}" + ); + } + for (expected_output, output) in expected_outputs.iter().zip(outputs) { + assert_eq!( + expected_output, output, + "Expected witness {expected_output:?} but got {output:?}" + ); + } + } + _ => panic!("Expected only Call opcode"), + } + } +} diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 0726b557616..e0e60b737ad 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -17,7 +17,7 @@ use super::{ ir::{ basic_block::BasicBlock, dfg::{CallStack, InsertInstructionResult}, - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{ConstrainError, InstructionId, Intrinsic}, }, ssa_gen::Ssa, @@ -42,13 +42,8 @@ impl FunctionBuilder { /// /// This creates the new function internally so there is no need to call .new_function() /// right after constructing a new FunctionBuilder. - pub(crate) fn new( - function_name: String, - function_id: FunctionId, - runtime: RuntimeType, - ) -> Self { - let mut new_function = Function::new(function_name, function_id); - new_function.set_runtime(runtime); + pub(crate) fn new(function_name: String, function_id: FunctionId) -> Self { + let new_function = Function::new(function_name, function_id); Self { current_block: new_function.entry_block(), @@ -58,6 +53,15 @@ impl FunctionBuilder { } } + /// Set the runtime of the initial function that is created internally after constructing + /// the FunctionBuilder. A function's default runtime type is `RuntimeType::Acir(InlineType::Inline)`. + /// This should only be used immediately following construction of a FunctionBuilder + /// and will panic if there are any already finished functions. + pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) { + assert_eq!(self.finished_functions.len(), 0, "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function"); + self.current_function.set_runtime(runtime); + } + /// Finish the current function and create a new function. /// /// A FunctionBuilder can always only work on one function at a time, so care @@ -78,8 +82,13 @@ impl FunctionBuilder { } /// Finish the current function and create a new ACIR function. - pub(crate) fn new_function(&mut self, name: String, function_id: FunctionId) { - self.new_function_with_type(name, function_id, RuntimeType::Acir); + pub(crate) fn new_function( + &mut self, + name: String, + function_id: FunctionId, + inline_type: InlineType, + ) { + self.new_function_with_type(name, function_id, RuntimeType::Acir(inline_type)); } /// Finish the current function and create a new unconstrained function. @@ -491,7 +500,7 @@ mod tests { use acvm::FieldElement; use crate::ssa::ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{Endian, Intrinsic}, map::Id, types::Type, @@ -506,7 +515,7 @@ mod tests { // let x = 7; // let bits = x.to_le_bits(8); let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let one = builder.numeric_constant(FieldElement::one(), Type::bool()); let zero = builder.numeric_constant(FieldElement::zero(), Type::bool()); diff --git a/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/compiler/noirc_evaluator/src/ssa/ir/dom.rs index c1df0428c64..bd1481a7474 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -252,7 +252,7 @@ mod tests { basic_block::BasicBlockId, dfg::CallStack, dom::DominatorTree, - function::{Function, RuntimeType}, + function::{Function, InlineType, RuntimeType}, instruction::TerminatorInstruction, map::Id, types::Type, @@ -286,7 +286,7 @@ mod tests { // return () // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let cond = builder.add_parameter(Type::unsigned(1)); let block1_id = builder.insert_block(); @@ -395,7 +395,7 @@ mod tests { // jump block1() // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let block1_id = builder.insert_block(); let block2_id = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 360f5710113..011bee36661 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -12,11 +12,39 @@ use super::value::ValueId; #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) enum RuntimeType { // A noir function, to be compiled in ACIR and executed by ACVM - Acir, + Acir(InlineType), // Unconstrained function, to be compiled to brillig and executed by the Brillig VM Brillig, } +/// Represents how a RuntimeType::Acir function should be inlined. +/// This type is only relevant for ACIR functions as we do not inline any Brillig functions +#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub(crate) enum InlineType { + /// The most basic entry point can expect all its functions to be inlined. + /// All function calls are expected to be inlined into a single ACIR. + #[default] + Inline, + /// Functions marked as foldable will not be inlined and compiled separately into ACIR + Fold, +} + +impl RuntimeType { + /// Returns whether the runtime type represents an entry point. + /// We return `false` for InlineType::Inline on default, which is true + /// in all cases except for main. `main` should be supported with special + /// handling in any places where this function determines logic. + pub(crate) fn is_entry_point(&self) -> bool { + match self { + RuntimeType::Acir(inline_type) => match inline_type { + InlineType::Inline => false, + InlineType::Fold => true, + }, + RuntimeType::Brillig => true, + } + } +} + /// A function holds a list of instructions. /// These instructions are further grouped into Basic blocks /// @@ -47,7 +75,7 @@ impl Function { pub(crate) fn new(name: String, id: FunctionId) -> Self { let mut dfg = DataFlowGraph::default(); let entry_block = dfg.make_block(); - Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir } + Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir(InlineType::default()) } } /// The name of the function. @@ -129,12 +157,21 @@ impl Function { impl std::fmt::Display for RuntimeType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - RuntimeType::Acir => write!(f, "acir"), + RuntimeType::Acir(inline_type) => write!(f, "acir({inline_type})"), RuntimeType::Brillig => write!(f, "brillig"), } } } +impl std::fmt::Display for InlineType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InlineType::Inline => write!(f, "inline"), + InlineType::Fold => write!(f, "fold"), + } + } +} + /// FunctionId is a reference for a function /// /// This Id is how each function refers to other functions diff --git a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs index 3cae86829ed..5d743e953a5 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs @@ -74,7 +74,7 @@ mod tests { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::{Function, RuntimeType}, + function::{Function, InlineType, RuntimeType}, map::Id, post_order::PostOrder, types::Type, @@ -112,7 +112,7 @@ mod tests { // D, F, E, B, A, (C dropped as unreachable) let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let block_b_id = builder.insert_block(); let block_c_id = builder.insert_block(); let block_d_id = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs index b737c51d145..6d556e86cb5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs @@ -77,7 +77,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{Binary, BinaryOp, Instruction}, map::Id, types::Type, @@ -100,7 +100,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let one = builder.field_constant(1u128); diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 06ae4bf5202..88948331960 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -283,7 +283,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction}, map::Id, types::Type, @@ -305,7 +305,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let one = builder.field_constant(1u128); @@ -361,7 +361,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(16)); let v1 = builder.add_parameter(Type::unsigned(16)); @@ -415,7 +415,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(16)); let v1 = builder.add_parameter(Type::unsigned(16)); @@ -471,7 +471,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let one = builder.field_constant(1u128); let v1 = builder.insert_binary(v0, BinaryOp::Add, one); @@ -518,7 +518,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(16)); let v1 = builder.insert_cast(v0, Type::unsigned(32)); @@ -562,7 +562,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::bool()); let v1 = builder.add_parameter(Type::bool()); let v2 = builder.add_parameter(Type::bool()); diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index 1f09a132132..ca6527eb0ec 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -13,7 +13,7 @@ use crate::ssa::{ function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - function::{Function, FunctionId, RuntimeType, Signature}, + function::{Function, FunctionId, Signature}, instruction::{BinaryOp, ConstrainError, Instruction}, types::{NumericType, Type}, value::{Value, ValueId}, @@ -304,7 +304,7 @@ fn create_apply_function( ) -> FunctionId { assert!(!function_ids.is_empty()); ssa.add_fn(|id| { - let mut function_builder = FunctionBuilder::new("apply".to_string(), id, RuntimeType::Acir); + let mut function_builder = FunctionBuilder::new("apply".to_string(), id); let target_id = function_builder.add_parameter(Type::field()); let params_ids = vecmap(signature.params, |typ| function_builder.add_parameter(typ)); diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 4c7beff0fbe..df89dcb716d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -169,7 +169,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, Intrinsic}, map::Id, types::Type, @@ -199,7 +199,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let b1 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 46f1e7a2765..e731a7952a6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -167,7 +167,9 @@ impl Ssa { /// For more information, see the module-level comment at the top of this file. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn flatten_cfg(mut self) -> Ssa { - flatten_function_cfg(self.main_mut()); + for function in self.functions.values_mut() { + flatten_function_cfg(function); + } self } } @@ -839,7 +841,7 @@ mod test { function_builder::FunctionBuilder, ir::{ dfg::DataFlowGraph, - function::{Function, RuntimeType}, + function::{Function, InlineType, RuntimeType}, instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, @@ -860,7 +862,7 @@ mod test { // return v1 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -914,7 +916,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -963,7 +965,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1024,7 +1026,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1114,7 +1116,7 @@ mod test { // ↘ ↙ // b9 let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1271,7 +1273,7 @@ mod test { // before the first store to allocate, which loaded an uninitialized value. // In this test we assert the ordering is strictly Allocate then Store then Load. let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1370,7 +1372,7 @@ mod test { // return // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); builder.insert_block(); // entry @@ -1423,7 +1425,7 @@ mod test { // jmp b3() // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); builder.insert_block(); // b0 let b1 = builder.insert_block(); @@ -1533,7 +1535,7 @@ mod test { // jmp b5() // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs index 59bee00936a..adb6d2871e5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs @@ -114,7 +114,12 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{cfg::ControlFlowGraph, function::RuntimeType, map::Id, types::Type}, + ir::{ + cfg::ControlFlowGraph, + function::{InlineType, RuntimeType}, + map::Id, + types::Type, + }, opt::flatten_cfg::branch_analysis::find_branch_ends, }; @@ -134,7 +139,7 @@ mod test { // ↘ ↙ // b9 let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -195,7 +200,7 @@ mod test { // ↘ ↙ // b15 let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index aff06af9921..7c7698d236d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -11,7 +11,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::{CallStack, InsertInstructionResult}, - function::{Function, FunctionId, RuntimeType}, + function::{Function, FunctionId, InlineType, RuntimeType}, instruction::{Instruction, InstructionId, TerminatorInstruction}, value::{Value, ValueId}, }, @@ -94,12 +94,13 @@ struct PerFunctionContext<'function> { } /// The entry point functions are each function we should inline into - and each function that -/// should be left in the final program. This is usually just `main` but also includes any -/// brillig functions used. +/// should be left in the final program. +/// This is the `main` function, any Acir functions with a [fold inline type][InlineType::Fold], +/// and any brillig functions used. fn get_entry_point_functions(ssa: &Ssa) -> BTreeSet { let functions = ssa.functions.iter(); let mut entry_points = functions - .filter(|(_, function)| function.runtime() == RuntimeType::Brillig) + .filter(|(_, function)| function.runtime().is_entry_point()) .map(|(id, _)| *id) .collect::>(); @@ -115,7 +116,8 @@ impl InlineContext { /// that could not be inlined calling it. fn new(ssa: &Ssa, entry_point: FunctionId) -> InlineContext { let source = &ssa.functions[&entry_point]; - let builder = FunctionBuilder::new(source.name().to_owned(), entry_point, source.runtime()); + let mut builder = FunctionBuilder::new(source.name().to_owned(), entry_point); + builder.set_runtime(source.runtime()); Self { builder, recursion_level: 0, entry_point, call_stack: CallStack::new() } } @@ -350,8 +352,12 @@ impl<'function> PerFunctionContext<'function> { match &self.source_function.dfg[*id] { Instruction::Call { func, arguments } => match self.get_function(*func) { Some(function) => match ssa.functions[&function].runtime() { - RuntimeType::Acir => self.inline_function(ssa, *id, function, arguments), - RuntimeType::Brillig => self.push_instruction(*id), + RuntimeType::Acir(InlineType::Inline) => { + self.inline_function(ssa, *id, function, arguments); + } + RuntimeType::Acir(InlineType::Fold) | RuntimeType::Brillig => { + self.push_instruction(*id); + } }, None => self.push_instruction(*id), }, @@ -517,7 +523,7 @@ mod test { function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, Intrinsic, TerminatorInstruction}, map::Id, types::Type, @@ -536,14 +542,14 @@ mod test { // return 72 // } let foo_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("foo".into(), foo_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("foo".into(), foo_id); let bar_id = Id::test_new(1); let bar = builder.import_function(bar_id); let results = builder.insert_call(bar, Vec::new(), vec![Type::field()]).to_vec(); builder.terminate_with_return(results); - builder.new_function("bar".into(), bar_id); + builder.new_function("bar".into(), bar_id, InlineType::default()); let expected_return = 72u128; let seventy_two = builder.field_constant(expected_return); builder.terminate_with_return(vec![seventy_two]); @@ -585,7 +591,7 @@ mod test { let id2_id = Id::test_new(3); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let main_v0 = builder.add_parameter(Type::field()); let main_f1 = builder.import_function(square_id); @@ -598,18 +604,18 @@ mod test { builder.terminate_with_return(vec![main_v16]); // Compiling square f1 - builder.new_function("square".into(), square_id); + builder.new_function("square".into(), square_id, InlineType::default()); let square_v0 = builder.add_parameter(Type::field()); let square_v2 = builder.insert_binary(square_v0, BinaryOp::Mul, square_v0); builder.terminate_with_return(vec![square_v2]); // Compiling id1 f2 - builder.new_function("id1".into(), id1_id); + builder.new_function("id1".into(), id1_id, InlineType::default()); let id1_v0 = builder.add_parameter(Type::Function); builder.terminate_with_return(vec![id1_v0]); // Compiling id2 f3 - builder.new_function("id2".into(), id2_id); + builder.new_function("id2".into(), id2_id, InlineType::default()); let id2_v0 = builder.add_parameter(Type::Function); builder.terminate_with_return(vec![id2_v0]); @@ -641,7 +647,7 @@ mod test { // return v4 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let factorial_id = Id::test_new(1); let factorial = builder.import_function(factorial_id); @@ -650,7 +656,7 @@ mod test { let results = builder.insert_call(factorial, vec![five], vec![Type::field()]).to_vec(); builder.terminate_with_return(results); - builder.new_function("factorial".into(), factorial_id); + builder.new_function("factorial".into(), factorial_id, InlineType::default()); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -741,7 +747,7 @@ mod test { // jmp b3(Field 2) // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let main_cond = builder.add_parameter(Type::bool()); let inner1_id = Id::test_new(1); @@ -751,14 +757,14 @@ mod test { builder.insert_call(assert_constant, vec![main_v2], vec![]); builder.terminate_with_return(vec![]); - builder.new_function("inner1".into(), inner1_id); + builder.new_function("inner1".into(), inner1_id, InlineType::default()); let inner1_cond = builder.add_parameter(Type::bool()); let inner2_id = Id::test_new(2); let inner2 = builder.import_function(inner2_id); let inner1_v2 = builder.insert_call(inner2, vec![inner1_cond], vec![Type::field()])[0]; builder.terminate_with_return(vec![inner1_v2]); - builder.new_function("inner2".into(), inner2_id); + builder.new_function("inner2".into(), inner2_id, InlineType::default()); let inner2_cond = builder.add_parameter(Type::bool()); let then_block = builder.insert_block(); let else_block = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 0a49ca4ecca..f1a38585bd6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -414,7 +414,7 @@ mod tests { ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, @@ -433,7 +433,7 @@ mod tests { // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let v0 = builder.insert_allocate(Type::Array(Rc::new(vec![Type::field()]), 2)); let one = builder.field_constant(FieldElement::one()); let two = builder.field_constant(FieldElement::one()); @@ -474,7 +474,7 @@ mod tests { // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let v0 = builder.insert_allocate(Type::field()); let one = builder.field_constant(FieldElement::one()); builder.insert_store(v0, one); @@ -508,7 +508,7 @@ mod tests { // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let v0 = builder.insert_allocate(Type::field()); let const_one = builder.field_constant(FieldElement::one()); builder.insert_store(v0, const_one); @@ -567,7 +567,7 @@ mod tests { // return v2, v3, v4 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.insert_allocate(Type::field()); @@ -647,7 +647,7 @@ mod tests { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.insert_allocate(Type::field()); diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index 4766bc3e8d2..7b5196f2004 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -166,8 +166,12 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::Instruction, map::Id, types::Type, + basic_block::BasicBlockId, + dfg::DataFlowGraph, + function::{InlineType, RuntimeType}, + instruction::Instruction, + map::Id, + types::Type, }, }; @@ -206,7 +210,8 @@ mod test { // return [v0] // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("foo".into(), main_id, RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("foo".into(), main_id); + builder.set_runtime(RuntimeType::Brillig); let inner_array_type = Type::Array(Rc::new(vec![Type::field()]), 2); let v0 = builder.add_parameter(inner_array_type.clone()); @@ -245,7 +250,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("mutator".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("mutator".into(), main_id); let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); let v0 = builder.add_parameter(array_type.clone()); @@ -294,7 +299,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("mutator2".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("mutator2".into(), main_id); let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); let reference_type = Type::Reference(Rc::new(array_type.clone())); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index a71a42d5757..1eb1e20d41d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -20,7 +20,9 @@ impl Ssa { /// See [`constant_folding`][self] module for more information. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn remove_bit_shifts(mut self) -> Ssa { - remove_bit_shifts(self.main_mut()); + for function in self.functions.values_mut() { + remove_bit_shifts(function); + } self } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index a31def8fd98..b9675d99c90 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -154,7 +154,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, TerminatorInstruction}, map::Id, types::Type, @@ -172,7 +172,7 @@ mod test { // return v1 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -228,7 +228,7 @@ mod test { // return Field 2 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::bool()); let b1 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 645adb6b350..92ec1e8f1bb 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -48,7 +48,7 @@ impl Ssa { // This check is always true with the addition of the above guard, but I'm // keeping it in case the guard on brillig functions is ever removed. - let abort_on_error = function.runtime() == RuntimeType::Acir; + let abort_on_error = matches!(function.runtime(), RuntimeType::Acir(_)); find_all_loops(function).unroll_each_loop(function, abort_on_error)?; } Ok(self) @@ -464,7 +464,12 @@ impl<'f> LoopIteration<'f> { mod tests { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{function::RuntimeType, instruction::BinaryOp, map::Id, types::Type}, + ir::{ + function::{InlineType, RuntimeType}, + instruction::BinaryOp, + map::Id, + types::Type, + }, }; #[test] @@ -503,7 +508,7 @@ mod tests { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -605,7 +610,7 @@ mod tests { // return Field 0 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index a9a707ca8ed..569e543ce40 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -12,8 +12,8 @@ use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::basic_block::BasicBlockId; use crate::ssa::ir::dfg::DataFlowGraph; -use crate::ssa::ir::function::FunctionId as IrFunctionId; use crate::ssa::ir::function::{Function, RuntimeType}; +use crate::ssa::ir::function::{FunctionId as IrFunctionId, InlineType}; use crate::ssa::ir::instruction::BinaryOp; use crate::ssa::ir::instruction::Instruction; use crate::ssa::ir::map::AtomicCounter; @@ -108,7 +108,8 @@ impl<'a> FunctionContext<'a> { .expect("No function in queue for the FunctionContext to compile") .1; - let builder = FunctionBuilder::new(function_name, function_id, runtime); + let mut builder = FunctionBuilder::new(function_name, function_id); + builder.set_runtime(runtime); let definitions = HashMap::default(); let mut this = Self { definitions, builder, shared_context, loops: Vec::new() }; this.add_parameters_to_scope(parameters); @@ -125,7 +126,8 @@ impl<'a> FunctionContext<'a> { if func.unconstrained { self.builder.new_brillig_function(func.name.clone(), id); } else { - self.builder.new_function(func.name.clone(), id); + let inline_type = if func.should_fold { InlineType::Fold } else { InlineType::Inline }; + self.builder.new_function(func.name.clone(), id, inline_type); } self.add_parameters_to_scope(&func.parameters); } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index a39df14d60a..3fe52f7f0e5 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -14,7 +14,10 @@ use noirc_frontend::{ use crate::{ errors::{InternalError, RuntimeError}, - ssa::{function_builder::data_bus::DataBusBuilder, ir::instruction::Intrinsic}, + ssa::{ + function_builder::data_bus::DataBusBuilder, + ir::{function::InlineType, instruction::Intrinsic}, + }, }; use self::{ @@ -52,14 +55,15 @@ pub(crate) fn generate_ssa( // Queue the main function for compilation context.get_or_queue_function(main_id); - let mut function_context = FunctionContext::new( main.name.clone(), &main.parameters, if force_brillig_runtime || main.unconstrained { RuntimeType::Brillig } else { - RuntimeType::Acir + let main_inline_type = + if main.should_fold { InlineType::Fold } else { InlineType::Inline }; + RuntimeType::Acir(main_inline_type) }, &context, ); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index 509f778f3b0..9995c031145 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -12,6 +12,7 @@ pub(crate) struct Ssa { pub(crate) functions: BTreeMap, pub(crate) main_id: FunctionId, pub(crate) next_id: AtomicCounter, + pub(crate) id_to_index: BTreeMap, } impl Ssa { @@ -26,7 +27,9 @@ impl Ssa { (f.id(), f) }); - Self { functions, main_id, next_id: AtomicCounter::starting_after(max_id) } + let id_to_index = btree_map(functions.iter().enumerate(), |(i, (id, _))| (*id, i as u32)); + + Self { functions, main_id, next_id: AtomicCounter::starting_after(max_id), id_to_index } } /// Returns the entry-point function of the program diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index a3a8d460572..03b92e15032 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -26,6 +26,7 @@ tracing.workspace = true petgraph = "0.6" [dev-dependencies] +base64.workspace = true strum = "0.24" strum_macros = "0.24" tempfile.workspace = true diff --git a/compiler/noirc_frontend/build.rs b/compiler/noirc_frontend/build.rs new file mode 100644 index 00000000000..53ae9489168 --- /dev/null +++ b/compiler/noirc_frontend/build.rs @@ -0,0 +1,15 @@ +use std::path::PathBuf; + +const BLNS_JSON_PATH: &str = "BLNS_JSON_PATH"; + +fn main() -> Result<(), String> { + let out_dir = std::env::var("OUT_DIR").unwrap(); + + let dest_path = PathBuf::from(out_dir.clone()).join("blns.base64.json"); + let dest_path_str = dest_path.to_str().unwrap(); + + println!("cargo:rustc-env={BLNS_JSON_PATH}={dest_path_str}"); + std::fs::copy("./src/blns/blns.base64.json", dest_path).unwrap(); + + Ok(()) +} diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index 3e8b78c1312..10d962a23f7 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -108,6 +108,7 @@ impl From for NoirFunction { Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal, Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, Some(FunctionAttribute::Recursive) => FunctionKind::Recursive, + Some(FunctionAttribute::Fold) => FunctionKind::Normal, None => FunctionKind::Normal, }; diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index b14ead8ad42..dea9fc0f3d3 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -120,7 +120,7 @@ impl StatementKind { // Desugar `a = b` to `a = a b`. This relies on the evaluation of `a` having no side effects, // which is currently enforced by the restricted syntax of LValues. if operator != Token::Assign { - let lvalue_expr = lvalue.as_expression(span); + let lvalue_expr = lvalue.as_expression(); let error_msg = "Token passed to Statement::assign is not a binary operator"; let infix = crate::InfixExpression { @@ -425,9 +425,9 @@ pub struct AssignStatement { #[derive(Debug, PartialEq, Eq, Clone)] pub enum LValue { Ident(Ident), - MemberAccess { object: Box, field_name: Ident }, - Index { array: Box, index: Expression }, - Dereference(Box), + MemberAccess { object: Box, field_name: Ident, span: Span }, + Index { array: Box, index: Expression, span: Span }, + Dereference(Box, Span), } #[derive(Debug, PartialEq, Eq, Clone)] @@ -484,28 +484,40 @@ impl Recoverable for Pattern { } impl LValue { - fn as_expression(&self, span: Span) -> Expression { + fn as_expression(&self) -> Expression { let kind = match self { LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), - LValue::MemberAccess { object, field_name } => { + LValue::MemberAccess { object, field_name, span: _ } => { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: object.as_expression(span), + lhs: object.as_expression(), rhs: field_name.clone(), })) } - LValue::Index { array, index } => ExpressionKind::Index(Box::new(IndexExpression { - collection: array.as_expression(span), - index: index.clone(), - })), - LValue::Dereference(lvalue) => { + LValue::Index { array, index, span: _ } => { + ExpressionKind::Index(Box::new(IndexExpression { + collection: array.as_expression(), + index: index.clone(), + })) + } + LValue::Dereference(lvalue, _span) => { ExpressionKind::Prefix(Box::new(crate::PrefixExpression { operator: crate::UnaryOp::Dereference { implicitly_added: false }, - rhs: lvalue.as_expression(span), + rhs: lvalue.as_expression(), })) } }; + let span = self.span(); Expression::new(kind, span) } + + pub fn span(&self) -> Span { + match self { + LValue::Ident(ident) => ident.span(), + LValue::MemberAccess { span, .. } + | LValue::Index { span, .. } + | LValue::Dereference(_, span) => *span, + } + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -675,9 +687,11 @@ impl Display for LValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LValue::Ident(ident) => ident.fmt(f), - LValue::MemberAccess { object, field_name } => write!(f, "{object}.{field_name}"), - LValue::Index { array, index } => write!(f, "{array}[{index}]"), - LValue::Dereference(lvalue) => write!(f, "*{lvalue}"), + LValue::MemberAccess { object, field_name, span: _ } => { + write!(f, "{object}.{field_name}") + } + LValue::Index { array, index, span: _ } => write!(f, "{array}[{index}]"), + LValue::Dereference(lvalue, _span) => write!(f, "*{lvalue}"), } } } diff --git a/compiler/noirc_frontend/src/blns/LICENSE b/compiler/noirc_frontend/src/blns/LICENSE new file mode 100644 index 00000000000..4ab5bbd793b --- /dev/null +++ b/compiler/noirc_frontend/src/blns/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2020 Max Woolf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/compiler/noirc_frontend/src/blns/README.md b/compiler/noirc_frontend/src/blns/README.md new file mode 100644 index 00000000000..201ed68aef0 --- /dev/null +++ b/compiler/noirc_frontend/src/blns/README.md @@ -0,0 +1,9 @@ +# BLNS + +[Big List of Naughty Strings](https://github.com/minimaxir/big-list-of-naughty-strings) + +## Updating + +```bash +wget -O blns.base64.json "https://raw.githubusercontent.com/minimaxir/big-list-of-naughty-strings/master/blns.base64.json" +``` diff --git a/compiler/noirc_frontend/src/blns/blns.base64.json b/compiler/noirc_frontend/src/blns/blns.base64.json new file mode 100644 index 00000000000..18e61c0d2c4 --- /dev/null +++ b/compiler/noirc_frontend/src/blns/blns.base64.json @@ -0,0 +1,679 @@ +[ + "", + "dW5kZWZpbmVk", + "dW5kZWY=", + "bnVsbA==", + "TlVMTA==", + "KG51bGwp", + "bmls", + "TklM", + "dHJ1ZQ==", + "ZmFsc2U=", + "VHJ1ZQ==", + "RmFsc2U=", + "VFJVRQ==", + "RkFMU0U=", + "Tm9uZQ==", + "aGFzT3duUHJvcGVydHk=", + "XA==", + "MA==", + "MQ==", + "MS4wMA==", + "JDEuMDA=", + "MS8y", + "MUUy", + "MUUwMg==", + "MUUrMDI=", + "LTE=", + "LTEuMDA=", + "LSQxLjAw", + "LTEvMg==", + "LTFFMg==", + "LTFFMDI=", + "LTFFKzAy", + "MS8w", + "MC8w", + "LTIxNDc0ODM2NDgvLTE=", + "LTkyMjMzNzIwMzY4NTQ3NzU4MDgvLTE=", + "LTA=", + "LTAuMA==", + "KzA=", + "KzAuMA==", + "MC4wMA==", + "MC4uMA==", + "Lg==", + "MC4wLjA=", + "MCwwMA==", + "MCwsMA==", + "LA==", + "MCwwLDA=", + "MC4wLzA=", + "MS4wLzAuMA==", + "MC4wLzAuMA==", + "MSwwLzAsMA==", + "MCwwLzAsMA==", + "LS0x", + "LQ==", + "LS4=", + "LSw=", + "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5", + "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5", + "TmFO", + "SW5maW5pdHk=", + "LUluZmluaXR5", + "SU5G", + "MSNJTkY=", + "LTEjSU5E", + "MSNRTkFO", + "MSNTTkFO", + "MSNJTkQ=", + "MHgw", + "MHhmZmZmZmZmZg==", + "MHhmZmZmZmZmZmZmZmZmZmZm", + "MHhhYmFkMWRlYQ==", + "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5", + "MSwwMDAuMDA=", + "MSAwMDAuMDA=", + "MScwMDAuMDA=", + "MSwwMDAsMDAwLjAw", + "MSAwMDAgMDAwLjAw", + "MScwMDAnMDAwLjAw", + "MS4wMDAsMDA=", + "MSAwMDAsMDA=", + "MScwMDAsMDA=", + "MS4wMDAuMDAwLDAw", + "MSAwMDAgMDAwLDAw", + "MScwMDAnMDAwLDAw", + "MDEwMDA=", + "MDg=", + "MDk=", + "Mi4yMjUwNzM4NTg1MDcyMDExZS0zMDg=", + "LC4vOydbXS09", + "PD4/OiJ7fXxfKw==", + "IUAjJCVeJiooKWB+", + "AQIDBAUGBwgODxAREhMUFRYXGBkaGxwdHh9/", + "woDCgcKCwoPChMKGwofCiMKJworCi8KMwo3CjsKPwpDCkcKSwpPClMKVwpbCl8KYwpnCmsKbwpzC", + "ncKewp8=", + "CwwgwoXCoOGagOKAgOKAgeKAguKAg+KAhOKAheKAhuKAh+KAiOKAieKAiuKAi+KAqOKAqeKAr+KB", + "n+OAgA==", + "wq3YgNiB2ILYg9iE2IXYnNud3I/hoI7igIvigIzigI3igI7igI/igKrigKvigKzigK3igK7igaDi", + "gaHigaLigaPigaTigabigafigajiganigarigavigaziga3iga7iga/vu7/vv7nvv7rvv7vwkYK9", + "8JuyoPCbsqHwm7Ki8Juyo/CdhbPwnYW08J2FtfCdhbbwnYW38J2FuPCdhbnwnYW686CAgfOggKDz", + "oICh86CAovOggKPzoICk86CApfOggKbzoICn86CAqPOggKnzoICq86CAq/OggKzzoICt86CArvOg", + "gK/zoICw86CAsfOggLLzoICz86CAtPOggLXzoIC286CAt/OggLjzoIC586CAuvOggLvzoIC886CA", + "vfOggL7zoIC/86CBgPOggYHzoIGC86CBg/OggYTzoIGF86CBhvOggYfzoIGI86CBifOggYrzoIGL", + "86CBjPOggY3zoIGO86CBj/OggZDzoIGR86CBkvOggZPzoIGU86CBlfOggZbzoIGX86CBmPOggZnz", + "oIGa86CBm/OggZzzoIGd86CBnvOggZ/zoIGg86CBofOggaLzoIGj86CBpPOggaXzoIGm86CBp/Og", + "gajzoIGp86CBqvOggavzoIGs86CBrfOgga7zoIGv86CBsPOggbHzoIGy86CBs/OggbTzoIG186CB", + "tvOggbfzoIG486CBufOggbrzoIG786CBvPOggb3zoIG+86CBvw==", + "77u/", + "77++", + "zqniiYjDp+KImuKIq8ucwrXiiaTiiaXDtw==", + "w6XDn+KIgsaSwqnLmeKIhsuawqzigKbDpg==", + "xZPiiJHCtMKu4oCgwqXCqMuGw7jPgOKAnOKAmA==", + "wqHihKLCo8Ki4oiewqfCtuKAosKqwrrigJPiiaA=", + "wrjLm8OH4peKxLHLnMOCwq/LmMK/", + "w4XDjcOOw4/LncOTw5Tvo7/DksOaw4bimIM=", + "xZLigJ7CtOKAsMuHw4HCqMuGw5jiiI/igJ3igJk=", + "YOKBhOKCrOKAueKAuu+sge+sguKAocKwwrfigJrigJTCsQ==", + "4oWb4oWc4oWd4oWe", + "0IHQgtCD0ITQhdCG0IfQiNCJ0IrQi9CM0I3QjtCP0JDQkdCS0JPQlNCV0JbQl9CY0JnQmtCb0JzQ", + "ndCe0J/QoNCh0KLQo9Ck0KXQptCn0KjQqdCq0KvQrNCt0K7Qr9Cw0LHQstCz0LTQtdC20LfQuNC5", + "0LrQu9C80L3QvtC/0YDRgdGC0YPRhNGF0YbRh9GI0YnRitGL0YzRjdGO0Y8=", + "2aDZodmi2aPZpNml2abZp9mo2ak=", + "4oGw4oG04oG1", + "4oKA4oKB4oKC", + "4oGw4oG04oG14oKA4oKB4oKC", + "4LiU4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH", + "4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ", + "4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH", + "4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmH", + "4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmHIOC4lOC5ieC5ieC5ieC5ieC5", + "ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5", + "ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5", + "h+C5ieC5ieC5ieC5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5", + "h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5", + "ieC5ieC5ieC5ieC5h+C5h+C5h+C5hyDguJTguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfg", + "uYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYnguYnguYnguYnguYfg", + "uYfguYfguYfguYfguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYng", + "uYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYng", + "uYnguYnguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYnguYfguYfg", + "uYfguYc=", + "Jw==", + "Ig==", + "Jyc=", + "IiI=", + "JyIn", + "IicnJyciJyI=", + "IiciJyInJycnIg==", + "PGZvbyB2YWw94oCcYmFy4oCdIC8+", + "PGZvbyB2YWw94oCcYmFy4oCdIC8+", + "PGZvbyB2YWw94oCdYmFy4oCcIC8+", + "PGZvbyB2YWw9YGJhcicgLz4=", + "55Sw5Lit44GV44KT44Gr44GC44GS44Gm5LiL44GV44GE", + "44OR44O844OG44Kj44O844G46KGM44GL44Gq44GE44GL", + "5ZKM6KO95ryi6Kqe", + "6YOo6JC95qC8", + "7IKs7ZqM6rO87ZWZ7JuQIOyWtO2VmeyXsOq1rOyGjA==", + "7LCm7LCo66W8IO2DgOqzoCDsmKgg7Y6y7Iuc66eo6rO8IOyRm+uLpOumrCDrmKDrsKnqsIHtlZg=", + "56S+5pyD56eR5a246Zmi6Kqe5a2456CU56m25omA", + "7Jq4656A67CU7Yag66W0", + "8KCcjvCgnLHwoJ258KCxk/CgsbjwoLKW8KCzjw==", + "6KGo44Od44GCQem3l8WSw6nvvKLpgI3DnMOfwqrEhcOx5LiC45CA8KCAgA==", + "44O94Ly84LqI2YTNnOC6iOC8ve++iSDjg73gvLzguojZhM2c4LqI4Ly9776J", + "KO+9oeKXlSDiiIAg4peV772hKQ==", + "772A772oKMK04oiA772A4oip", + "X1/vvpsoLF8sKik=", + "44O7KO+/o+KIgO+/oynjg7s6Kjo=", + "776f772l4py/44O+4pWyKO+9oeKXleKAv+KXle+9oSnilbHinL/vvaXvvp8=", + "LOOAguODuzoqOuODu+OCnOKAmSgg4pi7IM+JIOKYuyAp44CC44O7Oio644O744Kc4oCZ", + "KOKVr8Kw4pahwrDvvInila/vuLUg4pS74pSB4pS7KQ==", + "KO++ieCypeebiuCype+8ie++ie+7vyDilLvilIHilLs=", + "4pSs4pSA4pSs44OOKCDCuiBfIMK644OOKQ==", + "KCDNocKwIM2cypYgzaHCsCk=", + "8J+YjQ==", + "8J+RqfCfj70=", + "8J+RqOKAjfCfprAg8J+RqPCfj7/igI3wn6awIPCfkajigI3wn6axIPCfkajwn4+/4oCN8J+msSDwn6a58J+Pv+KAjeKZgu+4jw==", + "8J+RviDwn5mHIPCfkoEg8J+ZhSDwn5mGIPCfmYsg8J+ZjiDwn5mN", + "8J+QtSDwn5mIIPCfmYkg8J+Zig==", + "4p2k77iPIPCfkpQg8J+SjCDwn5KVIPCfkp4g8J+SkyDwn5KXIPCfkpYg8J+SmCDwn5KdIPCfkp8g", + "8J+SnCDwn5KbIPCfkpog8J+SmQ==", + "4pyL8J+PvyDwn5Kq8J+PvyDwn5GQ8J+PvyDwn5mM8J+PvyDwn5GP8J+PvyDwn5mP8J+Pvw==", + "8J+aviDwn4aSIPCfhpMg8J+GlSDwn4aWIPCfhpcg8J+GmSDwn4+n", + "MO+4j+KDoyAx77iP4oOjIDLvuI/ig6MgM++4j+KDoyA077iP4oOjIDXvuI/ig6MgNu+4j+KDoyA3", + "77iP4oOjIDjvuI/ig6MgOe+4j+KDoyDwn5Sf", + "8J+HuvCfh7jwn4e38J+HuvCfh7gg8J+HpvCfh6vwn4em8J+HsvCfh7g=", + "8J+HuvCfh7jwn4e38J+HuvCfh7jwn4em8J+Hq/Cfh6bwn4ey", + "8J+HuvCfh7jwn4e38J+HuvCfh7jwn4em", + "77yR77yS77yT", + "2aHZotmj", + "2KvZhSDZhtmB2LMg2LPZgti32Kog2YjYqNin2YTYqtit2K/Zitiv2IwsINis2LLZitix2KrZiiDY", + "qNin2LPYqtiu2K/Yp9mFINij2YYg2K/ZhtmILiDYpdiwINmH2YbYp9ifINin2YTYs9iq2KfYsSDZ", + "iNiq2YbYtdmK2Kgg2YPYp9mGLiDYo9mH2ZHZhCDYp9mK2LfYp9mE2YrYp9iMINio2LHZiti32KfZ", + "htmK2Kct2YHYsdmG2LPYpyDZgtivINij2K7YsC4g2LPZhNmK2YXYp9mG2Iwg2KXYqtmB2KfZgtmK", + "2Kkg2KjZitmGINmF2KcsINmK2LDZg9ixINin2YTYrdiv2YjYryDYo9mKINio2LnYrywg2YXYudin", + "2YXZhNipINio2YjZhNmG2K/Yp9iMINin2YTYpdi32YTYp9mCINi52YQg2KXZitmILg==", + "15HWsNa816jWtdeQ16nWtNeB15nXqiwg15HWuNa816jWuNeQINeQ1rHXnNa515TWtNeZ150sINeQ", + "1rXXqiDXlNa316nWuNa814HXnta315nWtNedLCDXldaw15DWtdeqINeU1rjXkNa416jWttel", + "15TWuNeZ1rDXqta415R0ZXN02KfZhNi12YHYrdin2Kog2KfZhNiq2ZHYrdmI2YQ=", + "77e9", + "77e6", + "2YXZj9mG2Y7Yp9mC2Y7YtNmO2KnZjyDYs9mP2KjZj9mE2ZAg2KfZkNiz2ZLYqtmQ2K7Zktiv2Y7Y", + "p9mF2ZAg2KfZhNmE2ZHZj9i62Y7YqdmQINmB2ZDZiiDYp9mE2YbZkdmP2LjZj9mF2ZAg2KfZhNmS", + "2YLZjtin2KbZkNmF2Y7YqdmQINmI2Y7ZgdmQ2YrZhSDZitmO2K7Zj9i12ZHZjiDYp9mE2KrZkdmO", + "2LfZktio2ZDZitmC2Y7Yp9iq2Y8g2KfZhNmS2K3Yp9iz2Y/ZiNio2ZDZitmR2Y7YqdmP2Iw=", + "4Zqb4ZqE4ZqT4ZqQ4ZqL4ZqS4ZqE4ZqA4ZqR4ZqE4ZqC4ZqR4ZqP4ZqF4Zqc", + "4Zqb4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqcCg==", + "4oCq4oCqdGVzdOKAqg==", + "4oCrdGVzdOKAqw==", + "4oCpdGVzdOKAqQ==", + "dGVzdOKBoHRlc3TigKs=", + "4oGmdGVzdOKBpw==", + "4bmwzLrMusyVb82eIMy3acyyzKzNh8yqzZluzJ3Ml82VdsyfzJzMmMymzZ9vzLbMmcywzKBrw6jN", + "msyuzLrMqsy5zLHMpCDMlnTMnc2VzLPMo8y7zKrNnmjMvM2TzLLMpsyzzJjMsmXNh8yjzLDMpsys", + "zY4gzKLMvMy7zLHMmGjNms2OzZnMnMyjzLLNhWnMpsyyzKPMsMykdsy7zY1lzLrMrcyzzKrMsC1t", + "zKJpzYVuzJbMusyezLLMr8ywZMy1zLzMn82ZzKnMvMyYzLMgzJ7MpcyxzLPMrXLMm8yXzJhlzZlw", + "zaByzLzMnsy7zK3Ml2XMusygzKPNn3PMmM2HzLPNjcydzYllzYnMpcyvzJ7Mss2azKzNnMe5zKzN", + "js2OzJ/Mls2HzKR0zY3MrMykzZPMvMytzZjNhWnMqsyxbs2gZ8y0zYkgzY/Nic2FY8yszJ9ozaFh", + "zKvMu8yvzZhvzKvMn8yWzY3MmcydzYlzzJfMpsyyLsyozLnNiMyj", + "zKHNk8yezYVJzJfMmMymzZ1uzYfNh82ZdsyuzKtva8yyzKvMmc2IacyWzZnMrcy5zKDMnm7Mocy7", + "zK7Mo8y6Z8yyzYjNmcytzZnMrM2OIMywdM2UzKZozJ7MsmXMosykIM2NzKzMss2WZsy0zJjNlcyj", + "w6jNluG6ucylzKlszZbNlM2aac2TzZrMps2gbs2WzY3Ml82TzLPMrmfNjSDMqG/NmsyqzaFmzJjM", + "o8ysIMyWzJjNlsyfzZnMrmPSic2UzKvNls2TzYfNls2FaMy1zKTMo82azZTDocyXzLzNlc2Fb8y8", + "zKPMpXPMsc2IzLrMlsymzLvNoi7Mm8yWzJ7MoMyrzLA=", + "zJfMus2WzLnMr82T4bmuzKTNjcylzYfNiGjMssyBZc2PzZPMvMyXzJnMvMyjzZQgzYfMnMyxzKDN", + "k82NzYVOzZXNoGXMl8yxesyYzJ3MnMy6zZlwzKTMusy5zY3Mr82aZcygzLvMoM2ccsyozKTNjcy6", + "zJbNlMyWzJZkzKDMn8ytzKzMnc2facymzZbMqc2TzZTMpGHMoMyXzKzNicyZbs2azZwgzLvMnsyw", + "zZrNhWjMtc2JacyzzJ52zKLNh+G4mc2OzZ8t0onMrcypzLzNlG3MpMytzKtpzZXNh8ydzKZuzJfN", + "meG4jcyfIMyvzLLNlc2ex6vMn8yvzLDMss2ZzLvMnWYgzKrMsMywzJfMlsytzJjNmGPMps2NzLLM", + "ns2NzKnMmeG4pc2aYcyuzY7Mn8yZzZzGocypzLnNjnPMpC7MncydINKJWsyhzJbMnM2WzLDMo82J", + "zJxhzZbMsM2ZzKzNoWzMssyrzLPNjcypZ8yhzJ/MvMyxzZrMnsyszYVvzJfNnC7Mnw==", + "zKZIzKzMpMyXzKTNnWXNnCDMnMylzJ3Mu82NzJ/MgXfMlWjMlsyvzZNvzJ3NmcyWzY7MscyuINKJ", + "zLrMmcyezJ/NiFfMt8y8zK1hzLrMqs2NxK/NiM2VzK3NmcyvzJx0zLbMvMyuc8yYzZnNlsyVIMyg", + "zKvMoELMu82NzZnNicyzzYVlzLVozLXMrM2HzKvNmWnMuc2TzLPMs8yuzY7Mq8yVbs2fZMy0zKrM", + "nMyWIMywzYnMqc2HzZnMss2ezYVUzZbMvM2TzKrNomjNj82TzK7Mu2XMrMydzJ/NhSDMpMy5zJ1X", + "zZnMnsydzZTNh82dzYVhzY/Nk82UzLnMvMyjbMy0zZTMsMykzJ/NlOG4vcyrLs2V", + "WsyuzJ7MoM2ZzZTNheG4gMyXzJ7NiMy7zJfhuLbNmc2OzK/MucyezZNHzLtPzK3Ml8yu", + "y5nJkG5i4bSJbMmQIMmQdcaDyZDJryDHncm5b2xvcCDKh8edIMedyblvccmQbCDKh24gyod1bnDh", + "tIlw4bSJyZR14bSJIMm5b2TJr8edyocgcG/Jr3Nu4bSJx50gb3AgcMedcyAnyofhtIlsx50gxoN1", + "4bSJyZRz4bSJZOG0iXDJkCDJuW7Kh8edyofJlMedc3VvyZQgJ8qHx53Jr8mQIMqH4bSJcyDJuW9s", + "b3Agya9uc2ThtIkgya/Hncm5b8ul", + "MDDLmcaWJC0=", + "77y0772I772FIO+9ke+9le+9ie+9g++9iyDvvYLvvZLvvY/vvZfvvY4g772G772P772YIO+9iu+9", + "le+9je+9kO+9kyDvvY/vvZbvvYXvvZIg772U772I772FIO+9jO+9ge+9mu+9mSDvvYTvvY/vvYc=", + "8J2Qk/CdkKHwnZCeIPCdkKrwnZCu8J2QovCdkJzwnZCkIPCdkJvwnZCr8J2QqPCdkLDwnZCnIPCd", + "kJ/wnZCo8J2QsSDwnZCj8J2QrvCdkKbwnZCp8J2QrCDwnZCo8J2Qr/CdkJ7wnZCrIPCdkK3wnZCh", + "8J2QniDwnZCl8J2QmvCdkLPwnZCyIPCdkJ3wnZCo8J2QoA==", + "8J2Vv/Cdlo3wnZaKIPCdlpbwnZaa8J2WjvCdlojwnZaQIPCdlofwnZaX8J2WlPCdlpzwnZaTIPCd", + "lovwnZaU8J2WnSDwnZaP8J2WmvCdlpLwnZaV8J2WmCDwnZaU8J2Wm/CdlorwnZaXIPCdlpnwnZaN", + "8J2WiiDwnZaR8J2WhvCdlp/wnZaeIPCdlonwnZaU8J2WjA==", + "8J2Ru/CdkonwnZKGIPCdkpLwnZKW8J2SivCdkoTwnZKMIPCdkoPwnZKT8J2SkPCdkpjwnZKPIPCd", + "kofwnZKQ8J2SmSDwnZKL8J2SlvCdko7wnZKR8J2SlCDwnZKQ8J2Sl/CdkobwnZKTIPCdkpXwnZKJ", + "8J2ShiDwnZKN8J2SgvCdkpvwnZKaIPCdkoXwnZKQ8J2SiA==", + "8J2To/Cdk7HwnZOuIPCdk7rwnZO+8J2TsvCdk6zwnZO0IPCdk6vwnZO78J2TuPCdlIDwnZO3IPCd", + "k6/wnZO48J2UgSDwnZOz8J2TvvCdk7bwnZO58J2TvCDwnZO48J2Tv/Cdk67wnZO7IPCdk73wnZOx", + "8J2TriDwnZO18J2TqvCdlIPwnZSCIPCdk63wnZO48J2TsA==", + "8J2Vi/CdlZnwnZWWIPCdlaLwnZWm8J2VmvCdlZTwnZWcIPCdlZPwnZWj8J2VoPCdlajwnZWfIPCd", + "lZfwnZWg8J2VqSDwnZWb8J2VpvCdlZ7wnZWh8J2VpCDwnZWg8J2Vp/CdlZbwnZWjIPCdlaXwnZWZ", + "8J2VliDwnZWd8J2VkvCdlavwnZWqIPCdlZXwnZWg8J2VmA==", + "8J2ag/CdmpHwnZqOIPCdmprwnZqe8J2akvCdmozwnZqUIPCdmovwnZqb8J2amPCdmqDwnZqXIPCd", + "mo/wnZqY8J2aoSDwnZqT8J2anvCdmpbwnZqZ8J2anCDwnZqY8J2an/Cdmo7wnZqbIPCdmp3wnZqR", + "8J2ajiDwnZqV8J2aivCdmqPwnZqiIPCdmo3wnZqY8J2akA==", + "4pKv4pKj4pKgIOKSrOKSsOKSpOKSnuKSpiDikp3ikq3ikqrikrLikqkg4pKh4pKq4pKzIOKSpeKS", + "sOKSqOKSq+KSriDikqrikrHikqDikq0g4pKv4pKj4pKgIOKSp+KSnOKSteKStCDikp/ikqrikqI=", + "PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+", + "Jmx0O3NjcmlwdCZndDthbGVydCgmIzM5OzEyMyYjMzk7KTsmbHQ7L3NjcmlwdCZndDs=", + "PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEyMykgLz4=", + "PHN2Zz48c2NyaXB0PjEyMzwxPmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "Ij48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "Jz48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "PjxzY3JpcHQ+YWxlcnQoMTIzKTwvc2NyaXB0Pg==", + "PC9zY3JpcHQ+PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+", + "PCAvIHNjcmlwdCA+PCBzY3JpcHQgPmFsZXJ0KDEyMyk8IC8gc2NyaXB0ID4=", + "b25mb2N1cz1KYVZhU0NyaXB0OmFsZXJ0KDEyMykgYXV0b2ZvY3Vz", + "IiBvbmZvY3VzPUphVmFTQ3JpcHQ6YWxlcnQoMTIzKSBhdXRvZm9jdXM=", + "JyBvbmZvY3VzPUphVmFTQ3JpcHQ6YWxlcnQoMTIzKSBhdXRvZm9jdXM=", + "77ycc2NyaXB077yeYWxlcnQoMTIzKe+8nC9zY3JpcHTvvJ4=", + "PHNjPHNjcmlwdD5yaXB0PmFsZXJ0KDEyMyk8L3NjPC9zY3JpcHQ+cmlwdD4=", + "LS0+PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+", + "IjthbGVydCgxMjMpO3Q9Ig==", + "JzthbGVydCgxMjMpO3Q9Jw==", + "SmF2YVNDcmlwdDphbGVydCgxMjMp", + "O2FsZXJ0KDEyMyk7", + "c3JjPUphVmFTQ3JpcHQ6cHJvbXB0KDEzMik=", + "Ij48c2NyaXB0PmFsZXJ0KDEyMyk7PC9zY3JpcHQgeD0i", + "Jz48c2NyaXB0PmFsZXJ0KDEyMyk7PC9zY3JpcHQgeD0n", + "PjxzY3JpcHQ+YWxlcnQoMTIzKTs8L3NjcmlwdCB4PQ==", + "IiBhdXRvZm9jdXMgb25rZXl1cD0iamF2YXNjcmlwdDphbGVydCgxMjMp", + "JyBhdXRvZm9jdXMgb25rZXl1cD0namF2YXNjcmlwdDphbGVydCgxMjMp", + "PHNjcmlwdHgyMHR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgzRXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwRHR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwOXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwQ3R5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgyRnR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwQXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "J2AiPjx4M0NzY3JpcHQ+amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "J2AiPjx4MDBzY3JpcHQ+amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "QUJDPGRpdiBzdHlsZT0ieHgzQWV4cHJlc3Npb24oamF2YXNjcmlwdDphbGVydCgxKSI+REVG", + "QUJDPGRpdiBzdHlsZT0ieDpleHByZXNzaW9ueDVDKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHByZXNzaW9ueDAwKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHB4MDByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHB4NUNyZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MEFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MDlleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTN4ODB4ODBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODRleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4QzJ4QTBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRF", + "Rg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4OEFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MERleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MENleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODdleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RUZ4QkJ4QkZleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MjBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODhleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MDBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4OEJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODZleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODVleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MEJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODNleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODlleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "PGEgaHJlZj0ieDBCamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEMyeEEwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVs", + "ZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUxeEEweDhFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDExamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDEwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDIwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDEzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDhBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweEFGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgxamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFEamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUxeDlBeDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAxamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUzeDgweDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDEyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBEamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBDamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweEE4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFCamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweEE5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgxeDlGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFDamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwMDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgzQTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwOTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwRDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwQTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQW9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyMm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwRG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyRm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwOW9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQ29uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwMG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyN29uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyMG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "ImAnPjxzY3JpcHQ+eDNCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDBEamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEVGeEJCeEJGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgxamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg0amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUzeDgweDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDA5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg1amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg4amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDAwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweEE4amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDhBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUxeDlBeDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDBDamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDJCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEYweDkweDk2eDlBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+LWphdmFzY3JpcHQ6YWxlcnQoMSk8L3NjcmlwdD4=", + "ImAnPjxzY3JpcHQ+eDBBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweEFGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDdFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg3amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgxeDlGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweEE5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEMyeDg1amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEVGeEJGeEFFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgzamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDhCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEVGeEJGeEJFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDIxamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgyamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg2amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUxeEEweDhFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDBCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDIwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEMyeEEwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "PGltZyB4MDBzcmM9eCBvbmVycm9yPSJhbGVydCgxKSI+", + "PGltZyB4NDdzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MTFzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MTJzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZ3g0N3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gxMHNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gxM3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gzMnNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3g0N3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gxMXNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyB4NDdzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MzRzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MzlzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MDBzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MDk9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTA9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MzI9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTI9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTE9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MDA9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4NDc9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eHgwOW9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxMG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxMW9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxMm9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxM29uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ1thXVtiXVtjXXNyY1tkXT14W2Vdb25lcnJvcj1bZl0iYWxlcnQoMSkiPg==", + "PGltZyBzcmM9eCBvbmVycm9yPXgwOSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMCJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMiJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgzMiJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgwMCJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGEgaHJlZj1qYXZhJiMxJiMyJiMzJiM0JiM1JiM2JiM3JiM4JiMxMSYjMTJzY3JpcHQ6amF2YXNj", + "cmlwdDphbGVydCgxKT5YWFg8L2E+", + "PGltZyBzcmM9InhgIGA8c2NyaXB0PmphdmFzY3JpcHQ6YWxlcnQoMSk8L3NjcmlwdD4iYCBgPg==", + "PGltZyBzcmMgb25lcnJvciAvIiAnIj0gYWx0PWphdmFzY3JpcHQ6YWxlcnQoMSkvLyI+", + "PHRpdGxlIG9ucHJvcGVydHljaGFuZ2U9amF2YXNjcmlwdDphbGVydCgxKT48L3RpdGxlPjx0aXRs", + "ZSB0aXRsZT0+", + "PGEgaHJlZj1odHRwOi8vZm9vLmJhci8jeD1geT48L2E+PGltZyBhbHQ9ImA+PGltZyBzcmM9eDp4", + "IG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT48L2E+Ij4=", + "PCEtLVtpZl0+PHNjcmlwdD5qYXZhc2NyaXB0OmFsZXJ0KDEpPC9zY3JpcHQgLS0+", + "PCEtLVtpZjxpbWcgc3JjPXggb25lcnJvcj1qYXZhc2NyaXB0OmFsZXJ0KDEpLy9dPiAtLT4=", + "PHNjcmlwdCBzcmM9Ii8lKGpzY3JpcHQpcyI+PC9zY3JpcHQ+", + "PHNjcmlwdCBzcmM9IlwlKGpzY3JpcHQpcyI+PC9zY3JpcHQ+", + "PElNRyAiIiI+PFNDUklQVD5hbGVydCgiWFNTIik8L1NDUklQVD4iPg==", + "PElNRyBTUkM9amF2YXNjcmlwdDphbGVydChTdHJpbmcuZnJvbUNoYXJDb2RlKDg4LDgzLDgzKSk+", + "PElNRyBTUkM9IyBvbm1vdXNlb3Zlcj0iYWxlcnQoJ3h4cycpIj4=", + "PElNRyBTUkM9IG9ubW91c2VvdmVyPSJhbGVydCgneHhzJykiPg==", + "PElNRyBvbm1vdXNlb3Zlcj0iYWxlcnQoJ3h4cycpIj4=", + "PElNRyBTUkM9JiMxMDY7JiM5NzsmIzExODsmIzk3OyYjMTE1OyYjOTk7JiMxMTQ7JiMxMDU7JiMx", + "MTI7JiMxMTY7JiM1ODsmIzk3OyYjMTA4OyYjMTAxOyYjMTE0OyYjMTE2OyYjNDA7JiMzOTsmIzg4", + "OyYjODM7JiM4MzsmIzM5OyYjNDE7Pg==", + "PElNRyBTUkM9JiMwMDAwMTA2JiMwMDAwMDk3JiMwMDAwMTE4JiMwMDAwMDk3JiMwMDAwMTE1JiMw", + "MDAwMDk5JiMwMDAwMTE0JiMwMDAwMTA1JiMwMDAwMTEyJiMwMDAwMTE2JiMwMDAwMDU4JiMwMDAw", + "MDk3JiMwMDAwMTA4JiMwMDAwMTAxJiMwMDAwMTE0JiMwMDAwMTE2JiMwMDAwMDQwJiMwMDAwMDM5", + "JiMwMDAwMDg4JiMwMDAwMDgzJiMwMDAwMDgzJiMwMDAwMDM5JiMwMDAwMDQxPg==", + "PElNRyBTUkM9JiN4NkEmI3g2MSYjeDc2JiN4NjEmI3g3MyYjeDYzJiN4NzImI3g2OSYjeDcwJiN4", + "NzQmI3gzQSYjeDYxJiN4NkMmI3g2NSYjeDcyJiN4NzQmI3gyOCYjeDI3JiN4NTgmI3g1MyYjeDUz", + "JiN4MjcmI3gyOT4=", + "PElNRyBTUkM9ImphdiBhc2NyaXB0OmFsZXJ0KCdYU1MnKTsiPg==", + "PElNRyBTUkM9ImphdiYjeDA5O2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+", + "PElNRyBTUkM9ImphdiYjeDBBO2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+", + "PElNRyBTUkM9ImphdiYjeDBEO2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+", + "cGVybCAtZSAncHJpbnQgIjxJTUcgU1JDPWphdmEwc2NyaXB0OmFsZXJ0KCJYU1MiKT4iOycgPiBv", + "dXQ=", + "PElNRyBTUkM9IiAmIzE0OyBqYXZhc2NyaXB0OmFsZXJ0KCdYU1MnKTsiPg==", + "PFNDUklQVC9YU1MgU1JDPSJodHRwOi8vaGEuY2tlcnMub3JnL3hzcy5qcyI+PC9TQ1JJUFQ+", + "PEJPRFkgb25sb2FkISMkJSYoKSp+Ky1fLiw6Oz9AWy98XV5gPWFsZXJ0KCJYU1MiKT4=", + "PFNDUklQVC9TUkM9Imh0dHA6Ly9oYS5ja2Vycy5vcmcveHNzLmpzIj48L1NDUklQVD4=", + "PDxTQ1JJUFQ+YWxlcnQoIlhTUyIpOy8vPDwvU0NSSVBUPg==", + "PFNDUklQVCBTUkM9aHR0cDovL2hhLmNrZXJzLm9yZy94c3MuanM/PCBCID4=", + "PFNDUklQVCBTUkM9Ly9oYS5ja2Vycy5vcmcvLmo+", + "PElNRyBTUkM9ImphdmFzY3JpcHQ6YWxlcnQoJ1hTUycpIg==", + "PGlmcmFtZSBzcmM9aHR0cDovL2hhLmNrZXJzLm9yZy9zY3JpcHRsZXQuaHRtbCA8", + "IjthbGVydCgnWFNTJyk7Ly8=", + "PHUgb25jb3B5PWFsZXJ0KCk+IENvcHkgbWU8L3U+", + "PGkgb253aGVlbD1hbGVydCgxKT4gU2Nyb2xsIG92ZXIgbWUgPC9pPg==", + "PHBsYWludGV4dD4=", + "aHR0cDovL2EvJSUzMCUzMA==", + "PC90ZXh0YXJlYT48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "MTtEUk9QIFRBQkxFIHVzZXJz", + "MSc7IERST1AgVEFCTEUgdXNlcnMtLSAx", + "JyBPUiAxPTEgLS0gMQ==", + "JyBPUiAnMSc9JzE=", + "JQ==", + "Xw==", + "LQ==", + "LS0=", + "LS12ZXJzaW9u", + "LS1oZWxw", + "JFVTRVI=", + "L2Rldi9udWxsOyB0b3VjaCAvdG1wL2JsbnMuZmFpbCA7IGVjaG8=", + "YHRvdWNoIC90bXAvYmxucy5mYWlsYA==", + "JCh0b3VjaCAvdG1wL2JsbnMuZmFpbCk=", + "QHtbc3lzdGVtICJ0b3VjaCAvdG1wL2JsbnMuZmFpbCJdfQ==", + "ZXZhbCgicHV0cyAnaGVsbG8gd29ybGQnIik=", + "U3lzdGVtKCJscyAtYWwgLyIp", + "YGxzIC1hbCAvYA==", + "S2VybmVsLmV4ZWMoImxzIC1hbCAvIik=", + "S2VybmVsLmV4aXQoMSk=", + "JXgoJ2xzIC1hbCAvJyk=", + "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/PjwhRE9DVFlQRSBmb28g", + "WyA8IUVMRU1FTlQgZm9vIEFOWSA+PCFFTlRJVFkgeHhlIFNZU1RFTSAiZmlsZTovLy9ldGMvcGFz", + "c3dkIiA+XT48Zm9vPiZ4eGU7PC9mb28+", + "JEhPTUU=", + "JEVOVnsnSE9NRSd9", + "JWQ=", + "JXM=", + "ezB9", + "JSouKnM=", + "Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZCUwMA==", + "Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL2hvc3Rz", + "KCkgeyAwOyB9OyB0b3VjaCAvdG1wL2JsbnMuc2hlbGxzaG9jazEuZmFpbDs=", + "KCkgeyBfOyB9ID5fWyQoJCgpKV0geyB0b3VjaCAvdG1wL2JsbnMuc2hlbGxzaG9jazIuZmFpbDsg", + "fQ==", + "PDw8ICVzKHVuPSclcycpID0gJXU=", + "KysrQVRIMA==", + "Q09O", + "UFJO", + "QVVY", + "Q0xPQ0sk", + "TlVM", + "QTo=", + "Wlo6", + "Q09NMQ==", + "TFBUMQ==", + "TFBUMg==", + "TFBUMw==", + "Q09NMg==", + "Q09NMw==", + "Q09NNA==", + "RENDIFNFTkQgU1RBUlRLRVlMT0dHRVIgMCAwIDA=", + "U2N1bnRob3JwZSBHZW5lcmFsIEhvc3BpdGFs", + "UGVuaXN0b25lIENvbW11bml0eSBDaHVyY2g=", + "TGlnaHR3YXRlciBDb3VudHJ5IFBhcms=", + "SmltbXkgQ2xpdGhlcm9l", + "SG9ybmltYW4gTXVzZXVt", + "c2hpdGFrZSBtdXNocm9vbXM=", + "Um9tYW5zSW5TdXNzZXguY28udWs=", + "aHR0cDovL3d3dy5jdW0ucWMuY2Ev", + "Q3JhaWcgQ29ja2J1cm4sIFNvZnR3YXJlIFNwZWNpYWxpc3Q=", + "TGluZGEgQ2FsbGFoYW4=", + "RHIuIEhlcm1hbiBJLiBMaWJzaGl0eg==", + "bWFnbmEgY3VtIGxhdWRl", + "U3VwZXIgQm93bCBYWFg=", + "bWVkaWV2YWwgZXJlY3Rpb24gb2YgcGFyYXBldHM=", + "ZXZhbHVhdGU=", + "bW9jaGE=", + "ZXhwcmVzc2lvbg==", + "QXJzZW5hbCBjYW5hbA==", + "Y2xhc3NpYw==", + "VHlzb24gR2F5", + "RGljayBWYW4gRHlrZQ==", + "YmFzZW1lbnQ=", + "SWYgeW91J3JlIHJlYWRpbmcgdGhpcywgeW91J3ZlIGJlZW4gaW4gYSBjb21hIGZvciBhbG1vc3Qg", + "MjAgeWVhcnMgbm93LiBXZSdyZSB0cnlpbmcgYSBuZXcgdGVjaG5pcXVlLiBXZSBkb24ndCBrbm93", + "IHdoZXJlIHRoaXMgbWVzc2FnZSB3aWxsIGVuZCB1cCBpbiB5b3VyIGRyZWFtLCBidXQgd2UgaG9w", + "ZSBpdCB3b3Jrcy4gUGxlYXNlIHdha2UgdXAsIHdlIG1pc3MgeW91Lg==", + "Um9zZXMgYXJlIBtbMDszMW1yZWQbWzBtLCB2aW9sZXRzIGFyZSAbWzA7MzRtYmx1ZS4gSG9wZSB5", + "b3UgZW5qb3kgdGVybWluYWwgaHVl", + "QnV0IG5vdy4uLhtbMjBDZm9yIG15IGdyZWF0ZXN0IHRyaWNrLi4uG1s4bQ==", + "VGhlIHF1aWMICAgICAhrIGJyb3duIGZvBwcHBwcHBwcHBwd4Li4uIFtCZWVlZXBd", + "UG93ZXLZhNmP2YTZj9i12ZHYqNmP2YTZj9mE2LXZkdio2Y/Ysdix2Ysg4KWjIOClo2gg4KWjIOCl", + "o+WGlw==", + "2q/ahtm+2pg=", + "eyUgcHJpbnQgJ3gnICogNjQgKiAxMDI0KiozICV9", + "e3sgIiIuX19jbGFzc19fLl9fbXJvX19bMl0uX19zdWJjbGFzc2VzX18oKVs0MF0oIi9ldGMvcGFz", + "c3dkIikucmVhZCgpIH19" +] + diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 8e5c174d270..71e0d44b478 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -282,7 +282,7 @@ impl DebugInstrumenter { .unwrap_or_else(|| panic!("var lookup failed for var_name={}", &id.0.contents)); build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.span()))) } - ast::LValue::Dereference(_lv) => { + ast::LValue::Dereference(_lv, span) => { // TODO: this is a dummy statement for now, but we should // somehow track the derefence and update the pointed to // variable @@ -303,16 +303,16 @@ impl DebugInstrumenter { }); break; } - ast::LValue::MemberAccess { object, field_name } => { + ast::LValue::MemberAccess { object, field_name, span } => { cursor = object; let field_name_id = self.insert_field_name(&field_name.0.contents); - indexes.push(sint_expr(-(field_name_id.0 as i128), expression_span)); + indexes.push(sint_expr(-(field_name_id.0 as i128), *span)); } - ast::LValue::Index { index, array } => { + ast::LValue::Index { index, array, span: _ } => { cursor = array; indexes.push(index.clone()); } - ast::LValue::Dereference(_ref) => { + ast::LValue::Dereference(_ref, _span) => { unimplemented![] } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index f8e3c4cab60..ec149dee96e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -942,7 +942,7 @@ impl<'a> Resolver<'a> { }); } let is_low_level_function = - func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); + attributes.function.as_ref().map_or(false, |func| func.is_low_level()); if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function { let error = ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }; @@ -991,6 +991,8 @@ impl<'a> Resolver<'a> { .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) .collect(); + let should_fold = attributes.is_foldable(); + FuncMeta { name: name_ident, kind: func.kind, @@ -1005,6 +1007,7 @@ impl<'a> Resolver<'a> { has_body: !func.def.body.is_empty(), trait_constraints: self.resolve_trait_constraints(&func.def.where_clause), is_entry_point: self.is_entry_point_function(func), + should_fold, } } @@ -1303,18 +1306,23 @@ impl<'a> Resolver<'a> { HirLValue::Ident(ident.0, Type::Error) } - LValue::MemberAccess { object, field_name } => { - let object = Box::new(self.resolve_lvalue(*object)); - HirLValue::MemberAccess { object, field_name, field_index: None, typ: Type::Error } - } - LValue::Index { array, index } => { + LValue::MemberAccess { object, field_name, span } => HirLValue::MemberAccess { + object: Box::new(self.resolve_lvalue(*object)), + field_name, + location: Location::new(span, self.file), + field_index: None, + typ: Type::Error, + }, + LValue::Index { array, index, span } => { let array = Box::new(self.resolve_lvalue(*array)); let index = self.resolve_expression(index); - HirLValue::Index { array, index, typ: Type::Error } + let location = Location::new(span, self.file); + HirLValue::Index { array, index, location, typ: Type::Error } } - LValue::Dereference(lvalue) => { + LValue::Dereference(lvalue, span) => { let lvalue = Box::new(self.resolve_lvalue(*lvalue)); - HirLValue::Dereference { lvalue, element_type: Type::Error } + let location = Location::new(span, self.file); + HirLValue::Dereference { lvalue, location, element_type: Type::Error } } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 6beb6929ce1..6c28aabe0fb 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -65,8 +65,6 @@ pub enum TypeCheckError { VariableMustBeMutable { name: String, span: Span }, #[error("No method named '{method_name}' found for type '{object_type}'")] UnresolvedMethodCall { method_name: String, object_type: Type, span: Span }, - #[error("Comparisons are invalid on Field types. Try casting the operands to a sized integer type first")] - InvalidComparisonOnField { span: Span }, #[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")] IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span }, #[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")] @@ -76,7 +74,7 @@ pub enum TypeCheckError { #[error("{kind} cannot be used in a unary operation")] InvalidUnaryOp { kind: String, span: Span }, #[error("Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first.")] - InvalidBitwiseOperationOnField { span: Span }, + FieldBitwiseOp { span: Span }, #[error("Integer cannot be used with type {typ}")] IntegerTypeMismatch { typ: Type, span: Span }, #[error("Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first")] @@ -224,12 +222,11 @@ impl From for Diagnostic { | TypeCheckError::TupleIndexOutOfBounds { span, .. } | TypeCheckError::VariableMustBeMutable { span, .. } | TypeCheckError::UnresolvedMethodCall { span, .. } - | TypeCheckError::InvalidComparisonOnField { span } | TypeCheckError::IntegerSignedness { span, .. } | TypeCheckError::IntegerBitWidth { span, .. } | TypeCheckError::InvalidInfixOp { span, .. } | TypeCheckError::InvalidUnaryOp { span, .. } - | TypeCheckError::InvalidBitwiseOperationOnField { span, .. } + | TypeCheckError::FieldBitwiseOp { span, .. } | TypeCheckError::IntegerTypeMismatch { span, .. } | TypeCheckError::FieldComparison { span, .. } | TypeCheckError::AmbiguousBitWidth { span, .. } @@ -315,7 +312,7 @@ impl From for Diagnostic { } TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), - "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), + "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), span), TypeCheckError::MismatchTraitImplNumParameters { expected_num_parameters, actual_num_parameters, diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 10476b6caef..b56e2dce2a9 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -126,7 +126,7 @@ impl<'interner> TypeChecker<'interner> { } } HirLiteral::Bool(_) => Type::Bool, - HirLiteral::Integer(_, _) => Type::polymorphic_integer_or_field(self.interner), + HirLiteral::Integer(_, _) => self.polymorphic_integer_or_field(), HirLiteral::Str(string) => { let len = Type::Constant(string.len() as u64); Type::String(Box::new(len)) @@ -152,14 +152,16 @@ impl<'interner> TypeChecker<'interner> { Ok((typ, use_impl)) => { if use_impl { let id = infix_expr.trait_method_id; - // Assume operators have no trait generics - self.verify_trait_constraint( - &lhs_type, - id.trait_id, - &[], - *expr_id, - span, - ); + + // Delay checking the trait constraint until the end of the function. + // Checking it now could bind an unbound type variable to any type + // that implements the trait. + let constraint = crate::hir_def::traits::TraitConstraint { + typ: lhs_type.clone(), + trait_id: id.trait_id, + trait_generics: Vec::new(), + }; + self.trait_constraints.push((constraint, *expr_id)); self.typecheck_operator_method(*expr_id, id, &lhs_type, span); } typ @@ -416,23 +418,28 @@ impl<'interner> TypeChecker<'interner> { self.interner.select_impl_for_expression(function_ident_id, impl_kind); } Err(erroring_constraints) => { - // Don't show any errors where try_get_trait returns None. - // This can happen if a trait is used that was never declared. - let constraints = erroring_constraints - .into_iter() - .map(|constraint| { - let r#trait = self.interner.try_get_trait(constraint.trait_id)?; - let mut name = r#trait.name.to_string(); - if !constraint.trait_generics.is_empty() { - let generics = vecmap(&constraint.trait_generics, ToString::to_string); - name += &format!("<{}>", generics.join(", ")); - } - Some((constraint.typ, name)) - }) - .collect::>>(); + if erroring_constraints.is_empty() { + self.errors.push(TypeCheckError::TypeAnnotationsNeeded { span }); + } else { + // Don't show any errors where try_get_trait returns None. + // This can happen if a trait is used that was never declared. + let constraints = erroring_constraints + .into_iter() + .map(|constraint| { + let r#trait = self.interner.try_get_trait(constraint.trait_id)?; + let mut name = r#trait.name.to_string(); + if !constraint.trait_generics.is_empty() { + let generics = + vecmap(&constraint.trait_generics, ToString::to_string); + name += &format!("<{}>", generics.join(", ")); + } + Some((constraint.typ, name)) + }) + .collect::>>(); - if let Some(constraints) = constraints { - self.errors.push(TypeCheckError::NoMatchingImplFound { constraints, span }); + if let Some(constraints) = constraints { + self.errors.push(TypeCheckError::NoMatchingImplFound { constraints, span }); + } } } } @@ -558,15 +565,13 @@ impl<'interner> TypeChecker<'interner> { let index_type = self.check_expression(&index_expr.index); let span = self.interner.expr_span(&index_expr.index); - index_type.unify( - &Type::polymorphic_integer_or_field(self.interner), - &mut self.errors, - || TypeCheckError::TypeMismatch { + index_type.unify(&self.polymorphic_integer_or_field(), &mut self.errors, || { + TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), expr_span: span, - }, - ); + } + }); // When writing `a[i]`, if `a : &mut ...` then automatically dereference `a` as many // times as needed to get the underlying array. @@ -836,6 +841,10 @@ impl<'interner> TypeChecker<'interner> { match (lhs_type, rhs_type) { // Avoid reporting errors multiple times (Error, _) | (_, Error) => Ok((Bool, false)), + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + self.comparator_operand_type_rules(&alias, other, op, span) + } // Matches on TypeVariable must be first to follow any type // bindings. @@ -844,12 +853,8 @@ impl<'interner> TypeChecker<'interner> { return self.comparator_operand_type_rules(other, binding, op, span); } - self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); - Ok((Bool, false)) - } - (Alias(alias, args), other) | (other, Alias(alias, args)) => { - let alias = alias.borrow().get_type(args); - self.comparator_operand_type_rules(&alias, other, op, span) + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + Ok((Bool, use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if sign_x != sign_y { @@ -1079,13 +1084,16 @@ impl<'interner> TypeChecker<'interner> { } } + /// Handles the TypeVariable case for checking binary operators. + /// Returns true if we should use the impl for the operator instead of the primitive + /// version of it. fn bind_type_variables_for_infix( &mut self, lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, span: Span, - ) { + ) -> bool { self.unify(lhs_type, rhs_type, || TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), actual: rhs_type.clone(), @@ -1093,22 +1101,26 @@ impl<'interner> TypeChecker<'interner> { span, }); - // In addition to unifying both types, we also have to bind either - // the lhs or rhs to an integer type variable. This ensures if both lhs - // and rhs are type variables, that they will have the correct integer - // type variable kind instead of TypeVariableKind::Normal. - let target = if op.kind.is_valid_for_field_type() { - Type::polymorphic_integer_or_field(self.interner) - } else { - Type::polymorphic_integer(self.interner) - }; + let use_impl = !lhs_type.is_numeric(); + + // If this operator isn't valid for fields we have to possibly narrow + // TypeVariableKind::IntegerOrField to TypeVariableKind::Integer. + // Doing so also ensures a type error if Field is used. + // The is_numeric check is to allow impls for custom types to bypass this. + if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric() { + let target = Type::polymorphic_integer(self.interner); + + use BinaryOpKind::*; + use TypeCheckError::*; + self.unify(lhs_type, &target, || match op.kind { + Less | LessEqual | Greater | GreaterEqual => FieldComparison { span }, + And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { span }, + Modulo => FieldModulo { span }, + other => unreachable!("Operator {other:?} should be valid for Field"), + }); + } - self.unify(lhs_type, &target, || TypeCheckError::TypeMismatchWithSource { - expected: lhs_type.clone(), - actual: rhs_type.clone(), - source: Source::Binary, - span, - }); + use_impl } // Given a binary operator and another type. This method will produce the output type @@ -1130,6 +1142,10 @@ impl<'interner> TypeChecker<'interner> { match (lhs_type, rhs_type) { // An error type on either side will always return an error (Error, _) | (_, Error) => Ok((Error, false)), + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + self.infix_operand_type_rules(&alias, op, other, span) + } // Matches on TypeVariable must be first so that we follow any type // bindings. @@ -1138,14 +1154,8 @@ impl<'interner> TypeChecker<'interner> { return self.infix_operand_type_rules(binding, op, other, span); } - self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); - - // Both types are unified so the choice of which to return is arbitrary - Ok((other.clone(), false)) - } - (Alias(alias, args), other) | (other, Alias(alias, args)) => { - let alias = alias.borrow().get_type(args); - self.infix_operand_type_rules(&alias, op, other, span) + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + Ok((other.clone(), use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if sign_x != sign_y { @@ -1170,7 +1180,7 @@ impl<'interner> TypeChecker<'interner> { if op.kind == BinaryOpKind::Modulo { return Err(TypeCheckError::FieldModulo { span }); } else { - return Err(TypeCheckError::InvalidBitwiseOperationOnField { span }); + return Err(TypeCheckError::FieldBitwiseOp { span }); } } Ok((FieldElement, false)) @@ -1211,7 +1221,7 @@ impl<'interner> TypeChecker<'interner> { self.errors .push(TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span }); } - let expected = Type::polymorphic_integer_or_field(self.interner); + let expected = self.polymorphic_integer_or_field(); rhs_type.unify(&expected, &mut self.errors, || TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span, diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 137608f8037..c5a04c33883 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -37,6 +37,11 @@ pub struct TypeChecker<'interner> { /// on each variable, but it is only until function calls when the types /// needed for the trait constraint may become known. trait_constraints: Vec<(TraitConstraint, ExprId)>, + + /// All type variables created in the current function. + /// This map is used to default any integer type variables at the end of + /// a function (before checking trait constraints) if a type wasn't already chosen. + type_variables: Vec, } /// Type checks a function and assigns the @@ -86,31 +91,13 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec Vec Vec Vec, ) { let meta = type_checker.interner.function_meta(&func_id); - if meta.is_entry_point && !param.1.is_valid_for_program_input() { + if (meta.is_entry_point || meta.should_fold) && !param.1.is_valid_for_program_input() { let span = param.0.span(); errors.push(TypeCheckError::InvalidTypeForEntryPoint { span }); } @@ -335,7 +348,13 @@ fn check_function_type_matches_expected_type( impl<'interner> TypeChecker<'interner> { fn new(interner: &'interner mut NodeInterner) -> Self { - Self { interner, errors: Vec::new(), trait_constraints: Vec::new(), current_function: None } + Self { + interner, + errors: Vec::new(), + trait_constraints: Vec::new(), + type_variables: Vec::new(), + current_function: None, + } } fn check_function_body(&mut self, body: &ExprId) -> Type { @@ -350,6 +369,7 @@ impl<'interner> TypeChecker<'interner> { interner, errors: Vec::new(), trait_constraints: Vec::new(), + type_variables: Vec::new(), current_function: None, }; let statement = this.interner.get_global(id).let_statement; @@ -383,6 +403,22 @@ impl<'interner> TypeChecker<'interner> { make_error, ); } + + /// Return a fresh integer or field type variable and log it + /// in self.type_variables to default it later. + fn polymorphic_integer_or_field(&mut self) -> Type { + let typ = Type::polymorphic_integer_or_field(self.interner); + self.type_variables.push(typ.clone()); + typ + } + + /// Return a fresh integer type variable and log it + /// in self.type_variables to default it later. + fn polymorphic_integer(&mut self) -> Type { + let typ = Type::polymorphic_integer(self.interner); + self.type_variables.push(typ.clone()); + typ + } } // XXX: These tests are all manual currently. @@ -503,6 +539,7 @@ mod test { trait_constraints: Vec::new(), direct_generics: Vec::new(), is_entry_point: true, + should_fold: false, }; interner.push_fn_meta(func_meta, func_id); @@ -561,12 +598,7 @@ mod test { "#; - let expected_num_errors = 0; - type_check_src_code_errors_expected( - src, - expected_num_errors, - vec![String::from("main"), String::from("foo")], - ); + type_check_src_code(src, vec![String::from("main"), String::from("foo")]); } #[test] fn basic_closure() { @@ -591,6 +623,19 @@ mod test { type_check_src_code(src, vec![String::from("main")]); } + + #[test] + fn fold_entry_point() { + let src = r#" + #[fold] + fn fold(x: &mut Field) -> Field { + *x + } + "#; + + type_check_src_code_errors_expected(src, vec![String::from("fold")], 1); + } + // This is the same Stub that is in the resolver, maybe we can pull this out into a test module and re-use? struct TestPathResolver(HashMap); @@ -624,15 +669,15 @@ mod test { } fn type_check_src_code(src: &str, func_namespace: Vec) { - type_check_src_code_errors_expected(src, 0, func_namespace); + type_check_src_code_errors_expected(src, func_namespace, 0); } // This function assumes that there is only one function and this is the // func id that is returned fn type_check_src_code_errors_expected( src: &str, - expected_number_errors: usize, func_namespace: Vec, + expected_num_type_check_errs: usize, ) { let (program, errors) = parse_program(src); let mut interner = NodeInterner::default(); @@ -640,9 +685,8 @@ mod test { assert_eq!( errors.len(), - expected_number_errors, - "expected {} errors, but got {}, errors: {:?}", - expected_number_errors, + 0, + "expected 0 parser errors, but got {}, errors: {:?}", errors.len(), errors ); @@ -688,6 +732,13 @@ mod test { // Type check section let errors = super::type_check_func(&mut interner, func_ids.first().cloned().unwrap()); - assert_eq!(errors, vec![]); + assert_eq!( + errors.len(), + expected_num_type_check_errs, + "expected {} type check errors, but got {}, errors: {:?}", + expected_num_type_check_errs, + errors.len(), + errors + ); } } diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 69363d5f00a..fb57aa75f89 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -1,5 +1,5 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Span; use crate::hir_def::expr::{HirExpression, HirIdent, HirLiteral}; use crate::hir_def::stmt::{ @@ -71,7 +71,7 @@ impl<'interner> TypeChecker<'interner> { expr_span: range_span, }); - let expected_type = Type::polymorphic_integer(self.interner); + let expected_type = self.polymorphic_integer(); self.unify(&start_range_type, &expected_type, || TypeCheckError::TypeCannotBeUsed { typ: start_range_type.clone(), @@ -195,51 +195,51 @@ impl<'interner> TypeChecker<'interner> { (typ.clone(), HirLValue::Ident(ident.clone(), typ), mutable) } - HirLValue::MemberAccess { object, field_name, .. } => { + HirLValue::MemberAccess { object, field_name, location, .. } => { let (lhs_type, object, mut mutable) = self.check_lvalue(object, assign_span); let mut object = Box::new(object); - let span = field_name.span(); let field_name = field_name.clone(); let object_ref = &mut object; let mutable_ref = &mut mutable; + let location = *location; let dereference_lhs = move |_: &mut Self, _, element_type| { // We must create a temporary value first to move out of object_ref before // we eventually reassign to it. let id = DefinitionId::dummy_id(); - let location = Location::new(span, fm::FileId::dummy()); let ident = HirIdent::non_trait_method(id, location); let tmp_value = HirLValue::Ident(ident, Type::Error); let lvalue = std::mem::replace(object_ref, Box::new(tmp_value)); - *object_ref = Box::new(HirLValue::Dereference { lvalue, element_type }); + *object_ref = + Box::new(HirLValue::Dereference { lvalue, element_type, location }); *mutable_ref = true; }; let name = &field_name.0.contents; let (object_type, field_index) = self - .check_field_access(&lhs_type, name, span, Some(dereference_lhs)) + .check_field_access(&lhs_type, name, field_name.span(), Some(dereference_lhs)) .unwrap_or((Type::Error, 0)); let field_index = Some(field_index); let typ = object_type.clone(); - let lvalue = HirLValue::MemberAccess { object, field_name, field_index, typ }; + let lvalue = + HirLValue::MemberAccess { object, field_name, field_index, typ, location }; (object_type, lvalue, mutable) } - HirLValue::Index { array, index, .. } => { + HirLValue::Index { array, index, location, .. } => { let index_type = self.check_expression(index); let expr_span = self.interner.expr_span(index); + let location = *location; - index_type.unify( - &Type::polymorphic_integer_or_field(self.interner), - &mut self.errors, - || TypeCheckError::TypeMismatch { + index_type.unify(&self.polymorphic_integer_or_field(), &mut self.errors, || { + TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), expr_span, - }, - ); + } + }); let (mut lvalue_type, mut lvalue, mut mutable) = self.check_lvalue(array, assign_span); @@ -248,7 +248,8 @@ impl<'interner> TypeChecker<'interner> { // as needed to unwrap any &mut wrappers. while let Type::MutableReference(element) = lvalue_type.follow_bindings() { let element_type = element.as_ref().clone(); - lvalue = HirLValue::Dereference { lvalue: Box::new(lvalue), element_type }; + lvalue = + HirLValue::Dereference { lvalue: Box::new(lvalue), element_type, location }; lvalue_type = *element; // We know this value to be mutable now since we found an `&mut` mutable = true; @@ -275,11 +276,12 @@ impl<'interner> TypeChecker<'interner> { }; let array = Box::new(lvalue); - (typ.clone(), HirLValue::Index { array, index: *index, typ }, mutable) + (typ.clone(), HirLValue::Index { array, index: *index, typ, location }, mutable) } - HirLValue::Dereference { lvalue, element_type: _ } => { + HirLValue::Dereference { lvalue, element_type: _, location } => { let (reference_type, lvalue, _) = self.check_lvalue(lvalue, assign_span); let lvalue = Box::new(lvalue); + let location = *location; let element_type = Type::type_variable(self.interner.next_type_variable_id()); let expected_type = Type::MutableReference(Box::new(element_type.clone())); @@ -291,7 +293,11 @@ impl<'interner> TypeChecker<'interner> { }); // Dereferences are always mutable since we already type checked against a &mut T - (element_type.clone(), HirLValue::Dereference { lvalue, element_type }, true) + ( + element_type.clone(), + HirLValue::Dereference { lvalue, element_type, location }, + true, + ) } } } diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 56543e8185c..a3bbc9445a8 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -124,6 +124,10 @@ pub struct FuncMeta { /// True if this function is an entry point to the program. /// For non-contracts, this means the function is `main`. pub is_entry_point: bool, + + /// True if this function is marked with an attribute + /// that indicates it should not be inlined, such as for folding. + pub should_fold: bool, } impl FuncMeta { diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 4e5f718cf47..c5e287b393c 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -99,6 +99,15 @@ impl HirPattern { | HirPattern::Struct(_, _, location) => location.span, } } + + pub(crate) fn location(&self) -> Location { + match self { + HirPattern::Identifier(ident) => ident.location, + HirPattern::Mutable(_, location) + | HirPattern::Tuple(_, location) + | HirPattern::Struct(_, _, location) => *location, + } + } } /// Represents an Ast form that can be assigned to. These @@ -111,14 +120,17 @@ pub enum HirLValue { field_name: Ident, field_index: Option, typ: Type, + location: Location, }, Index { array: Box, index: ExprId, typ: Type, + location: Location, }, Dereference { lvalue: Box, element_type: Type, + location: Location, }, } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 65592407d7d..ec8b54c33b8 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -543,11 +543,11 @@ impl TypeBinding { pub struct TypeVariableId(pub usize); impl Type { - pub fn default_int_type() -> Type { + pub fn default_int_or_field_type() -> Type { Type::FieldElement } - pub fn default_range_loop_type() -> Type { + pub fn default_int_type() -> Type { Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour) } @@ -589,6 +589,7 @@ impl Type { TypeBinding::Bound(binding) => binding.is_bindable(), TypeBinding::Unbound(_) => true, }, + Type::Alias(alias, args) => alias.borrow().get_type(args).is_bindable(), _ => false, } } @@ -605,6 +606,15 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) } + pub fn is_numeric(&self) -> bool { + use Type::*; + use TypeVariableKind as K; + matches!( + self.follow_bindings(), + FieldElement | Integer(..) | Bool | TypeVariable(_, K::Integer | K::IntegerOrField) + ) + } + fn contains_numeric_typevar(&self, target_id: TypeVariableId) -> bool { // True if the given type is a NamedGeneric with the target_id let named_generic_id_matches_target = |typ: &Type| { @@ -777,7 +787,7 @@ impl std::fmt::Display for Type { Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), Type::TypeVariable(binding, TypeVariableKind::Integer) => { if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", TypeVariableKind::Integer.default_type()) + write!(f, "{}", Type::default_int_type()) } else { write!(f, "{}", binding.borrow()) } @@ -1696,11 +1706,12 @@ impl BinaryTypeOperator { impl TypeVariableKind { /// Returns the default type this type variable should be bound to if it is still unbound /// during monomorphization. - pub(crate) fn default_type(&self) -> Type { + pub(crate) fn default_type(&self) -> Option { match self { - TypeVariableKind::IntegerOrField | TypeVariableKind::Normal => Type::default_int_type(), - TypeVariableKind::Integer => Type::default_range_loop_type(), - TypeVariableKind::Constant(length) => Type::Constant(*length), + TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), + TypeVariableKind::Integer => Some(Type::default_int_type()), + TypeVariableKind::Constant(length) => Some(Type::Constant(*length)), + TypeVariableKind::Normal => None, } } } @@ -1734,12 +1745,12 @@ impl From<&Type> for PrintableType { }, Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_range_loop_type().into(), + TypeBinding::Unbound(_) => Type::default_int_type().into(), }, Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_type().into(), + TypeBinding::Unbound(_) => Type::default_int_or_field_type().into(), } } Type::Bool => PrintableType::Boolean, diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index cf66ece0c30..c4b6bb288dc 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -726,6 +726,16 @@ mod tests { ); } + #[test] + fn fold_attribute() { + let input = r#"#[fold]"#; + + let mut lexer = Lexer::new(input); + let token = lexer.next_token().unwrap(); + + assert_eq!(token.token(), &Token::Attribute(Attribute::Function(FunctionAttribute::Fold))); + } + #[test] fn contract_library_method_attribute() { let input = r#"#[contract_library_method]"#; @@ -1087,4 +1097,114 @@ mod tests { assert_eq!(got, token); } } + + // returns a vector of: + // (expected_token_discriminator, strings_to_lex) + // expected_token_discriminator matches a given token when + // std::mem::discriminant returns the same discriminant for both. + fn blns_base64_to_statements(base64_str: String) -> Vec<(Option, Vec)> { + use base64::engine::general_purpose; + use std::borrow::Cow; + use std::io::Cursor; + use std::io::Read; + + let mut wrapped_reader = Cursor::new(base64_str); + let mut decoder = + base64::read::DecoderReader::new(&mut wrapped_reader, &general_purpose::STANDARD); + let mut base64_decoded = Vec::new(); + decoder.read_to_end(&mut base64_decoded).unwrap(); + + // NOTE: when successful, this is the same conversion method as used in + // noirc_driver::stdlib::stdlib_paths_with_source, viz. + // + // let source = std::str::from_utf8(..).unwrap().to_string(); + let s: Cow<'_, str> = match std::str::from_utf8(&base64_decoded) { + Ok(s) => std::borrow::Cow::Borrowed(s), + Err(_err) => { + // recover as much of the string as possible + // when str::from_utf8 fails + String::from_utf8_lossy(&base64_decoded) + } + }; + + vec![ + // Token::Ident(_) + (None, vec![format!("let \"{s}\" = ();")]), + (Some(Token::Str("".to_string())), vec![format!("let s = \"{s}\";")]), + ( + Some(Token::RawStr("".to_string(), 0)), + vec![ + // let s = r"Hello world"; + format!("let s = r\"{s}\";"), + // let s = r#"Simon says "hello world""#; + format!("let s = r#\"{s}\"#;"), + // // Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes + // let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; + format!("let s = r##\"{s}\"##;"), + format!("let s = r###\"{s}\"###;"), + format!("let s = r####\"{s}\"####; "), + format!("let s = r#####\"{s}\"#####;"), + ], + ), + (Some(Token::FmtStr("".to_string())), vec![format!("assert(x == y, f\"{s}\");")]), + // expected token not found + // (Some(Token::LineComment("".to_string(), None)), vec![ + (None, vec![format!("//{s}"), format!("// {s}")]), + // expected token not found + // (Some(Token::BlockComment("".to_string(), None)), vec![ + (None, vec![format!("/*{s}*/"), format!("/* {s} */"), format!("/*\n{s}\n*/")]), + ] + } + + #[test] + fn test_big_list_of_naughty_strings() { + use std::mem::discriminant; + + let blns_contents = include_str!(env!("BLNS_JSON_PATH")); + let blns_base64: Vec = + serde_json::from_str(blns_contents).expect("BLNS json invalid"); + for blns_base64_str in blns_base64 { + let statements = blns_base64_to_statements(blns_base64_str); + for (token_discriminator_opt, blns_program_strs) in statements { + for blns_program_str in blns_program_strs { + let mut expected_token_found = false; + let mut lexer = Lexer::new(&blns_program_str); + let mut result_tokens = Vec::new(); + loop { + match lexer.next_token() { + Ok(next_token) => { + result_tokens.push(next_token.clone()); + expected_token_found |= token_discriminator_opt + .as_ref() + .map(|token_discriminator| { + discriminant(token_discriminator) + == discriminant(&next_token.token()) + }) + .unwrap_or(true); + + if next_token == Token::EOF { + assert!(lexer.done, "lexer not done when EOF emitted!"); + break; + } + } + + Err(LexerErrorKind::InvalidIntegerLiteral { .. }) + | Err(LexerErrorKind::UnexpectedCharacter { .. }) + | Err(LexerErrorKind::UnterminatedBlockComment { .. }) => { + expected_token_found = true; + } + Err(err) => { + panic!("Unexpected lexer error found: {:?}", err) + } + } + } + + assert!( + expected_token_found, + "expected token not found: {token_discriminator_opt:?}\noutput:\n{result_tokens:?}", + ); + } + } + } + } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 4432a3f9e07..f8378cdd84b 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -427,6 +427,10 @@ impl Attributes { } None } + + pub fn is_foldable(&self) -> bool { + self.function.as_ref().map_or(false, |func_attribute| func_attribute.is_foldable()) + } } /// An Attribute can be either a Primary Attribute or a Secondary Attribute @@ -486,6 +490,7 @@ impl Attribute { } ["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)), ["recursive"] => Attribute::Function(FunctionAttribute::Recursive), + ["fold"] => Attribute::Function(FunctionAttribute::Fold), ["test", name] => { validate(name)?; let malformed_scope = @@ -537,6 +542,7 @@ pub enum FunctionAttribute { Oracle(String), Test(TestScope), Recursive, + Fold, } impl FunctionAttribute { @@ -565,6 +571,10 @@ impl FunctionAttribute { pub fn is_low_level(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) } + + pub fn is_foldable(&self) -> bool { + matches!(self, FunctionAttribute::Fold) + } } impl fmt::Display for FunctionAttribute { @@ -575,6 +585,7 @@ impl fmt::Display for FunctionAttribute { FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), FunctionAttribute::Recursive => write!(f, "#[recursive]"), + FunctionAttribute::Fold => write!(f, "#[fold]"), } } } @@ -619,6 +630,7 @@ impl AsRef for FunctionAttribute { FunctionAttribute::Oracle(string) => string, FunctionAttribute::Test { .. } => "", FunctionAttribute::Recursive => "", + FunctionAttribute::Fold => "", } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 4f633cbb350..7d20c2bcfee 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -211,6 +211,8 @@ pub struct Function { pub return_type: Type, pub unconstrained: bool, + pub should_fold: bool, + pub func_sig: FunctionSignature, } /// Compared to hir_def::types::Type, this monomorphized Type has: @@ -245,6 +247,7 @@ impl Type { #[derive(Debug, Clone, Hash)] pub struct Program { pub functions: Vec, + pub function_signatures: Vec, pub main_function_signature: FunctionSignature, /// Indicates whether witness indices are allowed to reoccur in the ABI of the resulting ACIR. /// @@ -264,6 +267,7 @@ impl Program { #[allow(clippy::too_many_arguments)] pub fn new( functions: Vec, + function_signatures: Vec, main_function_signature: FunctionSignature, return_distinctness: Distinctness, return_location: Option, @@ -275,6 +279,7 @@ impl Program { ) -> Program { Program { functions, + function_signatures, main_function_signature, return_distinctness, return_location, diff --git a/compiler/noirc_frontend/src/monomorphization/errors.rs b/compiler/noirc_frontend/src/monomorphization/errors.rs new file mode 100644 index 00000000000..3011c26cffe --- /dev/null +++ b/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -0,0 +1,39 @@ +use thiserror::Error; + +use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; + +#[derive(Debug, Error)] +pub enum MonomorphizationError { + #[error("Length of generic array could not be determined.")] + UnknownArrayLength { location: Location }, + + #[error("Type annotations needed")] + TypeAnnotationsNeeded { location: Location }, +} + +impl MonomorphizationError { + fn location(&self) -> Location { + match self { + MonomorphizationError::UnknownArrayLength { location } + | MonomorphizationError::TypeAnnotationsNeeded { location } => *location, + } + } +} + +impl From for FileDiagnostic { + fn from(error: MonomorphizationError) -> FileDiagnostic { + let location = error.location(); + let call_stack = vec![location]; + let diagnostic = error.into_diagnostic(); + diagnostic.in_file(location.file).with_call_stack(call_stack) + } +} + +impl MonomorphizationError { + fn into_diagnostic(self) -> CustomDiagnostic { + let message = self.to_string(); + let location = self.location(); + + CustomDiagnostic::simple_error(message, String::new(), location.span) + } +} diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 618eba8f190..6aa0abce152 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -8,16 +8,6 @@ //! //! The entry point to this pass is the `monomorphize` function which, starting from a given //! function, will monomorphize the entire reachable program. -use acvm::FieldElement; -use iter_extended::{btree_map, try_vecmap, vecmap}; -use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; -use noirc_printable_type::PrintableType; -use std::{ - collections::{BTreeMap, HashMap, VecDeque}, - unreachable, -}; -use thiserror::Error; - use crate::{ debug::DebugInstrumenter, hir_def::{ @@ -31,13 +21,25 @@ use crate::{ FunctionKind, IntegerBitSize, Signedness, Type, TypeBinding, TypeBindings, TypeVariable, TypeVariableKind, UnaryOp, Visibility, }; +use acvm::FieldElement; +use iter_extended::{btree_map, try_vecmap, vecmap}; +use noirc_errors::Location; +use noirc_printable_type::PrintableType; +use std::{ + collections::{BTreeMap, HashMap, VecDeque}, + unreachable, +}; -use self::ast::{Definition, FuncId, Function, LocalId, Program}; use self::debug_types::DebugTypeTracker; +use self::{ + ast::{Definition, FuncId, Function, LocalId, Program}, + errors::MonomorphizationError, +}; pub mod ast; mod debug; pub mod debug_types; +pub mod errors; pub mod printer; struct LambdaContext { @@ -88,40 +90,6 @@ struct Monomorphizer<'interner> { type HirType = crate::Type; -#[derive(Debug, Error)] -pub enum MonomorphizationError { - #[error("Length of generic array could not be determined.")] - UnknownArrayLength { location: Location }, -} - -impl MonomorphizationError { - fn call_stack(&self) -> Vec { - match self { - MonomorphizationError::UnknownArrayLength { location } => vec![*location], - } - } -} - -impl From for FileDiagnostic { - fn from(error: MonomorphizationError) -> FileDiagnostic { - let call_stack = error.call_stack(); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); - let diagnostic = error.into_diagnostic(); - diagnostic.in_file(file_id).with_call_stack(call_stack) - } -} - -impl MonomorphizationError { - fn into_diagnostic(self) -> CustomDiagnostic { - CustomDiagnostic::simple_error( - "Internal Consistency Evaluators Errors: \n - This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), - self.to_string(), - noirc_errors::Span::inclusive(0, 0) - ) - } -} - /// Starting from the given `main` function, monomorphize the entire program, /// replacing all references to type variables and NamedGenerics with concrete /// types, duplicating definitions as necessary to do so. @@ -161,6 +129,18 @@ pub fn monomorphize_debug( undo_instantiation_bindings(bindings); } + let func_sigs = monomorphizer + .finished_functions + .iter() + .flat_map(|(_, f)| { + if f.should_fold || f.id == Program::main_id() { + Some(f.func_sig.clone()) + } else { + None + } + }) + .collect(); + let functions = vecmap(monomorphizer.finished_functions, |(_, f)| f); let FuncMeta { return_distinctness, return_visibility, kind, .. } = monomorphizer.interner.function_meta(&main); @@ -169,6 +149,7 @@ pub fn monomorphize_debug( monomorphizer.debug_type_tracker.extract_vars_and_types(); let program = Program::new( functions, + func_sigs, function_sig, *return_distinctness, monomorphizer.return_location, @@ -303,20 +284,35 @@ impl<'interner> Monomorphizer<'interner> { } let meta = self.interner.function_meta(&f).clone(); + let func_sig = meta.function_signature(); + let modifiers = self.interner.function_modifiers(&f); let name = self.interner.function_name(&f).to_owned(); let body_expr_id = *self.interner.function(&f).as_expr(); let body_return_type = self.interner.id_type(body_expr_id); - let return_type = self.convert_type(match meta.return_type() { + let return_type = match meta.return_type() { Type::TraitAsType(..) => &body_return_type, - _ => meta.return_type(), - }); + other => other, + }; + + let return_type = Self::convert_type(return_type, meta.location)?; let unconstrained = modifiers.is_unconstrained; - let parameters = self.parameters(&meta.parameters); + let should_fold = meta.should_fold; + + let parameters = self.parameters(&meta.parameters)?; let body = self.expr(body_expr_id)?; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold, + func_sig, + }; self.push_function(id, function); Ok(()) @@ -329,12 +325,15 @@ impl<'interner> Monomorphizer<'interner> { /// Monomorphize each parameter, expanding tuple/struct patterns into multiple parameters /// and binding any generic types found. - fn parameters(&mut self, params: &Parameters) -> Vec<(ast::LocalId, bool, String, ast::Type)> { + fn parameters( + &mut self, + params: &Parameters, + ) -> Result, MonomorphizationError> { let mut new_params = Vec::with_capacity(params.len()); for (parameter, typ, _) in ¶ms.0 { - self.parameter(parameter, typ, &mut new_params); + self.parameter(parameter, typ, &mut new_params)?; } - new_params + Ok(new_params) } fn parameter( @@ -342,21 +341,22 @@ impl<'interner> Monomorphizer<'interner> { param: &HirPattern, typ: &HirType, new_params: &mut Vec<(ast::LocalId, bool, String, ast::Type)>, - ) { + ) -> Result<(), MonomorphizationError> { match param { HirPattern::Identifier(ident) => { let new_id = self.next_local_id(); let definition = self.interner.definition(ident.id); let name = definition.name.clone(); - new_params.push((new_id, definition.mutable, name, self.convert_type(typ))); + let typ = Self::convert_type(typ, ident.location)?; + new_params.push((new_id, definition.mutable, name, typ)); self.define_local(ident.id, new_id); } - HirPattern::Mutable(pattern, _) => self.parameter(pattern, typ, new_params), + HirPattern::Mutable(pattern, _) => self.parameter(pattern, typ, new_params)?, HirPattern::Tuple(fields, _) => { let tuple_field_types = unwrap_tuple_type(typ); for (field, typ) in fields.iter().zip(tuple_field_types) { - self.parameter(field, &typ, new_params); + self.parameter(field, &typ, new_params)?; } } HirPattern::Struct(_, fields, _) => { @@ -373,10 +373,11 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("Expected a field named '{field_name}' in the struct pattern") }); - self.parameter(field, &field_type, new_params); + self.parameter(field, &field_type, new_params)?; } } } + Ok(()) } fn expr( @@ -399,9 +400,10 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + let location = self.interner.id_location(expr); + let typ = Self::convert_type(&self.interner.id_type(expr), location)?; + if sign { - let typ = self.convert_type(&self.interner.id_type(expr)); - let location = self.interner.id_location(expr); match typ { ast::Type::Field => Literal(Integer(-value, typ, location)), ast::Type::Integer(_, bit_size) => { @@ -412,8 +414,6 @@ impl<'interner> Monomorphizer<'interner> { _ => unreachable!("Integer literal must be numeric"), } } else { - let typ = self.convert_type(&self.interner.id_type(expr)); - let location = self.interner.id_location(expr); Literal(Integer(value, typ, location)) } } @@ -437,7 +437,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Unary(ast::Unary { operator: prefix.operator, rhs: Box::new(self.expr(prefix.rhs)?), - result_type: self.convert_type(&self.interner.id_type(expr)), + result_type: Self::convert_type(&self.interner.id_type(expr), location)?, location, }) } @@ -466,8 +466,8 @@ impl<'interner> Monomorphizer<'interner> { let function_type = Type::Function(args, Box::new(ret.clone()), env); let method = infix.trait_method_id; - let func = self.resolve_trait_method_reference(expr, function_type, method); - self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location) + let func = self.resolve_trait_method_reference(expr, function_type, method)?; + self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location)? } else { let lhs = Box::new(lhs); let rhs = Box::new(rhs); @@ -485,23 +485,22 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Call(call) => self.function_call(call, expr)?, - HirExpression::Cast(cast) => ast::Expression::Cast(ast::Cast { - lhs: Box::new(self.expr(cast.lhs)?), - r#type: self.convert_type(&cast.r#type), - location: self.interner.expr_location(&expr), - }), + HirExpression::Cast(cast) => { + let location = self.interner.expr_location(&expr); + let typ = Self::convert_type(&cast.r#type, location)?; + let lhs = Box::new(self.expr(cast.lhs)?); + ast::Expression::Cast(ast::Cast { lhs, r#type: typ, location }) + } HirExpression::If(if_expr) => { - let cond = self.expr(if_expr.condition)?; - let then = self.expr(if_expr.consequence)?; + let condition = Box::new(self.expr(if_expr.condition)?); + let consequence = Box::new(self.expr(if_expr.consequence)?); let else_ = if_expr.alternative.map(|alt| self.expr(alt)).transpose()?.map(Box::new); - ast::Expression::If(ast::If { - condition: Box::new(cond), - consequence: Box::new(then), - alternative: else_, - typ: self.convert_type(&self.interner.id_type(expr)), - }) + + let location = self.interner.expr_location(&expr); + let typ = Self::convert_type(&self.interner.id_type(expr), location)?; + ast::Expression::If(ast::If { condition, consequence, alternative: else_, typ }) } HirExpression::Tuple(fields) => { @@ -528,7 +527,8 @@ impl<'interner> Monomorphizer<'interner> { array_elements: Vec, is_slice: bool, ) -> Result { - let typ = self.convert_type(&self.interner.id_type(array)); + let location = self.interner.expr_location(&array); + let typ = Self::convert_type(&self.interner.id_type(array), location)?; let contents = try_vecmap(array_elements, |id| self.expr(id))?; if is_slice { Ok(ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents, typ }))) @@ -544,7 +544,8 @@ impl<'interner> Monomorphizer<'interner> { length: HirType, is_slice: bool, ) -> Result { - let typ = self.convert_type(&self.interner.id_type(array)); + let location = self.interner.expr_location(&array); + let typ = Self::convert_type(&self.interner.id_type(array), location)?; let length = length.evaluate_to_u64().ok_or_else(|| { let location = self.interner.expr_location(&array); @@ -564,7 +565,8 @@ impl<'interner> Monomorphizer<'interner> { id: node_interner::ExprId, index: HirIndexExpression, ) -> Result { - let element_type = self.convert_type(&self.interner.id_type(id)); + let location = self.interner.expr_location(&id); + let element_type = Self::convert_type(&self.interner.id_type(id), location)?; let collection = Box::new(self.expr(index.collection)?); let index = Box::new(self.expr(index.index)?); @@ -595,11 +597,14 @@ impl<'interner> Monomorphizer<'interner> { self.define_local(for_loop.identifier.id, index_variable); let block = Box::new(self.expr(for_loop.block)?); + let index_location = for_loop.identifier.location; + let index_type = self.interner.id_type(for_loop.start_range); + let index_type = Self::convert_type(&index_type, index_location)?; Ok(ast::Expression::For(ast::For { index_variable, index_name: self.interner.definition_name(for_loop.identifier.id).to_owned(), - index_type: self.convert_type(&self.interner.id_type(for_loop.start_range)), + index_type, start_range: Box::new(start), end_range: Box::new(end), start_range_location: self.interner.expr_location(&for_loop.start_range), @@ -623,7 +628,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result { let expr = self.expr(let_statement.expression)?; let expected_type = self.interner.id_type(let_statement.expression); - Ok(self.unpack_pattern(let_statement.pattern, expr, &expected_type)) + self.unpack_pattern(let_statement.pattern, expr, &expected_type) } fn constructor( @@ -644,7 +649,8 @@ impl<'interner> Monomorphizer<'interner> { for (field_name, expr_id) in constructor.fields { let new_id = self.next_local_id(); let field_type = field_type_map.get(&field_name.0.contents).unwrap(); - let typ = self.convert_type(field_type); + let location = self.interner.expr_location(&expr_id); + let typ = Self::convert_type(field_type, location)?; field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); let expression = Box::new(self.expr(expr_id)?); @@ -688,19 +694,19 @@ impl<'interner> Monomorphizer<'interner> { pattern: HirPattern, value: ast::Expression, typ: &HirType, - ) -> ast::Expression { + ) -> Result { match pattern { HirPattern::Identifier(ident) => { let new_id = self.next_local_id(); self.define_local(ident.id, new_id); let definition = self.interner.definition(ident.id); - ast::Expression::Let(ast::Let { + Ok(ast::Expression::Let(ast::Let { id: new_id, mutable: definition.mutable, name: definition.name.clone(), expression: Box::new(value), - }) + })) } HirPattern::Mutable(pattern, _) => self.unpack_pattern(*pattern, value, typ), HirPattern::Tuple(patterns, _) => { @@ -729,7 +735,7 @@ impl<'interner> Monomorphizer<'interner> { &mut self, value: ast::Expression, fields: impl Iterator, - ) -> ast::Expression { + ) -> Result { let fresh_id = self.next_local_id(); let mut definitions = vec![ast::Expression::Let(ast::Let { @@ -740,21 +746,22 @@ impl<'interner> Monomorphizer<'interner> { })]; for (i, (field_pattern, field_type)) in fields.into_iter().enumerate() { - let location = None; + let location = field_pattern.location(); let mutable = false; let definition = Definition::Local(fresh_id); let name = i.to_string(); - let typ = self.convert_type(&field_type); + let typ = Self::convert_type(&field_type, location)?; + let location = Some(location); let new_rhs = ast::Expression::Ident(ast::Ident { location, mutable, definition, name, typ }); let new_rhs = ast::Expression::ExtractTupleField(Box::new(new_rhs), i); - let new_expr = self.unpack_pattern(field_pattern, new_rhs, &field_type); + let new_expr = self.unpack_pattern(field_pattern, new_rhs, &field_type)?; definitions.push(new_expr); } - ast::Expression::Block(definitions) + Ok(ast::Expression::Block(definitions)) } /// Find a captured variable in the innermost closure, and construct an expression @@ -780,15 +787,20 @@ impl<'interner> Monomorphizer<'interner> { } /// A local (ie non-global) ident only - fn local_ident(&mut self, ident: &HirIdent) -> Option { + fn local_ident( + &mut self, + ident: &HirIdent, + ) -> Result, MonomorphizationError> { let definition = self.interner.definition(ident.id); let name = definition.name.clone(); let mutable = definition.mutable; - let definition = self.lookup_local(ident.id)?; - let typ = self.convert_type(&self.interner.definition_type(ident.id)); + let Some(definition) = self.lookup_local(ident.id) else { + return Ok(None); + }; - Some(ast::Ident { location: Some(ident.location), mutable, definition, name, typ }) + let typ = Self::convert_type(&self.interner.definition_type(ident.id), ident.location)?; + Ok(Some(ast::Ident { location: Some(ident.location), mutable, definition, name, typ })) } fn ident( @@ -799,7 +811,7 @@ impl<'interner> Monomorphizer<'interner> { let typ = self.interner.id_type(expr_id); if let ImplKind::TraitMethod(method, _, _) = ident.impl_kind { - return Ok(self.resolve_trait_method_reference(expr_id, typ, method)); + return self.resolve_trait_method_reference(expr_id, typ, method); } let definition = self.interner.definition(ident.id); @@ -809,7 +821,7 @@ impl<'interner> Monomorphizer<'interner> { let location = Some(ident.location); let name = definition.name.clone(); let definition = self.lookup_function(*func_id, expr_id, &typ, None); - let typ = self.convert_type(&typ); + let typ = Self::convert_type(&typ, ident.location)?; let ident = ast::Ident { location, mutable, definition, name, typ: typ.clone() }; let ident_expression = ast::Expression::Ident(ident); if self.is_function_closure_type(&typ) { @@ -832,10 +844,13 @@ impl<'interner> Monomorphizer<'interner> { }; self.expr(let_.expression)? } - DefinitionKind::Local(_) => self.lookup_captured_expr(ident.id).unwrap_or_else(|| { - let ident = self.local_ident(&ident).unwrap(); - ast::Expression::Ident(ident) - }), + DefinitionKind::Local(_) => match self.lookup_captured_expr(ident.id) { + Some(expr) => expr, + None => { + let ident = self.local_ident(&ident)?.unwrap(); + ast::Expression::Ident(ident) + } + }, DefinitionKind::GenericType(type_variable) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => { @@ -848,7 +863,7 @@ impl<'interner> Monomorphizer<'interner> { let value = FieldElement::from(value as u128); let location = self.interner.id_location(expr_id); - let typ = self.convert_type(&typ); + let typ = Self::convert_type(&typ, ident.location)?; ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) } }; @@ -857,26 +872,28 @@ impl<'interner> Monomorphizer<'interner> { } /// Convert a non-tuple/struct type to a monomorphized type - fn convert_type(&self, typ: &HirType) -> ast::Type { - match typ { + fn convert_type(typ: &HirType, location: Location) -> Result { + Ok(match typ { HirType::FieldElement => ast::Type::Field, HirType::Integer(sign, bits) => ast::Type::Integer(*sign, *bits), HirType::Bool => ast::Type::Bool, HirType::String(size) => ast::Type::String(size.evaluate_to_u64().unwrap_or(0)), HirType::FmtString(size, fields) => { let size = size.evaluate_to_u64().unwrap_or(0); - let fields = Box::new(self.convert_type(fields.as_ref())); + let fields = Box::new(Self::convert_type(fields.as_ref(), location)?); ast::Type::FmtString(size, fields) } HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { - let element = Box::new(self.convert_type(element.as_ref())); - // TODO: convert to MonomorphizationError - let length = length.evaluate_to_u64().unwrap_or(0); + let element = Box::new(Self::convert_type(element.as_ref(), location)?); + let length = match length.evaluate_to_u64() { + Some(length) => length, + None => return Err(MonomorphizationError::TypeAnnotationsNeeded { location }), + }; ast::Type::Array(length, element) } HirType::Slice(element) => { - let element = Box::new(self.convert_type(element.as_ref())); + let element = Box::new(Self::convert_type(element.as_ref(), location)?); ast::Type::Slice(element) } HirType::TraitAsType(..) => { @@ -884,55 +901,53 @@ impl<'interner> Monomorphizer<'interner> { } HirType::NamedGeneric(binding, _) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return self.convert_type(binding); + return Self::convert_type(binding, location); } // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_type()); + binding.bind(HirType::default_int_or_field_type()); ast::Type::Field } HirType::TypeVariable(binding, kind) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return self.convert_type(binding); + return Self::convert_type(binding, location); } // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = if self.is_range_loop - && (matches!(kind, TypeVariableKind::IntegerOrField) - || matches!(kind, TypeVariableKind::Integer)) - { - Type::default_range_loop_type() - } else { - kind.default_type() + let default = match kind.default_type() { + Some(typ) => typ, + None => return Err(MonomorphizationError::TypeAnnotationsNeeded { location }), }; - let monomorphized_default = self.convert_type(&default); + let monomorphized_default = Self::convert_type(&default, location)?; binding.bind(default); monomorphized_default } HirType::Struct(def, args) => { let fields = def.borrow().get_fields(args); - let fields = vecmap(fields, |(_, field)| self.convert_type(&field)); + let fields = try_vecmap(fields, |(_, field)| Self::convert_type(&field, location))?; ast::Type::Tuple(fields) } - HirType::Alias(def, args) => self.convert_type(&def.borrow().get_type(args)), + HirType::Alias(def, args) => { + Self::convert_type(&def.borrow().get_type(args), location)? + } HirType::Tuple(fields) => { - let fields = vecmap(fields, |x| self.convert_type(x)); + let fields = try_vecmap(fields, |x| Self::convert_type(x, location))?; ast::Type::Tuple(fields) } HirType::Function(args, ret, env) => { - let args = vecmap(args, |x| self.convert_type(x)); - let ret = Box::new(self.convert_type(ret)); - let env = self.convert_type(env); + let args = try_vecmap(args, |x| Self::convert_type(x, location))?; + let ret = Box::new(Self::convert_type(ret, location)?); + let env = Self::convert_type(env, location)?; match &env { ast::Type::Unit => ast::Type::Function(args, ret, Box::new(env)), ast::Type::Tuple(_elements) => ast::Type::Tuple(vec![ @@ -948,7 +963,7 @@ impl<'interner> Monomorphizer<'interner> { } HirType::MutableReference(element) => { - let element = self.convert_type(element); + let element = Self::convert_type(element, location)?; ast::Type::MutableReference(Box::new(element)) } @@ -956,7 +971,7 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("Unexpected type {} found", typ) } HirType::Code => unreachable!("Tried to translate Code type into runtime code"), - } + }) } fn is_function_closure(&self, t: ast::Type) -> bool { @@ -987,7 +1002,7 @@ impl<'interner> Monomorphizer<'interner> { expr_id: node_interner::ExprId, function_type: HirType, method: TraitMethodId, - ) -> ast::Expression { + ) -> Result { let trait_impl = self .interner .get_selected_impl_for_expression(expr_id) @@ -1014,7 +1029,12 @@ impl<'interner> Monomorphizer<'interner> { Err(constraints) => { let failed_constraints = vecmap(constraints, |constraint| { let id = constraint.trait_id; - let name = self.interner.get_trait(id).name.to_string(); + let mut name = self.interner.get_trait(id).name.to_string(); + if !constraint.trait_generics.is_empty() { + let types = + vecmap(&constraint.trait_generics, |t| format!("{t:?}")); + name += &format!("<{}>", types.join(", ")); + } format!(" {}: {name}", constraint.typ) }) .join("\n"); @@ -1031,13 +1051,15 @@ impl<'interner> Monomorphizer<'interner> { }; let the_trait = self.interner.get_trait(method.trait_id); - ast::Expression::Ident(ast::Ident { + let location = self.interner.expr_location(&expr_id); + + Ok(ast::Expression::Ident(ast::Ident { definition: Definition::Function(func_id), mutable: false, location: None, name: the_trait.methods[method.method_index].name.0.contents.clone(), - typ: self.convert_type(&function_type), - }) + typ: Self::convert_type(&function_type, location)?, + })) } fn function_call( @@ -1052,7 +1074,8 @@ impl<'interner> Monomorphizer<'interner> { self.patch_debug_instrumentation_call(&call, &mut arguments)?; let return_type = self.interner.id_type(id); - let return_type = self.convert_type(&return_type); + let location = self.interner.expr_location(&id); + let return_type = Self::convert_type(&return_type, location)?; let location = call.location; @@ -1072,7 +1095,7 @@ impl<'interner> Monomorphizer<'interner> { let mut block_expressions = vec![]; let func_type = self.interner.id_type(call.func); - let func_type = self.convert_type(&func_type); + let func_type = Self::convert_type(&func_type, location)?; let is_closure = self.is_function_closure(func_type); let func = if is_closure { @@ -1094,7 +1117,7 @@ impl<'interner> Monomorphizer<'interner> { definition: Definition::Local(local_id), mutable: false, name: "tmp".to_string(), - typ: self.convert_type(&self.interner.id_type(call.func)), + typ: Self::convert_type(&self.interner.id_type(call.func), location)?, }); let env_argument = @@ -1293,24 +1316,24 @@ impl<'interner> Monomorphizer<'interner> { fn lvalue(&mut self, lvalue: HirLValue) -> Result { let value = match lvalue { - HirLValue::Ident(ident, _) => self - .lookup_captured_lvalue(ident.id) - .unwrap_or_else(|| ast::LValue::Ident(self.local_ident(&ident).unwrap())), + HirLValue::Ident(ident, _) => match self.lookup_captured_lvalue(ident.id) { + Some(value) => value, + None => ast::LValue::Ident(self.local_ident(&ident)?.unwrap()), + }, HirLValue::MemberAccess { object, field_index, .. } => { let field_index = field_index.unwrap(); let object = Box::new(self.lvalue(*object)?); ast::LValue::MemberAccess { object, field_index } } - HirLValue::Index { array, index, typ } => { - let location = self.interner.expr_location(&index); + HirLValue::Index { array, index, typ, location } => { let array = Box::new(self.lvalue(*array)?); let index = Box::new(self.expr(index)?); - let element_type = self.convert_type(&typ); + let element_type = Self::convert_type(&typ, location)?; ast::LValue::Index { array, index, element_type, location } } - HirLValue::Dereference { lvalue, element_type } => { + HirLValue::Dereference { lvalue, element_type, location } => { let reference = Box::new(self.lvalue(*lvalue)?); - let element_type = self.convert_type(&element_type); + let element_type = Self::convert_type(&element_type, location)?; ast::LValue::Dereference { reference, element_type } } }; @@ -1324,7 +1347,7 @@ impl<'interner> Monomorphizer<'interner> { expr: node_interner::ExprId, ) -> Result { if lambda.captures.is_empty() { - self.lambda_no_capture(lambda) + self.lambda_no_capture(lambda, expr) } else { let (setup, closure_variable) = self.lambda_with_setup(lambda, expr)?; Ok(ast::Expression::Block(vec![setup, closure_variable])) @@ -1334,16 +1357,19 @@ impl<'interner> Monomorphizer<'interner> { fn lambda_no_capture( &mut self, lambda: HirLambda, + expr: node_interner::ExprId, ) -> Result { - let ret_type = self.convert_type(&lambda.return_type); + let location = self.interner.expr_location(&expr); + let ret_type = Self::convert_type(&lambda.return_type, location)?; let lambda_name = "lambda"; - let parameter_types = vecmap(&lambda.parameters, |(_, typ)| self.convert_type(typ)); + let parameter_types = + try_vecmap(&lambda.parameters, |(_, typ)| Self::convert_type(typ, location))?; // Manually convert to Parameters type so we can reuse the self.parameters method let parameters = vecmap(lambda.parameters, |(pattern, typ)| (pattern, typ, Visibility::Private)).into(); - let parameters = self.parameters(¶meters); + let parameters = self.parameters(¶meters)?; let body = self.expr(lambda.body)?; let id = self.next_function_id(); @@ -1351,7 +1377,16 @@ impl<'interner> Monomorphizer<'interner> { let name = lambda_name.to_owned(); let unconstrained = false; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold: false, + func_sig: FunctionSignature::default(), + }; self.push_function(id, function); let typ = @@ -1386,15 +1421,17 @@ impl<'interner> Monomorphizer<'interner> { // patterns in the resulting tree, // which seems more fragile, we directly reuse the return parameters // of this function in those cases - let ret_type = self.convert_type(&lambda.return_type); + let location = self.interner.expr_location(&expr); + let ret_type = Self::convert_type(&lambda.return_type, location)?; let lambda_name = "lambda"; - let parameter_types = vecmap(&lambda.parameters, |(_, typ)| self.convert_type(typ)); + let parameter_types = + try_vecmap(&lambda.parameters, |(_, typ)| Self::convert_type(typ, location))?; // Manually convert to Parameters type so we can reuse the self.parameters method let parameters = vecmap(lambda.parameters, |(pattern, typ)| (pattern, typ, Visibility::Private)).into(); - let mut converted_parameters = self.parameters(¶meters); + let mut converted_parameters = self.parameters(¶meters)?; let id = self.next_function_id(); let name = lambda_name.to_owned(); @@ -1402,26 +1439,27 @@ impl<'interner> Monomorphizer<'interner> { let env_local_id = self.next_local_id(); let env_name = "env"; - let env_tuple = ast::Expression::Tuple(vecmap(&lambda.captures, |capture| { - match capture.transitive_capture_index { - Some(field_index) => match self.lambda_envs_stack.last() { - Some(lambda_ctx) => ast::Expression::ExtractTupleField( - Box::new(ast::Expression::Ident(lambda_ctx.env_ident.clone())), - field_index, - ), - None => unreachable!( - "Expected to find a parent closure environment, but found none" - ), - }, - None => { - let ident = self.local_ident(&capture.ident).unwrap(); - ast::Expression::Ident(ident) + let env_tuple = + ast::Expression::Tuple(try_vecmap(&lambda.captures, |capture| { + match capture.transitive_capture_index { + Some(field_index) => { + let lambda_ctx = self.lambda_envs_stack.last().expect( + "Expected to find a parent closure environment, but found none", + ); + + let ident = Box::new(ast::Expression::Ident(lambda_ctx.env_ident.clone())); + Ok(ast::Expression::ExtractTupleField(ident, field_index)) + } + None => { + let ident = self.local_ident(&capture.ident)?.unwrap(); + Ok(ast::Expression::Ident(ident)) + } } - } - })); + })?); + let expr_type = self.interner.id_type(expr); let env_typ = if let types::Type::Function(_, _, function_env_type) = expr_type { - self.convert_type(&function_env_type) + Self::convert_type(&function_env_type, location)? } else { unreachable!("expected a Function type for a Lambda node") }; @@ -1465,7 +1503,16 @@ impl<'interner> Monomorphizer<'interner> { parameters.append(&mut converted_parameters); let unconstrained = false; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold: false, + func_sig: FunctionSignature::default(), + }; self.push_function(id, function); let lambda_value = @@ -1583,7 +1630,16 @@ impl<'interner> Monomorphizer<'interner> { let name = lambda_name.to_owned(); let unconstrained = false; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold: false, + func_sig: FunctionSignature::default(), + }; self.push_function(id, function); ast::Expression::Ident(ast::Ident { @@ -1612,10 +1668,10 @@ impl<'interner> Monomorphizer<'interner> { rhs: ast::Expression, ret: Type, location: Location, - ) -> ast::Expression { + ) -> Result { let arguments = vec![lhs, rhs]; let func = Box::new(func); - let return_type = self.convert_type(&ret); + let return_type = Self::convert_type(&ret, location)?; let mut result = ast::Expression::Call(ast::Call { func, arguments, return_type, location }); @@ -1660,7 +1716,7 @@ impl<'interner> Monomorphizer<'interner> { _ => (), } - result + Ok(result) } /// Call sites are instantiated against the trait method, but when an impl is later selected, diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 09e0cb04d26..dcfceccdb57 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1079,6 +1079,7 @@ impl NodeInterner { /// constraint, but when where clauses are involved, the failing constraint may be several /// constraints deep. In this case, all of the constraints are returned, starting with the /// failing one. + /// If this list of failing constraints is empty, this means type annotations are required. pub fn lookup_trait_implementation( &self, object_type: &Type, @@ -1121,6 +1122,10 @@ impl NodeInterner { } /// Similar to `lookup_trait_implementation` but does not apply any type bindings on success. + /// On error returns either: + /// - 1+ failing trait constraints, including the original. + /// Each constraint after the first represents a `where` clause that was followed. + /// - 0 trait constraints indicating type annotations are needed to choose an impl. pub fn try_lookup_trait_implementation( &self, object_type: &Type, @@ -1138,6 +1143,11 @@ impl NodeInterner { Ok((impl_kind, bindings)) } + /// Returns the trait implementation if found. + /// On error returns either: + /// - 1+ failing trait constraints, including the original. + /// Each constraint after the first represents a `where` clause that was followed. + /// - 0 trait constraints indicating type annotations are needed to choose an impl. fn lookup_trait_implementation_helper( &self, object_type: &Type, @@ -1156,15 +1166,22 @@ impl NodeInterner { let object_type = object_type.substitute(type_bindings); + // If the object type isn't known, just return an error saying type annotations are needed. + if object_type.is_bindable() { + return Err(Vec::new()); + } + let impls = self.trait_implementation_map.get(&trait_id).ok_or_else(|| vec![make_constraint()])?; + let mut matching_impls = Vec::new(); + for (existing_object_type2, impl_kind) in impls { // Bug: We're instantiating only the object type's generics here, not all of the trait's generics like we need to let (existing_object_type, instantiation_bindings) = existing_object_type2.instantiate(self); - let mut fresh_bindings = TypeBindings::new(); + let mut fresh_bindings = type_bindings.clone(); let mut check_trait_generics = |impl_generics: &[Type]| { trait_generics.iter().zip(impl_generics).all(|(trait_generic, impl_generic2)| { @@ -1189,16 +1206,13 @@ impl NodeInterner { } if object_type.try_unify(&existing_object_type, &mut fresh_bindings).is_ok() { - // The unification was successful so we can append fresh_bindings to our bindings list - type_bindings.extend(fresh_bindings); - if let TraitImplKind::Normal(impl_id) = impl_kind { let trait_impl = self.get_trait_implementation(*impl_id); let trait_impl = trait_impl.borrow(); if let Err(mut errors) = self.validate_where_clause( &trait_impl.where_clause, - type_bindings, + &mut fresh_bindings, &instantiation_bindings, recursion_limit, ) { @@ -1207,11 +1221,20 @@ impl NodeInterner { } } - return Ok(impl_kind.clone()); + matching_impls.push((impl_kind.clone(), fresh_bindings)); } } - Err(vec![make_constraint()]) + if matching_impls.len() == 1 { + let (impl_, fresh_bindings) = matching_impls.pop().unwrap(); + *type_bindings = fresh_bindings; + Ok(impl_) + } else if matching_impls.is_empty() { + Err(vec![make_constraint()]) + } else { + // multiple matching impls, type annotations needed + Err(vec![]) + } } /// Verifies that each constraint in the given where clause is valid. @@ -1227,12 +1250,11 @@ impl NodeInterner { // Instantiation bindings are generally safe to force substitute into the same type. // This is needed here to undo any bindings done to trait methods by monomorphization. // Otherwise, an impl for (A, B) could get narrowed to only an impl for e.g. (u8, u16). - let constraint_type = constraint.typ.force_substitute(instantiation_bindings); - let constraint_type = constraint_type.substitute(type_bindings); + let constraint_type = + constraint.typ.force_substitute(instantiation_bindings).substitute(type_bindings); let trait_generics = vecmap(&constraint.trait_generics, |generic| { - let generic = generic.force_substitute(instantiation_bindings); - generic.substitute(type_bindings) + generic.force_substitute(instantiation_bindings).substitute(type_bindings) }); self.lookup_trait_implementation_helper( @@ -1241,10 +1263,11 @@ impl NodeInterner { &trait_generics, // Use a fresh set of type bindings here since the constraint_type originates from // our impl list, which we don't want to bind to. - &mut TypeBindings::new(), + type_bindings, recursion_limit - 1, )?; } + Ok(()) } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index a40355be8aa..7ecff12163a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -526,8 +526,8 @@ fn assign_operator() -> impl NoirParser { } enum LValueRhs { - MemberAccess(Ident), - Index(Expression), + MemberAccess(Ident, Span), + Index(Expression, Span), } fn lvalue<'a, P>(expr_parser: P) -> impl NoirParser + 'a @@ -539,23 +539,28 @@ where let dereferences = just(Token::Star) .ignore_then(lvalue.clone()) - .map(|lvalue| LValue::Dereference(Box::new(lvalue))); + .map_with_span(|lvalue, span| LValue::Dereference(Box::new(lvalue), span)); let parenthesized = lvalue.delimited_by(just(Token::LeftParen), just(Token::RightParen)); let term = choice((parenthesized, dereferences, l_ident)); - let l_member_rhs = just(Token::Dot).ignore_then(field_name()).map(LValueRhs::MemberAccess); + let l_member_rhs = + just(Token::Dot).ignore_then(field_name()).map_with_span(LValueRhs::MemberAccess); let l_index = expr_parser .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map(LValueRhs::Index); + .map_with_span(LValueRhs::Index); term.then(l_member_rhs.or(l_index).repeated()).foldl(|lvalue, rhs| match rhs { - LValueRhs::MemberAccess(field_name) => { - LValue::MemberAccess { object: Box::new(lvalue), field_name } + LValueRhs::MemberAccess(field_name, span) => { + let span = lvalue.span().merge(span); + LValue::MemberAccess { object: Box::new(lvalue), field_name, span } + } + LValueRhs::Index(index, span) => { + let span = lvalue.span().merge(span); + LValue::Index { array: Box::new(lvalue), index, span } } - LValueRhs::Index(index) => LValue::Index { array: Box::new(lvalue), index }, }) }) } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 6f92cb52a88..c4f0a8d67ba 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1033,19 +1033,19 @@ mod test { fn resolve_complex_closures() { let src = r#" fn main(x: Field) -> pub Field { - let closure_without_captures = |x| x + x; + let closure_without_captures = |x: Field| -> Field { x + x }; let a = closure_without_captures(1); - let closure_capturing_a_param = |y| y + x; + let closure_capturing_a_param = |y: Field| -> Field { y + x }; let b = closure_capturing_a_param(2); - let closure_capturing_a_local_var = |y| y + b; + let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; let c = closure_capturing_a_local_var(3); - let closure_with_transitive_captures = |y| { + let closure_with_transitive_captures = |y: Field| -> Field { let d = 5; - let nested_closure = |z| { - let doubly_nested_closure = |w| w + x + b; + let nested_closure = |z: Field| -> Field { + let doubly_nested_closure = |w: Field| -> Field { w + x + b }; a + z + y + d + x + doubly_nested_closure(4) + x + y }; let res = nested_closure(5); diff --git a/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts b/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts index 8b08b6f0dd8..39ad0d802fb 100644 --- a/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts +++ b/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts @@ -12,10 +12,12 @@ import { LogData } from '../../utils'; */ export class GithubDependencyResolver implements DependencyResolver { #fm: FileManager; + #fetch: typeof fetch; #log; - constructor(fm: FileManager) { + constructor(fm: FileManager, fetcher: typeof fetch) { this.#fm = fm; + this.#fetch = fetcher; this.#log = (msg: string, _data?: LogData) => { console.log(msg); }; @@ -56,7 +58,7 @@ export class GithubDependencyResolver implements DependencyResolver { return localArchivePath; } - const response = await fetch(url, { + const response = await this.#fetch(url, { method: 'GET', }); diff --git a/compiler/wasm/src/noir/noir-wasm-compiler.ts b/compiler/wasm/src/noir/noir-wasm-compiler.ts index 1ec3af1fd65..1fe3c79eae7 100644 --- a/compiler/wasm/src/noir/noir-wasm-compiler.ts +++ b/compiler/wasm/src/noir/noir-wasm-compiler.ts @@ -72,7 +72,8 @@ export class NoirWasmCompiler { const dependencyManager = new DependencyManager( [ new LocalDependencyResolver(fileManager), - new GithubCodeArchiveDependencyResolver(fileManager), + // use node's global fetch + new GithubCodeArchiveDependencyResolver(fileManager, fetch), // TODO support actual Git repositories ], noirPackage, diff --git a/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts b/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts index e7fae8afe8e..505b2269cd2 100644 --- a/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts +++ b/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts @@ -31,7 +31,7 @@ describe('GithubDependencyResolver', () => { let fetchStub: SinonStub | undefined; beforeEach(() => { - fetchStub = Sinon.stub(globalThis, 'fetch'); + fetchStub = Sinon.stub(); fm = createMemFSFileManager(createFsFromVolume(new Volume()), '/'); libDependency = { @@ -50,16 +50,12 @@ describe('GithubDependencyResolver', () => { }, }); - resolver = new GithubDependencyResolver(fm); + resolver = new GithubDependencyResolver(fm, fetchStub); // cut off outside access fetchStub.onCall(0).throws(new Error()); }); - afterEach(() => { - fetchStub?.restore(); - }); - it("returns null if it can't resolve a dependency", async () => { const dep = await resolver.resolveDependency(pkg, { path: '/lib-c', diff --git a/cspell.json b/cspell.json index e50940fbd10..16de9757fb8 100644 --- a/cspell.json +++ b/cspell.json @@ -6,6 +6,7 @@ "words": [ "aarch", "acir", + "acirs", "acvm", "aeiou", "appender", diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index 0eed50eb42b..dde29d7ee87 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -6,10 +6,10 @@ trait Eq { impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } } -impl Eq for u1 { fn eq(self, other: u1) -> bool { self == other } } -impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } } -impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } } impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other } } +impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } } +impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } } +impl Eq for u1 { fn eq(self, other: u1) -> bool { self == other } } impl Eq for i8 { fn eq(self, other: i8) -> bool { self == other } } impl Eq for i32 { fn eq(self, other: i32) -> bool { self == other } } @@ -107,8 +107,8 @@ trait Ord { // Note: Field deliberately does not implement Ord -impl Ord for u8 { - fn cmp(self, other: u8) -> Ordering { +impl Ord for u64 { + fn cmp(self, other: u64) -> Ordering { if self < other { Ordering::less() } else if self > other { @@ -131,8 +131,8 @@ impl Ord for u32 { } } -impl Ord for u64 { - fn cmp(self, other: u64) -> Ordering { +impl Ord for u8 { + fn cmp(self, other: u8) -> Ordering { if self < other { Ordering::less() } else if self > other { diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 5b97d809896..12bf373e671 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -1,7 +1,7 @@ use crate::hash::Hasher; use crate::default::Default; -global RATE = 3; +global RATE: u32 = 3; struct Poseidon2 { cache: [Field;3], diff --git a/noir_stdlib/src/ops.nr b/noir_stdlib/src/ops.nr index e561265629e..d855e794fb4 100644 --- a/noir_stdlib/src/ops.nr +++ b/noir_stdlib/src/ops.nr @@ -6,9 +6,9 @@ trait Add { impl Add for Field { fn add(self, other: Field) -> Field { self + other } } -impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } } -impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } } impl Add for u64 { fn add(self, other: u64) -> u64 { self + other } } +impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } } +impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } } impl Add for i8 { fn add(self, other: i8) -> i8 { self + other } } impl Add for i32 { fn add(self, other: i32) -> i32 { self + other } } @@ -22,9 +22,9 @@ trait Sub { impl Sub for Field { fn sub(self, other: Field) -> Field { self - other } } -impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } } -impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } } impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other } } +impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } } +impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } } impl Sub for i8 { fn sub(self, other: i8) -> i8 { self - other } } impl Sub for i32 { fn sub(self, other: i32) -> i32 { self - other } } @@ -38,9 +38,9 @@ trait Mul { impl Mul for Field { fn mul(self, other: Field) -> Field { self * other } } -impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } } -impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } } impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other } } +impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } } +impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } } impl Mul for i8 { fn mul(self, other: i8) -> i8 { self * other } } impl Mul for i32 { fn mul(self, other: i32) -> i32 { self * other } } @@ -54,9 +54,9 @@ trait Div { impl Div for Field { fn div(self, other: Field) -> Field { self / other } } -impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } } -impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } } impl Div for u64 { fn div(self, other: u64) -> u64 { self / other } } +impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } } +impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } } impl Div for i8 { fn div(self, other: i8) -> i8 { self / other } } impl Div for i32 { fn div(self, other: i32) -> i32 { self / other } } @@ -68,9 +68,9 @@ trait Rem{ } // docs:end:rem-trait -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } @@ -84,9 +84,9 @@ trait BitOr { impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } @@ -100,9 +100,9 @@ trait BitAnd { impl BitAnd for bool { fn bitand(self, other: bool) -> bool { self & other } } -impl BitAnd for u8 { fn bitand(self, other: u8) -> u8 { self & other } } -impl BitAnd for u32 { fn bitand(self, other: u32) -> u32 { self & other } } impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other } } +impl BitAnd for u32 { fn bitand(self, other: u32) -> u32 { self & other } } +impl BitAnd for u8 { fn bitand(self, other: u8) -> u8 { self & other } } impl BitAnd for i8 { fn bitand(self, other: i8) -> i8 { self & other } } impl BitAnd for i32 { fn bitand(self, other: i32) -> i32 { self & other } } @@ -116,9 +116,9 @@ trait BitXor { impl BitXor for bool { fn bitxor(self, other: bool) -> bool { self ^ other } } -impl BitXor for u8 { fn bitxor(self, other: u8) -> u8 { self ^ other } } -impl BitXor for u32 { fn bitxor(self, other: u32) -> u32 { self ^ other } } impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other } } +impl BitXor for u32 { fn bitxor(self, other: u32) -> u32 { self ^ other } } +impl BitXor for u8 { fn bitxor(self, other: u8) -> u8 { self ^ other } } impl BitXor for i8 { fn bitxor(self, other: i8) -> i8 { self ^ other } } impl BitXor for i32 { fn bitxor(self, other: i32) -> i32 { self ^ other } } @@ -130,14 +130,14 @@ trait Shl { } // docs:end:shl-trait -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u1 { fn shl(self, other: u1) -> u1 { self << other } } -// Bit shifting is not currently supported for signed integer types -// impl Shl for i8 { fn shl(self, other: i8) -> i8 { self << other } } -// impl Shl for i32 { fn shl(self, other: i32) -> i32 { self << other } } -// impl Shl for i64 { fn shl(self, other: i64) -> i64 { self << other } } +impl Shl for i8 { fn shl(self, other: i8) -> i8 { self << other } } +impl Shl for i32 { fn shl(self, other: i32) -> i32 { self << other } } +impl Shl for i64 { fn shl(self, other: i64) -> i64 { self << other } } // docs:start:shr-trait trait Shr { @@ -145,11 +145,11 @@ trait Shr { } // docs:end:shr-trait -impl Shr for u8 { fn shr(self, other: u8) -> u8 { self >> other } } -impl Shr for u32 { fn shr(self, other: u32) -> u32 { self >> other } } impl Shr for u64 { fn shr(self, other: u64) -> u64 { self >> other } } +impl Shr for u32 { fn shr(self, other: u32) -> u32 { self >> other } } +impl Shr for u8 { fn shr(self, other: u8) -> u8 { self >> other } } +impl Shr for u1 { fn shr(self, other: u1) -> u1 { self >> other } } -// Bit shifting is not currently supported for signed integer types -// impl Shr for i8 { fn shr(self, other: i8) -> i8 { self >> other } } -// impl Shr for i32 { fn shr(self, other: i32) -> i32 { self >> other } } -// impl Shr for i64 { fn shr(self, other: i64) -> i64 { self >> other } } +impl Shr for i8 { fn shr(self, other: i8) -> i8 { self >> other } } +impl Shr for i32 { fn shr(self, other: i32) -> i32 { self >> other } } +impl Shr for i64 { fn shr(self, other: i64) -> i64 { self >> other } } diff --git a/test_programs/compile_failure/array_length_defaulting/Nargo.toml b/test_programs/compile_failure/array_length_defaulting/Nargo.toml new file mode 100644 index 00000000000..fa376596ee2 --- /dev/null +++ b/test_programs/compile_failure/array_length_defaulting/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "array_length_defaulting" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] diff --git a/test_programs/compile_failure/array_length_defaulting/src/main.nr b/test_programs/compile_failure/array_length_defaulting/src/main.nr new file mode 100644 index 00000000000..216a9ae3f0c --- /dev/null +++ b/test_programs/compile_failure/array_length_defaulting/src/main.nr @@ -0,0 +1,10 @@ +fn main() { + let x = dep::std::unsafe::zeroed(); + foo(x); +} + +fn foo(array: [Field; N]) { + for elem in array { + println(elem); + } +} diff --git a/test_programs/compile_failure/typevar_default/Nargo.toml b/test_programs/compile_failure/typevar_default/Nargo.toml new file mode 100644 index 00000000000..b3cd08bb8fe --- /dev/null +++ b/test_programs/compile_failure/typevar_default/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "typevar_default" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/typevar_default/src/main.nr b/test_programs/compile_failure/typevar_default/src/main.nr new file mode 100644 index 00000000000..1eb9cf63de3 --- /dev/null +++ b/test_programs/compile_failure/typevar_default/src/main.nr @@ -0,0 +1,12 @@ +fn main() { + // Expecting error: type annotations needed (N not known) + let _ = slice_to_array(&[1, 2, 3]); +} + +fn slice_to_array(slice: [Field]) -> [Field; N] { + let mut array = [0; N]; + for i in 0 .. N { + array[i] = slice[i]; + } + array +} diff --git a/test_programs/compile_success_empty/option/src/main.nr b/test_programs/compile_success_empty/option/src/main.nr index 989c8f65bf4..c5f321256b1 100644 --- a/test_programs/compile_success_empty/option/src/main.nr +++ b/test_programs/compile_success_empty/option/src/main.nr @@ -39,9 +39,9 @@ fn main() { let add1_u64 = |value: Field| Option::some(value as u64 + 1); - assert(none.and_then(|_value| Option::none()).is_none()); + assert(none.and_then(|_value| none).is_none()); assert(none.and_then(add1_u64).is_none()); - assert(some.and_then(|_value| Option::none()).is_none()); + assert(some.and_then(|_value| none).is_none()); assert(some.and_then(add1_u64).unwrap() == 4); assert(some.and_then(|x| Option::some(x + ten)).unwrap() == 13); diff --git a/test_programs/compile_success_empty/regression_4635/Nargo.toml b/test_programs/compile_success_empty/regression_4635/Nargo.toml new file mode 100644 index 00000000000..563e262410f --- /dev/null +++ b/test_programs/compile_success_empty/regression_4635/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_4635" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_4635/src/main.nr b/test_programs/compile_success_empty/regression_4635/src/main.nr new file mode 100644 index 00000000000..23918e30785 --- /dev/null +++ b/test_programs/compile_success_empty/regression_4635/src/main.nr @@ -0,0 +1,59 @@ +trait FromField { + fn from_field(field: Field) -> Self; +} + +impl FromField for Field { + fn from_field(value: Field) -> Self { + value + } +} + +trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; +} + +global AZTEC_ADDRESS_LENGTH = 1; + +struct AztecAddress { + inner : Field +} + +impl FromField for AztecAddress { + fn from_field(value: Field) -> Self { + Self { inner: value } + } +} + +impl Deserialize for AztecAddress { + fn deserialize(fields: [Field; AZTEC_ADDRESS_LENGTH]) -> Self { + AztecAddress::from_field(fields[0]) + } +} + +impl Eq for AztecAddress { + fn eq(self, other: Self) -> bool { + self.inner == other.inner + } +} + +// Custom code + +struct MyStruct { + a: T +} + +impl Deserialize<1> for MyStruct { + fn deserialize(fields: [Field; 1]) -> Self where T: FromField { + Self{ a: FromField::from_field(fields[0]) } + } +} + +fn main() { + let fields = [5; 1]; + let foo = MyStruct::deserialize(fields); // Note I don't specify T here (the type of `foo.a`) + + let bar = AztecAddress { inner: 5 }; + + // Here `T` is apparently inferred to be `AztecAddress`, presumably because of the comparison. + assert(foo.a == bar); +} diff --git a/test_programs/compile_failure/hashmap_load_factor/Nargo.toml b/test_programs/execution_failure/hashmap_load_factor/Nargo.toml similarity index 100% rename from test_programs/compile_failure/hashmap_load_factor/Nargo.toml rename to test_programs/execution_failure/hashmap_load_factor/Nargo.toml diff --git a/test_programs/execution_failure/hashmap_load_factor/Prover.toml b/test_programs/execution_failure/hashmap_load_factor/Prover.toml new file mode 100644 index 00000000000..6d72cab47fa --- /dev/null +++ b/test_programs/execution_failure/hashmap_load_factor/Prover.toml @@ -0,0 +1,23 @@ +[[input]] +key = 1 +value = 0 + +[[input]] +key = 2 +value = 0 + +[[input]] +key = 3 +value = 0 + +[[input]] +key = 4 +value = 0 + +[[input]] +key = 5 +value = 0 + +[[input]] +key = 6 +value = 0 diff --git a/test_programs/compile_failure/hashmap_load_factor/src/main.nr b/test_programs/execution_failure/hashmap_load_factor/src/main.nr similarity index 100% rename from test_programs/compile_failure/hashmap_load_factor/src/main.nr rename to test_programs/execution_failure/hashmap_load_factor/src/main.nr diff --git a/test_programs/execution_success/bit_shifts_runtime/src/main.nr b/test_programs/execution_success/bit_shifts_runtime/src/main.nr index 28b3ef656c1..ff424c9fbf6 100644 --- a/test_programs/execution_success/bit_shifts_runtime/src/main.nr +++ b/test_programs/execution_success/bit_shifts_runtime/src/main.nr @@ -7,7 +7,7 @@ fn main(x: u64, y: u64) { assert(x >> y == 32); // Bit-shift with signed integers - let mut a :i8 = y as i8; + let mut a: i8 = y as i8; let mut b: i8 = x as i8; assert(b << 1 == -128); assert(b >> 2 == 16); diff --git a/test_programs/execution_success/fold_basic/Nargo.toml b/test_programs/execution_success/fold_basic/Nargo.toml new file mode 100644 index 00000000000..575ba1f3ad1 --- /dev/null +++ b/test_programs/execution_success/fold_basic/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_basic" +type = "bin" +authors = [""] +compiler_version = ">=0.25.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/fold_basic/Prover.toml b/test_programs/execution_success/fold_basic/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/test_programs/execution_success/fold_basic/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/test_programs/execution_success/fold_basic/src/main.nr b/test_programs/execution_success/fold_basic/src/main.nr new file mode 100644 index 00000000000..6c17120660b --- /dev/null +++ b/test_programs/execution_success/fold_basic/src/main.nr @@ -0,0 +1,11 @@ +fn main(x: Field, y: pub Field) { + let z = foo(x, y); + let z2 = foo(x, y); + assert(z == z2); +} + +#[fold] +fn foo(x: Field, y: Field) -> Field { + assert(x != y); + x +} diff --git a/test_programs/execution_success/fold_basic_nested_call/Nargo.toml b/test_programs/execution_success/fold_basic_nested_call/Nargo.toml new file mode 100644 index 00000000000..1b3c32999ae --- /dev/null +++ b/test_programs/execution_success/fold_basic_nested_call/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_basic_nested_call" +type = "bin" +authors = [""] +compiler_version = ">=0.25.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/fold_basic_nested_call/Prover.toml b/test_programs/execution_success/fold_basic_nested_call/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/test_programs/execution_success/fold_basic_nested_call/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/test_programs/execution_success/fold_basic_nested_call/src/main.nr b/test_programs/execution_success/fold_basic_nested_call/src/main.nr new file mode 100644 index 00000000000..6d02b734727 --- /dev/null +++ b/test_programs/execution_success/fold_basic_nested_call/src/main.nr @@ -0,0 +1,16 @@ +fn main(x: Field, y: pub Field) { + let z = func_with_nested_foo_call(x, y); + let z2 = func_with_nested_foo_call(x, y); + assert(z == z2); +} + +#[fold] +fn func_with_nested_foo_call(x: Field, y: Field) -> Field { + foo(x + 2, y) +} + +#[fold] +fn foo(x: Field, y: Field) -> Field { + assert(x != y); + x +} diff --git a/test_programs/execution_success/prelude/src/main.nr b/test_programs/execution_success/prelude/src/main.nr index c9ae448c486..226341f1e7b 100644 --- a/test_programs/execution_success/prelude/src/main.nr +++ b/test_programs/execution_success/prelude/src/main.nr @@ -1,6 +1,6 @@ fn main() { - let _xs = Vec::new(); - let _option = Option::none(); + let _xs: Vec = Vec::new(); + let _option: Option = Option::none(); print("42\n"); println("42"); diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs index b76d0eccc29..86e7277451f 100644 --- a/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -1,14 +1,14 @@ use std::io::{self, Write}; use acir::circuit::Program; -use acir::native_types::WitnessMap; +use acir::native_types::{WitnessMap, WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use crate::cli::fs::inputs::{read_bytecode_from_file, read_inputs_from_file}; use crate::cli::fs::witness::save_witness_to_dir; use crate::errors::CliError; -use nargo::ops::{execute_circuit, DefaultForeignCallExecutor}; +use nargo::ops::{execute_program, DefaultForeignCallExecutor}; use super::fs::witness::create_output_witness_string; @@ -39,8 +39,11 @@ pub(crate) struct ExecuteCommand { fn run_command(args: ExecuteCommand) -> Result { let bytecode = read_bytecode_from_file(&args.working_directory, &args.bytecode)?; let circuit_inputs = read_inputs_from_file(&args.working_directory, &args.input_witness)?; - let output_witness = execute_program_from_witness(&circuit_inputs, &bytecode, None)?; - let output_witness_string = create_output_witness_string(&output_witness)?; + let output_witness = execute_program_from_witness(circuit_inputs, &bytecode, None)?; + assert_eq!(output_witness.length(), 1, "ACVM CLI only supports a witness stack of size 1"); + let output_witness_string = create_output_witness_string( + &output_witness.peek().expect("Should have a witness stack item").witness, + )?; if args.output_witness.is_some() { save_witness_to_dir( &output_witness_string, @@ -61,16 +64,16 @@ pub(crate) fn run(args: ExecuteCommand) -> Result { } pub(crate) fn execute_program_from_witness( - inputs_map: &WitnessMap, + inputs_map: WitnessMap, bytecode: &[u8], foreign_call_resolver_url: Option<&str>, -) -> Result { +) -> Result { let blackbox_solver = Bn254BlackBoxSolver::new(); let program: Program = Program::deserialize_program(bytecode) .map_err(|_| CliError::CircuitDeserializationError())?; - execute_circuit( - &program.functions[0], - inputs_map.clone(), + execute_program( + &program, + inputs_map, &blackbox_solver, &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), ) diff --git a/tooling/backend_interface/src/proof_system.rs b/tooling/backend_interface/src/proof_system.rs index 105ae337793..3b47a7ced3a 100644 --- a/tooling/backend_interface/src/proof_system.rs +++ b/tooling/backend_interface/src/proof_system.rs @@ -56,7 +56,7 @@ impl Backend { pub fn prove( &self, program: &Program, - witness_values: WitnessStack, + witness_stack: WitnessStack, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -66,7 +66,7 @@ impl Backend { // Create a temporary file for the witness let serialized_witnesses: Vec = - witness_values.try_into().expect("could not serialize witness map"); + witness_stack.try_into().expect("could not serialize witness map"); let witness_path = temp_directory.join("witness").with_extension("tr"); write_to_file(&serialized_witnesses, &witness_path); diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index 854e284dd43..5a42ef36e7e 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -12,3 +12,5 @@ references scalar_mul signed_comparison to_bytes_integration +fold_basic +fold_basic_nested_call diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index f0de8d5d1c8..ba12a20460d 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -381,6 +381,9 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { ACVMStatus::RequiresForeignCall(_) => { unreachable!("Unexpected pending foreign call resolution"); } + ACVMStatus::RequiresAcirCall(_) => { + todo!("Multiple ACIR calls are not supported"); + } } } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index c743768bee2..ff238d79a46 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -64,7 +64,9 @@ impl NargoError { ExecutionError::SolvingError(error) => match error { OpcodeResolutionError::IndexOutOfBounds { .. } | OpcodeResolutionError::OpcodeNotSolvable(_) - | OpcodeResolutionError::UnsatisfiedConstrain { .. } => None, + | OpcodeResolutionError::UnsatisfiedConstrain { .. } + | OpcodeResolutionError::AcirMainCallAttempted { .. } + | OpcodeResolutionError::AcirCallOutputsMismatch { .. } => None, OpcodeResolutionError::BrilligFunctionFailed { message, .. } => Some(message), OpcodeResolutionError::BlackBoxFunctionFailed(_, reason) => Some(reason), }, diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 370393fea09..6d328d65119 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,5 +1,7 @@ +use acvm::acir::circuit::Program; +use acvm::acir::native_types::WitnessStack; use acvm::brillig_vm::brillig::ForeignCallResult; -use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; +use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; @@ -8,73 +10,145 @@ use crate::NargoError; use super::foreign_calls::{ForeignCallExecutor, NargoForeignCallResult}; -#[tracing::instrument(level = "trace", skip_all)] -pub fn execute_circuit( - circuit: &Circuit, - initial_witness: WitnessMap, - blackbox_solver: &B, - foreign_call_executor: &mut F, -) -> Result { - let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - - // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. - let mut assert_message: Option = None; - loop { - let solver_status = acvm.solve(); - - match solver_status { - ACVMStatus::Solved => break, - ACVMStatus::InProgress => { - unreachable!("Execution should not stop while in `InProgress` state.") - } - ACVMStatus::Failure(error) => { - let call_stack = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - Some(call_stack.clone()) - } - _ => None, - }; - - return Err(NargoError::ExecutionError(match call_stack { - Some(call_stack) => { - // First check whether we have a runtime assertion message that should be resolved on an ACVM failure - // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded - // messages associated with a specific `OpcodeLocation`. - // Otherwise return the provided opcode resolution error. - if let Some(assert_message) = assert_message { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else if let Some(assert_message) = circuit.get_assert_message( - *call_stack.last().expect("Call stacks should not be empty"), - ) { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else { - ExecutionError::SolvingError(error) +struct ProgramExecutor<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> { + functions: &'a [Circuit], + // This gets built as we run through the program looking at each function call + witness_stack: WitnessStack, + + blackbox_solver: &'a B, + + foreign_call_executor: &'a mut F, +} + +impl<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> ProgramExecutor<'a, B, F> { + fn new( + functions: &'a [Circuit], + blackbox_solver: &'a B, + foreign_call_executor: &'a mut F, + ) -> Self { + ProgramExecutor { + functions, + witness_stack: WitnessStack::default(), + blackbox_solver, + foreign_call_executor, + } + } + + fn finalize(self) -> WitnessStack { + self.witness_stack + } + + #[tracing::instrument(level = "trace", skip_all)] + fn execute_circuit( + &mut self, + circuit: &Circuit, + initial_witness: WitnessMap, + ) -> Result { + let mut acvm = ACVM::new(self.blackbox_solver, &circuit.opcodes, initial_witness); + + // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. + let mut assert_message: Option = None; + loop { + let solver_status = acvm.solve(); + + match solver_status { + ACVMStatus::Solved => break, + ACVMStatus::InProgress => { + unreachable!("Execution should not stop while in `InProgress` state.") + } + ACVMStatus::Failure(error) => { + let call_stack = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { + opcode_location: ErrorLocation::Resolved(opcode_location), + } => Some(vec![*opcode_location]), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + Some(call_stack.clone()) } - } - None => ExecutionError::SolvingError(error), - })); - } - ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - match foreign_call_result { - NargoForeignCallResult::BrilligOutput(foreign_call_result) => { - acvm.resolve_pending_foreign_call(foreign_call_result); - } - NargoForeignCallResult::ResolvedAssertMessage(message) => { - if assert_message.is_some() { - unreachable!("Resolving an assert message should happen only once as the VM should have failed"); + _ => None, + }; + + return Err(NargoError::ExecutionError(match call_stack { + Some(call_stack) => { + // First check whether we have a runtime assertion message that should be resolved on an ACVM failure + // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded + // messages associated with a specific `OpcodeLocation`. + // Otherwise return the provided opcode resolution error. + if let Some(assert_message) = assert_message { + ExecutionError::AssertionFailed( + assert_message.to_owned(), + call_stack, + ) + } else if let Some(assert_message) = circuit.get_assert_message( + *call_stack.last().expect("Call stacks should not be empty"), + ) { + ExecutionError::AssertionFailed( + assert_message.to_owned(), + call_stack, + ) + } else { + ExecutionError::SolvingError(error) + } + } + None => ExecutionError::SolvingError(error), + })); + } + ACVMStatus::RequiresForeignCall(foreign_call) => { + let foreign_call_result = self.foreign_call_executor.execute(&foreign_call)?; + match foreign_call_result { + NargoForeignCallResult::BrilligOutput(foreign_call_result) => { + acvm.resolve_pending_foreign_call(foreign_call_result); } - assert_message = Some(message); + NargoForeignCallResult::ResolvedAssertMessage(message) => { + if assert_message.is_some() { + unreachable!("Resolving an assert message should happen only once as the VM should have failed"); + } + assert_message = Some(message); - acvm.resolve_pending_foreign_call(ForeignCallResult::default()); + acvm.resolve_pending_foreign_call(ForeignCallResult::default()); + } } } + ACVMStatus::RequiresAcirCall(call_info) => { + let acir_to_call = &self.functions[call_info.id as usize]; + let initial_witness = call_info.initial_witness; + let call_solved_witness = + self.execute_circuit(acir_to_call, initial_witness)?; + let mut call_resolved_outputs = Vec::new(); + for return_witness_index in acir_to_call.return_values.indices() { + if let Some(return_value) = + call_solved_witness.get_index(return_witness_index) + { + call_resolved_outputs.push(*return_value); + } else { + return Err(ExecutionError::SolvingError( + OpcodeNotSolvable::MissingAssignment(return_witness_index).into(), + ) + .into()); + } + } + acvm.resolve_pending_acir_call(call_resolved_outputs); + self.witness_stack.push(call_info.id, call_solved_witness); + } } } + + Ok(acvm.finalize()) } +} + +#[tracing::instrument(level = "trace", skip_all)] +pub fn execute_program( + program: &Program, + initial_witness: WitnessMap, + blackbox_solver: &B, + foreign_call_executor: &mut F, +) -> Result { + let main = &program.functions[0]; + + let mut executor = + ProgramExecutor::new(&program.functions, blackbox_solver, foreign_call_executor); + let main_witness = executor.execute_circuit(main, initial_witness)?; + executor.witness_stack.push(0, main_witness); - Ok(acvm.finalize()) + Ok(executor.finalize()) } diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 55e9e927800..2f5e6ebb7d4 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -2,7 +2,7 @@ pub use self::compile::{ collect_errors, compile_contract, compile_program, compile_program_with_debug_instrumenter, compile_workspace, report_errors, }; -pub use self::execute::execute_circuit; +pub use self::execute::execute_program; pub use self::foreign_calls::{ DefaultForeignCallExecutor, ForeignCall, ForeignCallExecutor, NargoForeignCallResult, }; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 8cf2934da4d..45b1a88f99c 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -1,4 +1,7 @@ -use acvm::{acir::native_types::WitnessMap, BlackBoxFunctionSolver}; +use acvm::{ + acir::native_types::{WitnessMap, WitnessStack}, + BlackBoxFunctionSolver, +}; use noirc_driver::{compile_no_check, CompileError, CompileOptions}; use noirc_errors::{debug_info::DebugInfo, FileDiagnostic}; use noirc_evaluator::errors::RuntimeError; @@ -6,7 +9,7 @@ use noirc_frontend::hir::{def_map::TestFunction, Context}; use crate::{errors::try_to_diagnose_runtime_error, NargoError}; -use super::{execute_circuit, DefaultForeignCallExecutor}; +use super::{execute_program, DefaultForeignCallExecutor}; pub enum TestStatus { Pass, @@ -33,9 +36,8 @@ pub fn run_test( Ok(compiled_program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. - let circuit_execution = execute_circuit( - // TODO(https://github.com/noir-lang/noir/issues/4428) - &compiled_program.program.functions[0], + let circuit_execution = execute_program( + &compiled_program.program, WitnessMap::new(), blackbox_solver, &mut DefaultForeignCallExecutor::new(show_output, foreign_call_resolver_url), @@ -83,7 +85,7 @@ fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunct fn test_status_program_compile_pass( test_function: &TestFunction, debug: DebugInfo, - circuit_execution: Result, + circuit_execution: Result, ) -> TestStatus { let circuit_execution_err = match circuit_execution { // Circuit execution was successful; ie no errors or unsatisfied constraints diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 1f448105ee2..4f3e2886b2e 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -187,7 +187,11 @@ fn run_async( } if let Some(witness_name) = witness_name { - let witness_path = save_witness_to_dir(solved_witness, witness_name, target_dir)?; + let witness_path = save_witness_to_dir( + WitnessStack::from(solved_witness), + witness_name, + target_dir, + )?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 022bf7b761e..697f6d7c1ea 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,4 +1,4 @@ -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::WitnessStack; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -91,7 +91,7 @@ pub(crate) fn run( let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); - let (return_value, solved_witness) = execute_program_and_decode( + let (return_value, witness_stack) = execute_program_and_decode( compiled_program, package, &args.prover_name, @@ -103,7 +103,7 @@ pub(crate) fn run( println!("[{}] Circuit output: {return_value:?}", package.name); } if let Some(witness_name) = &args.witness_name { - let witness_path = save_witness_to_dir(solved_witness, witness_name, target_dir)?; + let witness_path = save_witness_to_dir(witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } @@ -116,35 +116,37 @@ fn execute_program_and_decode( package: &Package, prover_name: &str, foreign_call_resolver_url: Option<&str>, -) -> Result<(Option, WitnessMap), CliError> { +) -> Result<(Option, WitnessStack), CliError> { // Parse the initial witness values from Prover.toml let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; - let solved_witness = execute_program(&program, &inputs_map, foreign_call_resolver_url)?; + let witness_stack = execute_program(&program, &inputs_map, foreign_call_resolver_url)?; let public_abi = program.abi.public_abi(); - let (_, return_value) = public_abi.decode(&solved_witness)?; + // Get the entry point witness for the ABI + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + let (_, return_value) = public_abi.decode(main_witness)?; - Ok((return_value, solved_witness)) + Ok((return_value, witness_stack)) } pub(crate) fn execute_program( compiled_program: &CompiledProgram, inputs_map: &InputMap, foreign_call_resolver_url: Option<&str>, -) -> Result { +) -> Result { let blackbox_solver = Bn254BlackBoxSolver::new(); let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - // TODO(https://github.com/noir-lang/noir/issues/4428) - let solved_witness_err = nargo::ops::execute_circuit( - &compiled_program.program.functions[0], + let solved_witness_stack_err = nargo::ops::execute_program( + &compiled_program.program, initial_witness, &blackbox_solver, &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), ); - match solved_witness_err { - Ok(solved_witness) => Ok(solved_witness), + match solved_witness_stack_err { + Ok(solved_witness_stack) => Ok(solved_witness_stack), Err(err) => { let debug_artifact = DebugArtifact { debug_symbols: vec![compiled_program.debug.clone()], diff --git a/tooling/nargo_cli/src/cli/fs/witness.rs b/tooling/nargo_cli/src/cli/fs/witness.rs index 52b5c385e5d..613cdec28da 100644 --- a/tooling/nargo_cli/src/cli/fs/witness.rs +++ b/tooling/nargo_cli/src/cli/fs/witness.rs @@ -1,21 +1,19 @@ use std::path::{Path, PathBuf}; -use acvm::acir::native_types::{WitnessMap, WitnessStack}; +use acvm::acir::native_types::WitnessStack; use nargo::constants::WITNESS_EXT; use super::{create_named_dir, write_to_file}; use crate::errors::FilesystemError; pub(crate) fn save_witness_to_dir>( - witnesses: WitnessMap, + witness_stack: WitnessStack, witness_name: &str, witness_dir: P, ) -> Result { create_named_dir(witness_dir.as_ref(), "witness"); let witness_path = witness_dir.as_ref().join(witness_name).with_extension(WITNESS_EXT); - // TODO(https://github.com/noir-lang/noir/issues/4428) - let witness_stack: WitnessStack = witnesses.into(); let buf: Vec = witness_stack.try_into()?; write_to_file(buf.as_slice(), &witness_path); diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index 272f2fa8e5d..b9e4bca9e69 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,4 +1,3 @@ -use acvm::acir::native_types::WitnessStack; use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::ops::{compile_program, report_errors}; @@ -123,12 +122,14 @@ pub(crate) fn prove_package( let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi)?; - let solved_witness = - execute_program(&compiled_program, &inputs_map, foreign_call_resolver_url)?; + let witness_stack = execute_program(&compiled_program, &inputs_map, foreign_call_resolver_url)?; // Write public inputs into Verifier.toml let public_abi = compiled_program.abi.public_abi(); - let (public_inputs, return_value) = public_abi.decode(&solved_witness)?; + // Get the entry point witness for the ABI + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + let (public_inputs, return_value) = public_abi.decode(main_witness)?; write_inputs_to_file( &public_inputs, @@ -139,7 +140,7 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.program, WitnessStack::from(solved_witness))?; + let proof = backend.prove(&compiled_program.program, witness_stack)?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index cffb0c20cd5..d0dcb373963 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -147,7 +147,9 @@ impl AbiType { Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { TypeBinding::Bound(typ) => Self::from_type(context, typ), - TypeBinding::Unbound(_) => Self::from_type(context, &Type::default_int_type()), + TypeBinding::Unbound(_) => { + Self::from_type(context, &Type::default_int_or_field_type()) + } }, Type::Bool => Self::Boolean, Type::String(size) => {