From cf5cc28ef6adbed355e69cc73e6e96804a679b3a Mon Sep 17 00:00:00 2001 From: AztecBot Date: Mon, 15 Jul 2024 08:02:10 +0000 Subject: [PATCH 1/2] [1 changes] feat: Allow comptime attributes on traits & functions (https://github.com/noir-lang/noir/pull/5496) feat: skip reading values immediately after it being written into an array (https://github.com/noir-lang/noir/pull/5449) fix: Don't type error when calling certain trait impls in the interpreter (https://github.com/noir-lang/noir/pull/5471) feat: LSP hover (https://github.com/noir-lang/noir/pull/5491) chore: update typo PR script (https://github.com/noir-lang/noir/pull/5488) feat: Handle ACIR calls in the debugger (https://github.com/noir-lang/noir/pull/5051) feat: Add unquote function (https://github.com/noir-lang/noir/pull/5497) feat: Allow arguments to attribute functions (https://github.com/noir-lang/noir/pull/5494) --- .noir-sync-commit | 2 +- .../acvm-repo/acir/src/circuit/mod.rs | 2 +- .../opcodes/black_box_function_call.rs | 2 +- .../acir/tests/test_program_serialization.rs | 2 +- .../compiler/optimizers/redundant_range.rs | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../acvm-repo/blackbox_solver/src/bigint.rs | 4 +- .../acvm-repo/brillig_vm/src/black_box.rs | 8 + noir/noir-repo/compiler/fm/Cargo.toml | 1 + noir/noir-repo/compiler/fm/src/lib.rs | 21 + .../compiler/noirc_driver/src/lib.rs | 11 +- .../noirc_driver/tests/stdlib_warnings.rs | 2 +- .../compiler/noirc_errors/src/reporter.rs | 49 +- .../brillig/brillig_gen/brillig_black_box.rs | 46 +- .../compiler/noirc_evaluator/src/errors.rs | 3 - .../compiler/noirc_evaluator/src/ssa.rs | 19 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 156 ++-- .../noirc_evaluator/src/ssa/ir/instruction.rs | 36 +- .../src/ssa/opt/constant_folding.rs | 128 +++- .../src/ssa/ssa_gen/context.rs | 11 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 2 +- .../compiler/noirc_frontend/src/ast/traits.rs | 2 + .../src/elaborator/expressions.rs | 126 +++- .../noirc_frontend/src/elaborator/mod.rs | 192 ++++- .../noirc_frontend/src/elaborator/patterns.rs | 44 +- .../noirc_frontend/src/elaborator/scope.rs | 20 +- .../src/elaborator/statements.rs | 21 +- .../noirc_frontend/src/elaborator/traits.rs | 16 +- .../noirc_frontend/src/elaborator/types.rs | 178 +++-- .../noirc_frontend/src/hir/comptime/errors.rs | 20 +- .../src/hir/comptime/hir_to_display_ast.rs | 428 +++++++++++ .../src/hir/comptime/interpreter.rs | 20 +- .../noirc_frontend/src/hir/comptime/mod.rs | 1 + .../noirc_frontend/src/hir/comptime/scan.rs | 28 +- .../noirc_frontend/src/hir/comptime/tests.rs | 10 +- .../noirc_frontend/src/hir/comptime/value.rs | 24 +- .../src/hir/def_collector/dc_crate.rs | 87 ++- .../src/hir/def_collector/dc_mod.rs | 33 +- .../noirc_frontend/src/hir/def_map/mod.rs | 2 + .../src/hir/resolution/import.rs | 21 +- .../src/hir/resolution/path_resolver.rs | 9 +- .../src/hir/resolution/resolver.rs | 4 +- .../src/hir/resolution/traits.rs | 3 +- .../src/hir/type_check/errors.rs | 3 + .../noirc_frontend/src/hir/type_check/expr.rs | 4 + .../noirc_frontend/src/hir/type_check/mod.rs | 3 +- .../noirc_frontend/src/hir_def/expr.rs | 4 + .../noirc_frontend/src/hir_def/function.rs | 5 +- .../noirc_frontend/src/hir_def/types.rs | 502 +++++++------ .../noirc_frontend/src/lexer/token.rs | 13 +- .../compiler/noirc_frontend/src/locations.rs | 137 +++- .../src/monomorphization/mod.rs | 72 +- .../noirc_frontend/src/node_interner.rs | 147 +++- .../src/parser/parser/traits.rs | 18 +- .../noirc_frontend/src/parser/parser/types.rs | 30 +- .../compiler/noirc_frontend/src/tests.rs | 3 +- noir/noir-repo/cspell.json | 2 + .../docs/docs/explainers/explainer-oracle.md | 4 +- .../docs/docs/how_to/how-to-oracles.md | 4 +- .../docs/docs/how_to/how-to-recursion.md | 2 +- .../docs/how_to/how-to-solidity-verifier.md | 6 +- .../src/collections/bounded_vec.nr | 47 +- .../noir_stdlib/src/embedded_curve_ops.nr | 28 +- noir/noir-repo/noir_stdlib/src/hash/keccak.nr | 122 ++++ noir/noir-repo/noir_stdlib/src/hash/mod.nr | 11 +- noir/noir-repo/noir_stdlib/src/meta/mod.nr | 7 + noir/noir-repo/scripts/bump-bb.sh | 11 + noir/noir-repo/scripts/install_bb.sh | 2 +- noir/noir-repo/scripts/redo-typo-pr.sh | 13 +- .../Nargo.toml | 7 + .../src/main.nr | 4 + .../attribute_args/Nargo.toml | 7 + .../attribute_args/src/main.nr | 20 + .../function_attribute/Nargo.toml | 7 + .../function_attribute/src/main.nr | 18 + .../regression_5428}/Nargo.toml | 2 +- .../regression_5428/src/main.nr | 9 + .../trait_attribute/Nargo.toml | 7 + .../trait_attribute/src/main.nr | 19 + .../compile_success_empty/unquote/Nargo.toml | 7 + .../compile_success_empty/unquote/src/main.nr | 4 + .../binary_operator_overloading/Nargo.toml | 7 + .../Prover.toml | 0 .../src/main.nr | 0 .../Nargo.toml | 2 +- .../unary_operator_overloading/Prover.toml | 1 + .../unary_operator_overloading/src/main.nr | 36 + .../verify_honk_proof/Prover.toml | 4 - .../verify_honk_proof/src/main.nr | 21 - noir/noir-repo/test_programs/gates_report.sh | 6 +- noir/noir-repo/tooling/debugger/Cargo.toml | 2 +- .../tooling/debugger/ignored-tests.txt | 14 +- .../noir-repo/tooling/debugger/src/context.rs | 680 ++++++++++++------ noir/noir-repo/tooling/debugger/src/dap.rs | 48 +- noir/noir-repo/tooling/debugger/src/lib.rs | 17 +- noir/noir-repo/tooling/debugger/src/repl.rs | 138 ++-- .../noir-repo/tooling/debugger/tests/debug.rs | 2 +- noir/noir-repo/tooling/lsp/src/lib.rs | 111 ++- .../tooling/lsp/src/notifications/mod.rs | 36 +- .../lsp/src/requests/code_lens_request.rs | 10 +- .../lsp/src/requests/goto_declaration.rs | 6 +- .../lsp/src/requests/goto_definition.rs | 13 +- .../tooling/lsp/src/requests/hover.rs | 641 +++++++++++++++++ .../noir-repo/tooling/lsp/src/requests/mod.rs | 120 +++- .../tooling/lsp/src/requests/references.rs | 94 ++- .../tooling/lsp/src/requests/rename.rs | 79 +- .../tooling/lsp/src/requests/test_run.rs | 2 +- .../tooling/lsp/src/requests/tests.rs | 2 +- noir/noir-repo/tooling/lsp/src/types.rs | 8 +- .../test_programs/rename_function/src/main.nr | 13 + .../test_programs/struct_member/Nargo.toml | 6 + .../test_programs/struct_member/src/main.nr | 12 + .../lsp/test_programs/workspace/Nargo.toml | 2 + .../test_programs/workspace/one/Nargo.toml | 4 + .../test_programs/workspace/one/src/lib.nr | 25 + .../test_programs/workspace/two/Nargo.toml | 7 + .../test_programs/workspace/two/src/lib.nr | 53 ++ .../test_programs/workspace/two/src/other.nr | 2 + noir/noir-repo/tooling/nargo_cli/build.rs | 7 +- .../tooling/nargo_cli/src/cli/check_cmd.rs | 11 +- .../tooling/nargo_cli/src/cli/debug_cmd.rs | 52 +- .../tooling/nargo_cli/src/cli/export_cmd.rs | 1 + .../tooling/nargo_cli/src/cli/test_cmd.rs | 2 + .../tooling/nargo_cli/tests/stdlib-tests.rs | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 +- 127 files changed, 4168 insertions(+), 1257 deletions(-) create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs create mode 100644 noir/noir-repo/noir_stdlib/src/hash/keccak.nr create mode 100755 noir/noir-repo/scripts/bump-bb.sh create mode 100644 noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/attribute_args/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/function_attribute/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr rename noir/noir-repo/test_programs/{execution_success/verify_honk_proof => compile_success_empty/regression_5428}/Nargo.toml (66%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/regression_5428/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/trait_attribute/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/trait_attribute/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/unquote/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/unquote/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_success/binary_operator_overloading/Nargo.toml rename noir/noir-repo/test_programs/execution_success/{operator_overloading => binary_operator_overloading}/Prover.toml (100%) rename noir/noir-repo/test_programs/execution_success/{operator_overloading => binary_operator_overloading}/src/main.nr (100%) rename noir/noir-repo/test_programs/execution_success/{operator_overloading => unary_operator_overloading}/Nargo.toml (70%) create mode 100644 noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/unary_operator_overloading/src/main.nr delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml delete mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/src/requests/hover.rs create mode 100644 noir/noir-repo/tooling/lsp/test_programs/struct_member/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/struct_member/src/main.nr create mode 100644 noir/noir-repo/tooling/lsp/test_programs/workspace/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/workspace/one/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/workspace/one/src/lib.nr create mode 100644 noir/noir-repo/tooling/lsp/test_programs/workspace/two/Nargo.toml create mode 100644 noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/lib.nr create mode 100644 noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/other.nr diff --git a/.noir-sync-commit b/.noir-sync-commit index 3222858403e..b4cd2314c3f 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -bb6913ac53620fabd73e24ca1a2b1369225903ec +b59a29e5b246121a4d81e4894a4b10f5df4dd5cf diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs index 115ecbf7992..5d749e709b3 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -360,7 +360,7 @@ mod tests { use std::collections::BTreeSet; use super::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, }; use crate::{ diff --git a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 7c560a0a346..6478f0c7a19 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -520,7 +520,7 @@ mod tests { use crate::{circuit::Opcode, native_types::Witness}; use acir_field::{AcirField, FieldElement}; - use super::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}; + use super::{BlackBoxFuncCall, FunctionInput}; fn keccakf1600_opcode() -> Opcode { let inputs: Box<[FunctionInput; 25]> = diff --git a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs index 3a42cc41d47..3047ac002e8 100644 --- a/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/noir-repo/acvm-repo/acir/tests/test_program_serialization.rs @@ -14,7 +14,7 @@ use std::collections::BTreeSet; use acir::{ circuit::{ brillig::{BrilligBytecode, BrilligInputs, BrilligOutputs}, - opcodes::{BlackBoxFuncCall, BlockId, ConstantOrWitnessEnum, FunctionInput, MemOp}, + opcodes::{BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 87a026148d7..b03b6715abe 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -150,7 +150,7 @@ mod tests { use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ circuit::{ - opcodes::{BlackBoxFuncCall, ConstantOrWitnessEnum, FunctionInput}, + opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, ExpressionWidth, Opcode, PublicInputs, }, native_types::{Expression, Witness}, diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index c07d2d8a4c1..16fb26e55db 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs index 5b19f03a238..b8bc9dc0d70 100644 --- a/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs +++ b/noir/noir-repo/acvm-repo/blackbox_solver/src/bigint.rs @@ -18,7 +18,7 @@ pub struct BigIntSolver { } impl BigIntSolver { - pub(crate) fn get_bigint( + pub fn get_bigint( &self, id: u32, func: BlackBoxFunc, @@ -32,7 +32,7 @@ impl BigIntSolver { .cloned() } - pub(crate) fn get_modulus( + pub fn get_modulus( &self, id: u32, func: BlackBoxFunc, diff --git a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs index 53599f79bc7..d37258036fc 100644 --- a/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/noir-repo/acvm-repo/brillig_vm/src/black_box.rs @@ -421,6 +421,14 @@ impl BrilligBigintSolver { rhs: u32, func: BlackBoxFunc, ) -> Result { + let modulus_lhs = self.bigint_solver.get_modulus(lhs, func)?; + let modulus_rhs = self.bigint_solver.get_modulus(rhs, func)?; + if modulus_lhs != modulus_rhs { + return Err(BlackBoxResolutionError::Failed( + func, + "moduli should be identical in BigInt operation".to_string(), + )); + } let id = self.create_bigint_id(); self.bigint_solver.bigint_op(lhs, rhs, id, func)?; Ok(id) diff --git a/noir/noir-repo/compiler/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index 1a356d93d89..b48f445be36 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true [dependencies] codespan-reporting.workspace = true +iter-extended.workspace = true serde.workspace = true [dev-dependencies] diff --git a/noir/noir-repo/compiler/fm/src/lib.rs b/noir/noir-repo/compiler/fm/src/lib.rs index 2e52d802479..37da29fc982 100644 --- a/noir/noir-repo/compiler/fm/src/lib.rs +++ b/noir/noir-repo/compiler/fm/src/lib.rs @@ -7,6 +7,7 @@ mod file_map; pub use file_map::{File, FileId, FileMap, PathString}; +use iter_extended::vecmap; // Re-export for the lsp pub use codespan_reporting::files as codespan_files; @@ -103,6 +104,26 @@ impl FileManager { pub fn name_to_id(&self, file_name: PathBuf) -> Option { self.file_map.get_file_id(&PathString::from_path(file_name)) } + + /// Find a file by its path suffix, e.g. "src/main.nr" is a suffix of + /// "some_dir/package_name/src/main.nr"` + pub fn find_by_path_suffix(&self, suffix: &str) -> Result, Vec> { + let suffix_path: Vec<_> = Path::new(suffix).components().rev().collect(); + let results: Vec<_> = self + .path_to_id + .iter() + .filter(|(path, _id)| { + path.components().rev().zip(suffix_path.iter()).all(|(x, y)| &x == y) + }) + .collect(); + if results.is_empty() { + Ok(None) + } else if results.len() == 1 { + Ok(Some(*results[0].1)) + } else { + Err(vecmap(results, |(path, _id)| path.clone())) + } + } } pub trait NormalizePath { diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index 4ba9b85f967..2b0769e30d4 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -103,6 +103,11 @@ pub struct CompileOptions { #[arg(long, hide = true)] pub use_legacy: bool, + /// Enable printing results of comptime evaluation: provide a path suffix + /// for the module to debug, e.g. "package_name/src/main.nr" + #[arg(long)] + pub debug_comptime_in_file: Option, + /// Outputs the paths to any modified artifacts #[arg(long, hide = true)] pub show_artifact_paths: bool, @@ -258,12 +263,14 @@ pub fn check_crate( deny_warnings: bool, disable_macros: bool, use_legacy: bool, + debug_comptime_in_file: Option<&str>, ) -> CompilationResult<()> { let macros: &[&dyn MacroProcessor] = if disable_macros { &[] } else { &[&aztec_macros::AztecMacro as &dyn MacroProcessor] }; let mut errors = vec![]; - let diagnostics = CrateDefMap::collect_defs(crate_id, context, use_legacy, macros); + let diagnostics = + CrateDefMap::collect_defs(crate_id, context, use_legacy, debug_comptime_in_file, macros); errors.extend(diagnostics.into_iter().map(|(error, file_id)| { let diagnostic = CustomDiagnostic::from(&error); diagnostic.in_file(file_id) @@ -301,6 +308,7 @@ pub fn compile_main( options.deny_warnings, options.disable_macros, options.use_legacy, + options.debug_comptime_in_file.as_deref(), )?; let main = context.get_main_function(&crate_id).ok_or_else(|| { @@ -342,6 +350,7 @@ pub fn compile_contract( options.deny_warnings, options.disable_macros, options.use_legacy, + options.debug_comptime_in_file.as_deref(), )?; // TODO: We probably want to error if contracts is empty diff --git a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs index 47ce893d202..0e098d0d087 100644 --- a/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs +++ b/noir/noir-repo/compiler/noirc_driver/tests/stdlib_warnings.rs @@ -25,7 +25,7 @@ fn stdlib_does_not_produce_constant_warnings() -> Result<(), ErrorsAndWarnings> let root_crate_id = prepare_crate(&mut context, file_name); let ((), warnings) = - noirc_driver::check_crate(&mut context, root_crate_id, false, false, false)?; + noirc_driver::check_crate(&mut context, root_crate_id, false, false, false, None)?; assert_eq!(warnings, Vec::new(), "stdlib is producing {} warnings", warnings.len()); diff --git a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs index ea7c97b1f25..3ce0f268715 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/reporter.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/reporter.rs @@ -19,6 +19,7 @@ pub enum DiagnosticKind { Error, Bug, Warning, + Info, } /// A count of errors that have been already reported to stderr @@ -37,30 +38,57 @@ impl CustomDiagnostic { } } - pub fn simple_error( + fn simple_with_kind( primary_message: String, secondary_message: String, secondary_span: Span, + kind: DiagnosticKind, ) -> CustomDiagnostic { CustomDiagnostic { message: primary_message, secondaries: vec![CustomLabel::new(secondary_message, secondary_span)], notes: Vec::new(), - kind: DiagnosticKind::Error, + kind, } } + pub fn simple_error( + primary_message: String, + secondary_message: String, + secondary_span: Span, + ) -> CustomDiagnostic { + Self::simple_with_kind( + primary_message, + secondary_message, + secondary_span, + DiagnosticKind::Error, + ) + } + pub fn simple_warning( primary_message: String, secondary_message: String, secondary_span: Span, ) -> CustomDiagnostic { - CustomDiagnostic { - message: primary_message, - secondaries: vec![CustomLabel::new(secondary_message, secondary_span)], - notes: Vec::new(), - kind: DiagnosticKind::Warning, - } + Self::simple_with_kind( + primary_message, + secondary_message, + secondary_span, + DiagnosticKind::Warning, + ) + } + + pub fn simple_info( + primary_message: String, + secondary_message: String, + secondary_span: Span, + ) -> CustomDiagnostic { + Self::simple_with_kind( + primary_message, + secondary_message, + secondary_span, + DiagnosticKind::Info, + ) } pub fn simple_bug( @@ -96,6 +124,10 @@ impl CustomDiagnostic { matches!(self.kind, DiagnosticKind::Warning) } + pub fn is_info(&self) -> bool { + matches!(self.kind, DiagnosticKind::Info) + } + pub fn is_bug(&self) -> bool { matches!(self.kind, DiagnosticKind::Bug) } @@ -191,6 +223,7 @@ fn convert_diagnostic( ) -> Diagnostic { let diagnostic = match (cd.kind, deny_warnings) { (DiagnosticKind::Warning, false) => Diagnostic::warning(), + (DiagnosticKind::Info, _) => Diagnostic::note(), (DiagnosticKind::Bug, ..) => Diagnostic::bug(), _ => Diagnostic::error(), }; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index aa9cb8cd7a3..8e2b2fb7a29 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -6,7 +6,7 @@ use acvm::{ use crate::brillig::brillig_ir::{ brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugToString, - BrilligBinaryOp, BrilligContext, + BrilligContext, }; /// Transforms SSA's black box function calls into the corresponding brillig instructions @@ -239,11 +239,10 @@ pub(crate) fn convert_black_box_call( BlackBoxFunc::RecursiveAggregation => {} BlackBoxFunc::BigIntAdd => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(rhs_modulus)], - [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(modulus_id)], + [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { - prepare_bigint_output(brillig_context, lhs_modulus, rhs_modulus, modulus_id); brillig_context.black_box_op_instruction(BlackBoxOp::BigIntAdd { lhs: lhs.address, rhs: rhs.address, @@ -257,11 +256,10 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::BigIntSub => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(rhs_modulus)], - [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(modulus_id)], + [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { - prepare_bigint_output(brillig_context, lhs_modulus, rhs_modulus, modulus_id); brillig_context.black_box_op_instruction(BlackBoxOp::BigIntSub { lhs: lhs.address, rhs: rhs.address, @@ -275,11 +273,10 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::BigIntMul => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(rhs_modulus)], - [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(modulus_id)], + [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { - prepare_bigint_output(brillig_context, lhs_modulus, rhs_modulus, modulus_id); brillig_context.black_box_op_instruction(BlackBoxOp::BigIntMul { lhs: lhs.address, rhs: rhs.address, @@ -293,11 +290,10 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::BigIntDiv => { if let ( - [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(rhs_modulus)], - [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(modulus_id)], + [BrilligVariable::SingleAddr(lhs), BrilligVariable::SingleAddr(_lhs_modulus), BrilligVariable::SingleAddr(rhs), BrilligVariable::SingleAddr(_rhs_modulus)], + [BrilligVariable::SingleAddr(output), BrilligVariable::SingleAddr(_modulus_id)], ) = (function_arguments, function_results) { - prepare_bigint_output(brillig_context, lhs_modulus, rhs_modulus, modulus_id); brillig_context.black_box_op_instruction(BlackBoxOp::BigIntDiv { lhs: lhs.address, rhs: rhs.address, @@ -416,27 +412,3 @@ fn convert_array_or_vector( ), } } - -fn prepare_bigint_output( - brillig_context: &mut BrilligContext, - lhs_modulus: &SingleAddrVariable, - rhs_modulus: &SingleAddrVariable, - modulus_id: &SingleAddrVariable, -) { - // Check moduli - let condition = brillig_context.allocate_register(); - let condition_adr = SingleAddrVariable { address: condition, bit_size: 1 }; - brillig_context.binary_instruction( - *lhs_modulus, - *rhs_modulus, - condition_adr, - BrilligBinaryOp::Equals, - ); - brillig_context.codegen_constrain( - condition_adr, - Some("moduli should be identical in BigInt operation".to_string()), - ); - brillig_context.deallocate_register(condition); - - brillig_context.mov_instruction(modulus_id.address, lhs_modulus.address); -} diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs index 2b328898257..2c7ec0f8e1a 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/errors.rs @@ -19,8 +19,6 @@ use serde::{Deserialize, Serialize}; pub enum RuntimeError { #[error(transparent)] InternalError(#[from] InternalError), - #[error("Index out of bounds, array has size {array_size}, but index was {index}")] - IndexOutOfBounds { index: usize, array_size: usize, call_stack: CallStack }, #[error("Range constraint of {num_bits} bits is too large for the Field size")] InvalidRangeConstraint { num_bits: u32, call_stack: CallStack }, #[error("The value `{value:?}` cannot fit into `{typ}` which has range `{range}`")] @@ -145,7 +143,6 @@ impl RuntimeError { | InternalError::UndeclaredAcirVar { call_stack } | InternalError::Unexpected { call_stack, .. }, ) - | RuntimeError::IndexOutOfBounds { call_stack, .. } | RuntimeError::InvalidRangeConstraint { call_stack, .. } | RuntimeError::TypeConversion { call_stack, .. } | RuntimeError::UnInitialized { call_stack, .. } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 820374df9c1..81327cec013 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -190,12 +190,19 @@ pub fn create_program( let recursive = program.recursive; let ArtifactsAndWarnings((generated_acirs, generated_brillig, error_types), ssa_level_warnings) = optimize_into_acir(program, options)?; - assert_eq!( - generated_acirs.len(), - func_sigs.len(), - "The generated ACIRs should match the supplied function signatures" - ); - + if options.force_brillig_output { + assert_eq!( + generated_acirs.len(), + 1, + "Only the main ACIR is expected when forcing Brillig output" + ); + } else { + assert_eq!( + generated_acirs.len(), + func_sigs.len(), + "The generated ACIRs should match the supplied function signatures" + ); + } let mut program_artifact = SsaProgramArtifact::new(generated_brillig, error_types); // Add warnings collected at the Ssa stage diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index cfcc7a9a997..1bdc9aaf4eb 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -975,6 +975,8 @@ impl<'a> Context<'a> { .into()) } }; + // Ensure that array id is fully resolved. + let array = dfg.resolve(array); let array_id = dfg.resolve(array); let array_typ = dfg.type_of_value(array_id); @@ -992,7 +994,6 @@ impl<'a> Context<'a> { // If we find one, we will use it when computing the index under the enable_side_effect predicate // If not, array_get(..) will use a fallback costing one multiplication in the worst case. // cf. https://github.com/noir-lang/noir/pull/4971 - // For simplicity we compute the offset only for simple arrays let is_simple_array = dfg.instruction_results(instruction).len() == 1 && can_omit_element_sizes_array(&array_typ); @@ -1085,45 +1086,30 @@ impl<'a> Context<'a> { } }; - let side_effects_always_enabled = - self.acir_context.is_constant_one(&self.current_side_effects_enabled_var); - let index_out_of_bounds = index >= array_size; - - // Note that the value of `side_effects_always_enabled` doesn't affect the value which we return here for valid - // indices, just whether we return an error for invalid indices at compile time or defer until execution. - match (side_effects_always_enabled, index_out_of_bounds) { - (true, false) => { - let value = match store_value { - Some(store_value) => AcirValue::Array(array.update(index, store_value)), - None => array[index].clone(), - }; + if index >= array_size { + return Ok(false); + } + + if let Some(store_value) = store_value { + let side_effects_always_enabled = + self.acir_context.is_constant_one(&self.current_side_effects_enabled_var); + if side_effects_always_enabled { + // If we know that this write will always occur then we can perform it at compile time. + let value = AcirValue::Array(array.update(index, store_value)); self.define_result(dfg, instruction, value); Ok(true) + } else { + // If a predicate is applied however we must wait until runtime. + Ok(false) } - (false, false) => { - if store_value.is_none() { - // If there is a predicate and the index is not out of range, we can optimistically perform the - // read at compile time as if the predicate is true. - // - // This is as if the predicate is false, any side-effects will be disabled so the value returned - // will not affect the rest of execution. - self.define_result(dfg, instruction, array[index].clone()); - Ok(true) - } else { - // We do not do this for a array writes however. - Ok(false) - } - } - - // Report the error if side effects are enabled. - (true, true) => { - let call_stack = self.acir_context.get_call_stack(); - Err(RuntimeError::IndexOutOfBounds { index, array_size, call_stack }) - } - // Index is out of bounds but predicate may result in this array operation being skipped - // so we don't return an error now. - (false, true) => Ok(false), + } else { + // If the index is not out of range, we can optimistically perform the read at compile time + // as if the predicate were true. This is as if the predicate were to resolve to false then + // the result should not affect the rest of circuit execution. + let value = array[index].clone(); + self.define_result(dfg, instruction, value); + Ok(true) } } @@ -1138,13 +1124,14 @@ impl<'a> Context<'a> { /// It is a dummy value because in the case of a false predicate, the value stored at the requested index will be itself. fn convert_array_operation_inputs( &mut self, - array: ValueId, + array_id: ValueId, dfg: &DataFlowGraph, index: ValueId, store_value: Option, offset: usize, ) -> Result<(AcirVar, Option), RuntimeError> { - let (array_id, array_typ, block_id) = self.check_array_is_initialized(array, dfg)?; + let array_typ = dfg.type_of_value(array_id); + let block_id = self.ensure_array_is_initialized(array_id, dfg)?; let index_var = self.convert_numeric_value(index, dfg)?; let index_var = self.get_flattened_index(&array_typ, array_id, index_var, dfg)?; @@ -1263,22 +1250,22 @@ impl<'a> Context<'a> { dfg: &DataFlowGraph, mut index_side_effect: bool, ) -> Result { - let (array_id, _, block_id) = self.check_array_is_initialized(array, dfg)?; + let block_id = self.ensure_array_is_initialized(array, dfg)?; let results = dfg.instruction_results(instruction); let res_typ = dfg.type_of_value(results[0]); // Get operations to call-data parameters are replaced by a get to the call-data-bus array if let Some(call_data) = self.data_bus.call_data { - if self.data_bus.call_data_map.contains_key(&array_id) { + if self.data_bus.call_data_map.contains_key(&array) { // TODO: the block_id of call-data must be notified to the backend // TODO: should we do the same for return-data? let type_size = res_typ.flattened_size(); let type_size = self.acir_context.add_constant(FieldElement::from(type_size as i128)); let offset = self.acir_context.mul_var(var_index, type_size)?; - let bus_index = self.acir_context.add_constant(FieldElement::from( - self.data_bus.call_data_map[&array_id] as i128, - )); + let bus_index = self + .acir_context + .add_constant(FieldElement::from(self.data_bus.call_data_map[&array] as i128)); let new_index = self.acir_context.add_var(offset, bus_index)?; return self.array_get(instruction, call_data, new_index, dfg, index_side_effect); } @@ -1292,8 +1279,7 @@ impl<'a> Context<'a> { let mut value = self.array_get_value(&res_typ, block_id, &mut var_index)?; if let AcirValue::Var(value_var, typ) = &value { - let array_id = dfg.resolve(array_id); - let array_typ = dfg.type_of_value(array_id); + let array_typ = dfg.type_of_value(array); if let (Type::Numeric(numeric_type), AcirType::NumericType(num)) = (array_typ.first(), typ) { @@ -1377,7 +1363,7 @@ impl<'a> Context<'a> { } }; - let (array_id, array_typ, block_id) = self.check_array_is_initialized(array, dfg)?; + let block_id = self.ensure_array_is_initialized(array, dfg)?; // Every array has a length in its type, so we fetch that from // the SSA IR. @@ -1386,10 +1372,11 @@ impl<'a> Context<'a> { // However, this size is simply the capacity of a slice. The capacity is dependent upon the witness // and may contain data for which we want to restrict access. The true slice length is tracked in a // a separate SSA value and restrictions on slice indices should be generated elsewhere in the SSA. + let array_typ = dfg.type_of_value(array); let array_len = if !array_typ.contains_slice_element() { array_typ.flattened_size() } else { - self.flattened_slice_size(array_id, dfg) + self.flattened_slice_size(array, dfg) }; // Since array_set creates a new array, we create a new block ID for this @@ -1412,18 +1399,13 @@ impl<'a> Context<'a> { self.array_set_value(&store_value, result_block_id, &mut var_index)?; let element_type_sizes = if !can_omit_element_sizes_array(&array_typ) { - let acir_value = self.convert_value(array_id, dfg); - Some(self.init_element_type_sizes_array( - &array_typ, - array_id, - Some(&acir_value), - dfg, - )?) + let acir_value = self.convert_value(array, dfg); + Some(self.init_element_type_sizes_array(&array_typ, array, Some(&acir_value), dfg)?) } else { None }; - let value_types = self.convert_value(array_id, dfg).flat_numeric_types(); + let value_types = self.convert_value(array, dfg).flat_numeric_types(); // Compiler sanity check assert_eq!(value_types.len(), array_len, "ICE: The length of the flattened type array should match the length of the dynamic array"); @@ -1469,37 +1451,33 @@ impl<'a> Context<'a> { Ok(()) } - fn check_array_is_initialized( + fn ensure_array_is_initialized( &mut self, array: ValueId, dfg: &DataFlowGraph, - ) -> Result<(ValueId, Type, BlockId), RuntimeError> { - // Fetch the internal SSA ID for the array - let array_id = dfg.resolve(array); - - let array_typ = dfg.type_of_value(array_id); - + ) -> Result { // Use the SSA ID to get or create its block ID - let block_id = self.block_id(&array_id); + let block_id = self.block_id(&array); // Check if the array has already been initialized in ACIR gen // if not, we initialize it using the values from SSA let already_initialized = self.initialized_arrays.contains(&block_id); if !already_initialized { - let value = &dfg[array_id]; + let value = &dfg[array]; match value { Value::Array { .. } | Value::Instruction { .. } => { - let value = self.convert_value(array_id, dfg); + let value = self.convert_value(array, dfg); + let array_typ = dfg.type_of_value(array); let len = if !array_typ.contains_slice_element() { array_typ.flattened_size() } else { - self.flattened_slice_size(array_id, dfg) + self.flattened_slice_size(array, dfg) }; self.initialize_array(block_id, len, Some(value))?; } _ => { return Err(InternalError::General { - message: format!("Array {array_id} should be initialized"), + message: format!("Array {array} should be initialized"), call_stack: self.acir_context.get_call_stack(), } .into()); @@ -1507,7 +1485,7 @@ impl<'a> Context<'a> { } } - Ok((array_id, array_typ, block_id)) + Ok(block_id) } fn init_element_type_sizes_array( @@ -1761,7 +1739,7 @@ impl<'a> Context<'a> { /// Converts an SSA terminator's return values into their ACIR representations fn get_num_return_witnesses( - &mut self, + &self, terminator: &TerminatorInstruction, dfg: &DataFlowGraph, ) -> usize { @@ -1815,7 +1793,7 @@ impl<'a> Context<'a> { has_constant_return |= self.acir_context.is_constant(&acir_var); if is_databus { // We do not return value for the data bus. - self.check_array_is_initialized( + self.ensure_array_is_initialized( self.data_bus.return_data.expect( "`is_databus == true` implies `data_bus.return_data` is `Some`", ), @@ -2182,8 +2160,9 @@ impl<'a> Context<'a> { Ok(vec![AcirValue::Var(self.acir_context.add_constant(len), AcirType::field())]) } Intrinsic::AsSlice => { - let (slice_contents, slice_typ, block_id) = - self.check_array_is_initialized(arguments[0], dfg)?; + let slice_contents = arguments[0]; + let slice_typ = dfg.type_of_value(slice_contents); + let block_id = self.ensure_array_is_initialized(slice_contents, dfg)?; assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let result_block_id = self.block_id(&result_ids[1]); @@ -2227,8 +2206,9 @@ impl<'a> Context<'a> { Intrinsic::SlicePushBack => { // arguments = [slice_length, slice_contents, ...elements_to_push] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - let (slice_contents, slice_typ, _) = - self.check_array_is_initialized(arguments[1], dfg)?; + let slice_contents = arguments[1]; + let slice_typ = dfg.type_of_value(slice_contents); + assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let slice = self.convert_value(slice_contents, dfg); @@ -2294,9 +2274,8 @@ impl<'a> Context<'a> { Intrinsic::SlicePushFront => { // arguments = [slice_length, slice_contents, ...elements_to_push] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; - - let (slice_contents, slice_typ, _) = - self.check_array_is_initialized(arguments[1], dfg)?; + let slice_contents = arguments[1]; + let slice_typ = dfg.type_of_value(slice_contents); assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let slice: AcirValue = self.convert_value(slice_contents, dfg); @@ -2359,6 +2338,7 @@ impl<'a> Context<'a> { Intrinsic::SlicePopBack => { // arguments = [slice_length, slice_contents] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; + let slice_contents = arguments[1]; let one = self.acir_context.add_constant(FieldElement::one()); let new_slice_length = self.acir_context.sub_var(slice_length, one)?; @@ -2367,8 +2347,8 @@ impl<'a> Context<'a> { // the elements stored at that index will no longer be able to be accessed. let mut var_index = new_slice_length; - let (slice_contents, slice_typ, block_id) = - self.check_array_is_initialized(arguments[1], dfg)?; + let slice_typ = dfg.type_of_value(slice_contents); + let block_id = self.ensure_array_is_initialized(slice_contents, dfg)?; assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let mut popped_elements = Vec::new(); @@ -2393,9 +2373,11 @@ impl<'a> Context<'a> { Intrinsic::SlicePopFront => { // arguments = [slice_length, slice_contents] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; + let slice_contents = arguments[1]; + + let slice_typ = dfg.type_of_value(slice_contents); + let block_id = self.ensure_array_is_initialized(slice_contents, dfg)?; - let (slice_contents, slice_typ, block_id) = - self.check_array_is_initialized(arguments[1], dfg)?; assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let one = self.acir_context.add_constant(FieldElement::one()); @@ -2434,9 +2416,11 @@ impl<'a> Context<'a> { Intrinsic::SliceInsert => { // arguments = [slice_length, slice_contents, insert_index, ...elements_to_insert] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; + let slice_contents = arguments[1]; + + let slice_typ = dfg.type_of_value(slice_contents); + let block_id = self.ensure_array_is_initialized(slice_contents, dfg)?; - let (slice_contents, slice_typ, block_id) = - self.check_array_is_initialized(arguments[1], dfg)?; assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let slice = self.convert_value(slice_contents, dfg); @@ -2573,9 +2557,11 @@ impl<'a> Context<'a> { Intrinsic::SliceRemove => { // arguments = [slice_length, slice_contents, remove_index] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; + let slice_contents = arguments[1]; + + let slice_typ = dfg.type_of_value(slice_contents); + let block_id = self.ensure_array_is_initialized(slice_contents, dfg)?; - let (slice_contents, slice_typ, block_id) = - self.check_array_is_initialized(arguments[1], dfg)?; assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); let slice = self.convert_value(slice_contents, dfg); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index b08283a9ceb..9146fe94832 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -288,25 +288,33 @@ impl Instruction { } /// Indicates if the instruction can be safely replaced with the results of another instruction with the same inputs. - pub(crate) fn can_be_deduplicated(&self, dfg: &DataFlowGraph) -> bool { + /// If `deduplicate_with_predicate` is set, we assume we're deduplicating with the instruction + /// and its predicate, rather than just the instruction. Setting this means instructions that + /// rely on predicates can be deduplicated as well. + pub(crate) fn can_be_deduplicated( + &self, + dfg: &DataFlowGraph, + deduplicate_with_predicate: bool, + ) -> bool { use Instruction::*; match self { // These either have side-effects or interact with memory - Constrain(..) - | EnableSideEffects { .. } + EnableSideEffects { .. } | Allocate | Load { .. } | Store { .. } | IncrementRc { .. } - | DecrementRc { .. } - | RangeCheck { .. } => false, + | DecrementRc { .. } => false, Call { func, .. } => match dfg[*func] { Value::Intrinsic(intrinsic) => !intrinsic.has_side_effects(), _ => false, }, + // We can deduplicate these instructions if we know the predicate is also the same. + Constrain(..) | RangeCheck { .. } => deduplicate_with_predicate, + // These can have different behavior depending on the EnableSideEffectsIf context. // Replacing them with a similar instruction potentially enables replacing an instruction // with one that was disabled. See @@ -317,7 +325,9 @@ impl Instruction { | Truncate { .. } | IfElse { .. } | ArrayGet { .. } - | ArraySet { .. } => !self.requires_acir_gen_predicate(dfg), + | ArraySet { .. } => { + deduplicate_with_predicate || !self.requires_acir_gen_predicate(dfg) + } } } @@ -372,7 +382,7 @@ impl Instruction { } /// If true the instruction will depends on enable_side_effects context during acir-gen - fn requires_acir_gen_predicate(&self, dfg: &DataFlowGraph) -> bool { + pub(crate) fn requires_acir_gen_predicate(&self, dfg: &DataFlowGraph) -> bool { match self { Instruction::Binary(binary) if matches!(binary.operator, BinaryOp::Div | BinaryOp::Mod) => @@ -592,6 +602,18 @@ impl Instruction { } } Instruction::ArrayGet { array, index } => { + if let Value::Instruction { instruction, .. } = &dfg[*array] { + if let Instruction::ArraySet { index: write_index, value, .. } = + dfg[*instruction] + { + // If we're reading from an index of the array which we just wrote to, we can return + // the value which we wrote without performing the read. + if dfg.resolve(write_index) == dfg.resolve(*index) { + return SimplifiedTo(value); + } + } + } + let array = dfg.get_array_constant(*array); let index = dfg.get_numeric_constant(*index); if let (Some((array, _)), Some(index)) = (array, index) { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index d7cd366e9af..160105d27e6 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -87,12 +87,16 @@ struct Context { block_queue: Vec, } +/// HashMap from (Instruction, side_effects_enabled_var) to the results of the instruction. +/// Stored as a two-level map to avoid cloning Instructions during the `.get` call. +type InstructionResultCache = HashMap, Vec>>; + impl Context { fn fold_constants_in_block(&mut self, function: &mut Function, block: BasicBlockId) { let instructions = function.dfg[block].take_instructions(); // Cache of instructions without any side-effects along with their outputs. - let mut cached_instruction_results: HashMap> = HashMap::default(); + let mut cached_instruction_results = HashMap::default(); // Contains sets of values which are constrained to be equivalent to each other. // @@ -124,7 +128,7 @@ impl Context { dfg: &mut DataFlowGraph, block: BasicBlockId, id: InstructionId, - instruction_result_cache: &mut HashMap>, + instruction_result_cache: &mut InstructionResultCache, constraint_simplification_mappings: &mut HashMap>, side_effects_enabled_var: &mut ValueId, ) { @@ -134,7 +138,9 @@ impl Context { let old_results = dfg.instruction_results(id).to_vec(); // If a copy of this instruction exists earlier in the block, then reuse the previous results. - if let Some(cached_results) = instruction_result_cache.get(&instruction) { + if let Some(cached_results) = + Self::get_cached(dfg, instruction_result_cache, &instruction, *side_effects_enabled_var) + { Self::replace_result_ids(dfg, &old_results, cached_results); return; } @@ -150,6 +156,7 @@ impl Context { dfg, instruction_result_cache, constraint_simplification_mapping, + *side_effects_enabled_var, ); // If we just inserted an `Instruction::EnableSideEffects`, we need to update `side_effects_enabled_var` @@ -224,8 +231,9 @@ impl Context { instruction: Instruction, instruction_results: Vec, dfg: &DataFlowGraph, - instruction_result_cache: &mut HashMap>, + instruction_result_cache: &mut InstructionResultCache, constraint_simplification_mapping: &mut HashMap, + side_effects_enabled_var: ValueId, ) { if self.use_constraint_info { // If the instruction was a constraint, then create a link between the two `ValueId`s @@ -258,8 +266,15 @@ impl Context { // If the instruction doesn't have side-effects and if it won't interact with enable_side_effects during acir_gen, // we cache the results so we can reuse them if the same instruction appears again later in the block. - if instruction.can_be_deduplicated(dfg) { - instruction_result_cache.insert(instruction, instruction_results); + if instruction.can_be_deduplicated(dfg, self.use_constraint_info) { + let use_predicate = + self.use_constraint_info && instruction.requires_acir_gen_predicate(dfg); + let predicate = use_predicate.then_some(side_effects_enabled_var); + + instruction_result_cache + .entry(instruction) + .or_default() + .insert(predicate, instruction_results); } } @@ -273,6 +288,25 @@ impl Context { dfg.set_value_from_id(*old_result, *new_result); } } + + fn get_cached<'a>( + dfg: &DataFlowGraph, + instruction_result_cache: &'a mut InstructionResultCache, + instruction: &Instruction, + side_effects_enabled_var: ValueId, + ) -> Option<&'a Vec> { + let results_for_instruction = instruction_result_cache.get(instruction); + + // See if there's a cached version with no predicate first + if let Some(results) = results_for_instruction.and_then(|map| map.get(&None)) { + return Some(results); + } + + let predicate = + instruction.requires_acir_gen_predicate(dfg).then_some(side_effects_enabled_var); + + results_for_instruction.and_then(|map| map.get(&predicate)) + } } #[cfg(test)] @@ -725,4 +759,86 @@ mod test { let ending_instruction_count = instructions.len(); assert_eq!(starting_instruction_count, ending_instruction_count); } + + #[test] + fn deduplicate_instructions_with_predicates() { + // fn main f0 { + // b0(v0: bool, v1: bool, v2: [u32; 2]): + // enable_side_effects v0 + // v3 = array_get v2, index u32 0 + // v4 = array_set v2, index u32 1, value: u32 2 + // v5 = array_get v4, index u32 0 + // constrain_eq v3, v5 + // enable_side_effects v1 + // v6 = array_get v2, index u32 0 + // v7 = array_set v2, index u32 1, value: u32 2 + // v8 = array_get v7, index u32 0 + // constrain_eq v6, v8 + // enable_side_effects v0 + // v9 = array_get v2, index u32 0 + // v10 = array_set v2, index u32 1, value: u32 2 + // v11 = array_get v10, index u32 0 + // constrain_eq v9, v11 + // } + let main_id = Id::test_new(0); + + // Compiling main + 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::Array(Rc::new(vec![Type::field()]), 2)); + + let zero = builder.numeric_constant(0u128, Type::length_type()); + let one = builder.numeric_constant(1u128, Type::length_type()); + let two = builder.numeric_constant(2u128, Type::length_type()); + + builder.insert_enable_side_effects_if(v0); + let v3 = builder.insert_array_get(v2, zero, Type::length_type()); + let v4 = builder.insert_array_set(v2, one, two); + let v5 = builder.insert_array_get(v4, zero, Type::length_type()); + builder.insert_constrain(v3, v5, None); + + builder.insert_enable_side_effects_if(v1); + let v6 = builder.insert_array_get(v2, zero, Type::length_type()); + let v7 = builder.insert_array_set(v2, one, two); + let v8 = builder.insert_array_get(v7, zero, Type::length_type()); + builder.insert_constrain(v6, v8, None); + + // We expect all these instructions after the 'enable side effects' instruction to be removed. + builder.insert_enable_side_effects_if(v0); + let v9 = builder.insert_array_get(v2, zero, Type::length_type()); + let v10 = builder.insert_array_set(v2, one, two); + let v11 = builder.insert_array_get(v10, zero, Type::length_type()); + builder.insert_constrain(v9, v11, None); + + let ssa = builder.finish(); + println!("{ssa}"); + + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + assert_eq!(instructions.len(), 15); + + // Expected output: + // + // fn main f0 { + // b0(v0: bool, v1: bool, v2: [Field; 2]): + // enable_side_effects v0 + // v3 = array_get v2, index Field 0 + // v4 = array_set v2, index Field 1, value: Field 2 + // v5 = array_get v4, index Field 0 + // constrain_eq v3, v5 + // enable_side_effects v1 + // v7 = array_set v2, index Field 1, value: Field 2 + // v8 = array_get v7, index Field 0 + // constrain_eq v3, v8 + // enable_side_effects v0 + // } + let ssa = ssa.fold_constants_using_constraints(); + println!("{ssa}"); + + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + assert_eq!(instructions.len(), 10); + } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index e013546f14a..8e55debec1d 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -5,7 +5,7 @@ use acvm::{acir::AcirField, FieldElement}; use iter_extended::vecmap; use noirc_errors::Location; use noirc_frontend::ast::{BinaryOpKind, Signedness}; -use noirc_frontend::monomorphization::ast::{self, LocalId, Parameters}; +use noirc_frontend::monomorphization::ast::{self, InlineType, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; use crate::errors::RuntimeError; @@ -121,9 +121,14 @@ impl<'a> FunctionContext<'a> { /// /// Note that the previous function cannot be resumed after calling this. Developers should /// avoid calling new_function until the previous function is completely finished with ssa-gen. - pub(super) fn new_function(&mut self, id: IrFunctionId, func: &ast::Function) { + pub(super) fn new_function( + &mut self, + id: IrFunctionId, + func: &ast::Function, + force_brillig_runtime: bool, + ) { self.definitions.clear(); - if func.unconstrained { + if func.unconstrained || (force_brillig_runtime && func.inline_type != InlineType::Inline) { self.builder.new_brillig_function(func.name.clone(), id); } else { self.builder.new_function(func.name.clone(), id, func.inline_type); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index afe44881830..abd251b008f 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -111,7 +111,7 @@ pub(crate) fn generate_ssa( // to generate SSA for each function used within the program. while let Some((src_function_id, dest_id)) = context.pop_next_function_in_queue() { let function = &context.program[src_function_id]; - function_context.new_function(dest_id, function); + function_context.new_function(dest_id, function, force_brillig_runtime); function_context.codegen_function_body(&function.body)?; } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs index b1b14e3f657..064ea8ba9d9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -7,6 +7,7 @@ use crate::ast::{ BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, Path, UnresolvedGenerics, UnresolvedType, }; +use crate::macros_api::SecondaryAttribute; use crate::node_interner::TraitId; /// AST node for trait definitions: @@ -18,6 +19,7 @@ pub struct NoirTrait { pub where_clause: Vec, pub span: Span, pub items: Vec, + pub attributes: Vec, } /// Any declaration inside the body of a trait that a user is required to diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs index ad79af4e63d..72e2beea570 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -9,25 +9,25 @@ use crate::{ UnresolvedTypeExpression, }, hir::{ - comptime::{self, Interpreter, InterpreterError}, + comptime::{self, InterpreterError}, resolution::{errors::ResolverError, resolver::LambdaContext}, type_check::TypeCheckError, }, hir_def::{ expr::{ HirArrayLiteral, HirBinaryOp, HirBlockExpression, HirCallExpression, HirCastExpression, - HirConstructorExpression, HirIfExpression, HirIndexExpression, HirInfixExpression, - HirLambda, HirMemberAccess, HirMethodCallExpression, HirMethodReference, - HirPrefixExpression, + HirConstructorExpression, HirExpression, HirIfExpression, HirIndexExpression, + HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, + HirMethodReference, HirPrefixExpression, }, traits::TraitConstraint, }, macros_api::{ - BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirExpression, - HirLiteral, HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, + BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirLiteral, + HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, MethodCallExpression, PrefixExpression, }, - node_interner::{DefinitionKind, ExprId, FuncId, ReferenceId}, + node_interner::{DefinitionKind, ExprId, FuncId, TraitMethodId}, token::Tokens, QuotedType, Shared, StructType, Type, }; @@ -39,7 +39,7 @@ impl<'context> Elaborator<'context> { let (hir_expr, typ) = match expr.kind { ExpressionKind::Literal(literal) => self.elaborate_literal(literal, expr.span), ExpressionKind::Block(block) => self.elaborate_block(block), - ExpressionKind::Prefix(prefix) => self.elaborate_prefix(*prefix), + ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix), ExpressionKind::Index(index) => self.elaborate_index(*index), ExpressionKind::Call(call) => self.elaborate_call(*call, expr.span), ExpressionKind::MethodCall(call) => self.elaborate_method_call(*call, expr.span), @@ -227,11 +227,22 @@ impl<'context> Elaborator<'context> { (HirExpression::Literal(HirLiteral::FmtStr(str, fmt_str_idents)), typ) } - fn elaborate_prefix(&mut self, prefix: PrefixExpression) -> (HirExpression, Type) { + fn elaborate_prefix(&mut self, prefix: PrefixExpression) -> (ExprId, Type) { let span = prefix.rhs.span; let (rhs, rhs_type) = self.elaborate_expression(prefix.rhs); - let ret_type = self.type_check_prefix_operand(&prefix.operator, &rhs_type, span); - (HirExpression::Prefix(HirPrefixExpression { operator: prefix.operator, rhs }), ret_type) + let trait_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); + + let operator = prefix.operator; + let expr = + HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id: trait_id }); + let expr_id = self.interner.push_expr(expr); + self.interner.push_expr_location(expr_id, span, self.file); + + let result = self.prefix_operand_type_rules(&operator, &rhs_type, span); + let typ = self.handle_operand_type_rules_result(result, &rhs_type, trait_id, expr_id, span); + + self.interner.push_expr_type(expr_id, typ.clone()); + (expr_id, typ) } fn elaborate_index(&mut self, index_expr: IndexExpression) -> (HirExpression, Type) { @@ -309,6 +320,7 @@ impl<'context> Elaborator<'context> { let (mut object, mut object_type) = self.elaborate_expression(method_call.object); object_type = object_type.follow_bindings(); + let method_name_span = method_call.method_name.span(); let method_name = method_call.method_name.0.contents.as_str(); match self.lookup_method(&object_type, method_name, span) { Some(method_ref) => { @@ -374,6 +386,9 @@ impl<'context> Elaborator<'context> { self.interner.push_expr_type(function_id, func_type.clone()); + self.interner + .add_function_reference(func_id, Location::new(method_name_span, self.file)); + // Type check the new call now that it has been changed from a method call // to a function call. This way we avoid duplicating code. let typ = self.type_check_call(&function_call, func_type, function_args, span); @@ -388,7 +403,8 @@ impl<'context> Elaborator<'context> { constructor: ConstructorExpression, ) -> (HirExpression, Type) { let span = constructor.type_name.span(); - let is_self_type = constructor.type_name.last_segment().is_self_type_name(); + let last_segment = constructor.type_name.last_segment(); + let is_self_type = last_segment.is_self_type_name(); let (r#type, struct_generics) = if let Some(struct_id) = constructor.struct_type { let typ = self.interner.get_struct(struct_id); @@ -418,9 +434,9 @@ impl<'context> Elaborator<'context> { struct_generics, }); - let referenced = ReferenceId::Struct(struct_type.borrow().id); - let reference = ReferenceId::Reference(Location::new(span, self.file), is_self_type); - self.interner.add_reference(referenced, reference); + let struct_id = struct_type.borrow().id; + let reference_location = Location::new(last_segment.span(), self.file); + self.interner.add_struct_reference(struct_id, reference_location, is_self_type); (expr, Type::Struct(struct_type, generics)) } @@ -440,8 +456,13 @@ impl<'context> Elaborator<'context> { let mut unseen_fields = struct_type.borrow().field_names(); for (field_name, field) in fields { - let expected_type = field_types.iter().find(|(name, _)| name == &field_name.0.contents); - let expected_type = expected_type.map(|(_, typ)| typ).unwrap_or(&Type::Error); + let expected_field_with_index = field_types + .iter() + .enumerate() + .find(|(_, (name, _))| name == &field_name.0.contents); + let expected_index = expected_field_with_index.map(|(index, _)| index); + let expected_type = + expected_field_with_index.map(|(_, (_, typ))| typ).unwrap_or(&Type::Error); let field_span = field.span; let (resolved, field_type) = self.elaborate_expression(field); @@ -468,6 +489,14 @@ impl<'context> Elaborator<'context> { }); } + if let Some(expected_index) = expected_index { + self.interner.add_struct_member_reference( + struct_type.borrow().id, + expected_index, + Location::new(field_name.span(), self.file), + ); + } + ret.push((field_name, resolved)); } @@ -489,10 +518,11 @@ impl<'context> Elaborator<'context> { ) -> (ExprId, Type) { let (lhs, lhs_type) = self.elaborate_expression(access.lhs); let rhs = access.rhs; + let rhs_span = rhs.span(); // `is_offset` is only used when lhs is a reference and we want to return a reference to rhs let access = HirMemberAccess { lhs, rhs, is_offset: false }; let expr_id = self.intern_expr(HirExpression::MemberAccess(access.clone()), span); - let typ = self.type_check_member_access(access, expr_id, lhs_type, span); + let typ = self.type_check_member_access(access, expr_id, lhs_type, rhs_span); self.interner.push_expr_type(expr_id, typ.clone()); (expr_id, typ) } @@ -527,19 +557,38 @@ impl<'context> Elaborator<'context> { let expr_id = self.interner.push_expr(expr); self.interner.push_expr_location(expr_id, span, self.file); - let typ = match self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, span) { + let result = self.infix_operand_type_rules(&lhs_type, &operator, &rhs_type, span); + let typ = + self.handle_operand_type_rules_result(result, &lhs_type, Some(trait_id), expr_id, span); + + self.interner.push_expr_type(expr_id, typ.clone()); + (expr_id, typ) + } + + fn handle_operand_type_rules_result( + &mut self, + result: Result<(Type, bool), TypeCheckError>, + operand_type: &Type, + trait_id: Option, + expr_id: ExprId, + span: Span, + ) -> Type { + match result { Ok((typ, use_impl)) => { if use_impl { + let trait_id = + trait_id.expect("ice: expected some trait_id when use_impl is true"); + // 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 = TraitConstraint { - typ: lhs_type.clone(), + typ: operand_type.clone(), trait_id: trait_id.trait_id, trait_generics: Vec::new(), }; self.push_trait_constraint(constraint, expr_id); - self.type_check_operator_method(expr_id, trait_id, &lhs_type, span); + self.type_check_operator_method(expr_id, trait_id, operand_type, span); } typ } @@ -547,10 +596,7 @@ impl<'context> Elaborator<'context> { self.push_err(error); Type::Error } - }; - - self.interner.push_expr_type(expr_id, typ.clone()); - (expr_id, typ) + } } fn elaborate_if(&mut self, if_expr: IfExpression) -> (HirExpression, Type) { @@ -660,12 +706,20 @@ impl<'context> Elaborator<'context> { // call is not yet solved for. self.function_context.push(Default::default()); let (block, _typ) = self.elaborate_block_expression(block); - self.check_and_pop_function_context(); - let mut interpreter = - Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id); + self.check_and_pop_function_context(); + let mut interpreter_errors = vec![]; + let mut interpreter = self.setup_interpreter(&mut interpreter_errors); let value = interpreter.evaluate_block(block); - self.inline_comptime_value(value, span) + self.include_interpreter_errors(interpreter_errors); + let (id, typ) = self.inline_comptime_value(value, span); + + let location = self.interner.id_location(id); + self.debug_comptime(location, |interner| { + interner.expression(&id).to_display_ast(interner, location.span).kind + }); + + (id, typ) } pub(super) fn inline_comptime_value( @@ -736,9 +790,9 @@ impl<'context> Elaborator<'context> { } }; - let mut interpreter = - Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id); - + let file = self.file; + let mut interpreter_errors = vec![]; + let mut interpreter = self.setup_interpreter(&mut interpreter_errors); let mut comptime_args = Vec::new(); let mut errors = Vec::new(); @@ -748,17 +802,19 @@ impl<'context> Elaborator<'context> { let location = interpreter.interner.expr_location(&argument); comptime_args.push((arg, location)); } - Err(error) => errors.push((error.into(), self.file)), + Err(error) => errors.push((error.into(), file)), } } + let bindings = interpreter.interner.get_instantiation_bindings(func).clone(); + let result = interpreter.call_function(function, comptime_args, bindings, location); + self.include_interpreter_errors(interpreter_errors); + if !errors.is_empty() { self.errors.append(&mut errors); return None; } - let bindings = interpreter.interner.get_instantiation_bindings(func).clone(); - let result = interpreter.call_function(function, comptime_args, bindings, location); let (expr_id, typ) = self.inline_comptime_value(result, location.span); Some((self.interner.expression(&expr_id), typ)) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index 6104da582a7..b0fd8f79557 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1,5 +1,6 @@ use std::{ collections::{BTreeMap, BTreeSet}, + fmt::Display, rc::Rc, }; @@ -25,6 +26,7 @@ use crate::{ traits::TraitConstraint, types::{Generics, Kind, ResolvedGeneric}, }, + lexer::Lexer, macros_api::{ BlockExpression, Ident, NodeInterner, NoirFunction, NoirStruct, Pattern, SecondaryAttribute, StructId, @@ -34,6 +36,7 @@ use crate::{ TypeAliasId, }, parser::TopLevelStatement, + token::Tokens, Shared, Type, TypeBindings, TypeVariable, }; use crate::{ @@ -157,6 +160,9 @@ pub struct Elaborator<'context> { /// up all currently visible definitions. The first scope is always the global scope. comptime_scopes: Vec>, + /// The scope of --debug-comptime, or None if unset + debug_comptime_in_file: Option, + /// These are the globals that have yet to be elaborated. /// This map is used to lazily evaluate these globals if they're encountered before /// they are elaborated (e.g. in a function's type or another global's RHS). @@ -178,7 +184,11 @@ struct FunctionContext { } impl<'context> Elaborator<'context> { - pub fn new(context: &'context mut Context, crate_id: CrateId) -> Self { + pub fn new( + context: &'context mut Context, + crate_id: CrateId, + debug_comptime_in_file: Option, + ) -> Self { Self { scopes: ScopeForest::default(), errors: Vec::new(), @@ -198,6 +208,7 @@ impl<'context> Elaborator<'context> { function_context: vec![FunctionContext::default()], current_trait_impl: None, comptime_scopes: vec![HashMap::default()], + debug_comptime_in_file, unresolved_globals: BTreeMap::new(), } } @@ -206,8 +217,9 @@ impl<'context> Elaborator<'context> { context: &'context mut Context, crate_id: CrateId, items: CollectedItems, + debug_comptime_in_file: Option, ) -> Vec<(CompilationError, FileId)> { - let mut this = Self::new(context, crate_id); + let mut this = Self::new(context, crate_id, debug_comptime_in_file); // Filter out comptime items to execute their functions first if needed. // This step is why comptime items can only refer to other comptime items @@ -240,11 +252,11 @@ impl<'context> Elaborator<'context> { } // Must resolve structs before we resolve globals. - let generated_items = self.collect_struct_definitions(items.types); + let mut generated_items = self.collect_struct_definitions(items.types); self.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); - self.collect_traits(items.traits); + self.collect_traits(items.traits, &mut generated_items); // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be @@ -266,6 +278,10 @@ impl<'context> Elaborator<'context> { self.elaborate_global(global); } + // We have to run any comptime attributes on functions before the function is elaborated + // since the generated items are checked beforehand as well. + self.run_attributes_on_functions(&items.functions, &mut generated_items); + // After everything is collected, we can elaborate our generated items. // It may be better to inline these within `items` entirely since elaborating them // all here means any globals will not see these. Inlining them completely within `items` @@ -708,6 +724,12 @@ impl<'context> Elaborator<'context> { let statements = std::mem::take(&mut func.def.body.statements); let body = BlockExpression { statements }; + let struct_id = if let Some(Type::Struct(struct_type, _)) = &self.self_type { + Some(struct_type.borrow().id) + } else { + None + }; + let meta = FuncMeta { name: name_ident, kind: func.kind, @@ -715,6 +737,7 @@ impl<'context> Elaborator<'context> { typ, direct_generics, all_generics: self.generics.clone(), + struct_id, trait_impl: self.current_trait_impl, parameters: parameters.into(), parameter_idents, @@ -1191,6 +1214,7 @@ impl<'context> Elaborator<'context> { let span = typ.struct_def.span; let fields = self.resolve_struct_fields(typ.struct_def, type_id); + let fields_len = fields.len(); self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); @@ -1217,7 +1241,13 @@ impl<'context> Elaborator<'context> { } }); - self.run_comptime_attributes_on_struct(attributes, type_id, span, &mut generated_items); + for field_index in 0..fields_len { + self.interner + .add_definition_location(ReferenceId::StructMember(type_id, field_index), None); + } + + let item = Value::StructDefinition(type_id); + self.run_comptime_attributes_on_item(&attributes, item, span, &mut generated_items); } // Check whether the struct fields have nested slices @@ -1243,17 +1273,17 @@ impl<'context> Elaborator<'context> { generated_items } - fn run_comptime_attributes_on_struct( + fn run_comptime_attributes_on_item( &mut self, - attributes: Vec, - struct_id: StructId, + attributes: &[SecondaryAttribute], + item: Value, span: Span, generated_items: &mut CollectedItems, ) { for attribute in attributes { if let SecondaryAttribute::Custom(name) = attribute { if let Err(error) = - self.run_comptime_attribute_on_struct(name, struct_id, span, generated_items) + self.run_comptime_attribute_on_item(name, item.clone(), span, generated_items) { self.errors.push(error); } @@ -1261,30 +1291,36 @@ impl<'context> Elaborator<'context> { } } - fn run_comptime_attribute_on_struct( + fn run_comptime_attribute_on_item( &mut self, - attribute: String, - struct_id: StructId, + attribute: &str, + item: Value, span: Span, generated_items: &mut CollectedItems, ) -> Result<(), (CompilationError, FileId)> { + let location = Location::new(span, self.file); + let (function_name, mut arguments) = Self::parse_attribute(attribute, location) + .unwrap_or_else(|| (attribute.to_string(), Vec::new())); + let id = self - .lookup_global(Path::from_single(attribute, span)) + .lookup_global(Path::from_single(function_name, span)) .map_err(|_| (ResolverError::UnknownAnnotation { span }.into(), self.file))?; let definition = self.interner.definition(id); let DefinitionKind::Function(function) = definition.kind else { return Err((ResolverError::NonFunctionInAnnotation { span }.into(), self.file)); }; - let mut interpreter = - Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id); - let location = Location::new(span, self.file); - let arguments = vec![(Value::StructDefinition(struct_id), location)]; + self.handle_varargs_attribute(function, &mut arguments, location); + arguments.insert(0, (item, location)); + + let mut interpreter_errors = vec![]; + let mut interpreter = self.setup_interpreter(&mut interpreter_errors); let value = interpreter .call_function(function, arguments, TypeBindings::new(), location) .map_err(|error| error.into_compilation_error_pair())?; + self.include_interpreter_errors(interpreter_errors); if value != Value::Unit { let items = value @@ -1297,6 +1333,59 @@ impl<'context> Elaborator<'context> { Ok(()) } + /// Parses an attribute in the form of a function call (e.g. `#[foo(a b, c d)]`) into + /// the function and quoted arguments called (e.g. `("foo", vec![(a b, location), (c d, location)])`) + fn parse_attribute( + annotation: &str, + location: Location, + ) -> Option<(String, Vec<(Value, Location)>)> { + let (tokens, errors) = Lexer::lex(annotation); + if !errors.is_empty() { + return None; + } + + let mut tokens = tokens.0; + if tokens.len() >= 4 { + // Remove the outer `ident ( )` wrapping the function arguments + let first = tokens.remove(0).into_token(); + let second = tokens.remove(0).into_token(); + + // Last token is always an EndOfInput + let _ = tokens.pop().unwrap().into_token(); + let last = tokens.pop().unwrap().into_token(); + + use crate::lexer::token::Token::*; + if let (Ident(name), LeftParen, RightParen) = (first, second, last) { + let args = tokens.split(|token| *token.token() == Comma); + let args = + vecmap(args, |arg| (Value::Code(Rc::new(Tokens(arg.to_vec()))), location)); + return Some((name, args)); + } + } + + None + } + + /// Checks if the given attribute function is a varargs function. + /// If so, we should pass its arguments in one slice rather than as separate arguments. + fn handle_varargs_attribute( + &mut self, + function: FuncId, + arguments: &mut Vec<(Value, Location)>, + location: Location, + ) { + let meta = self.interner.function_meta(&function); + let parameters = &meta.parameters.0; + + // If the last parameter is a slice, this is a varargs function. + if parameters.last().map_or(false, |(_, typ, _)| matches!(typ, Type::Slice(_))) { + let typ = Type::Slice(Box::new(Type::Quoted(crate::QuotedType::Quoted))); + let slice_elements = arguments.drain(..).map(|(value, _)| value); + let slice = Value::Slice(slice_elements.collect(), typ); + arguments.push((slice, location)); + } + } + pub fn resolve_struct_fields( &mut self, unresolved: NoirStruct, @@ -1349,7 +1438,8 @@ impl<'context> Elaborator<'context> { self.elaborate_comptime_global(global_id); } - self.interner.add_definition_location(ReferenceId::Global(global_id)); + self.interner + .add_definition_location(ReferenceId::Global(global_id), Some(self.module_id())); self.local_module = old_module; self.file = old_file; @@ -1365,9 +1455,8 @@ impl<'context> Elaborator<'context> { let global = self.interner.get_global(global_id); let definition_id = global.definition_id; let location = global.location; - - let mut interpreter = - Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id); + let mut interpreter_errors = vec![]; + let mut interpreter = self.setup_interpreter(&mut interpreter_errors); if let Err(error) = interpreter.evaluate_let(let_statement) { self.errors.push(error.into_compilation_error_pair()); @@ -1376,8 +1465,13 @@ impl<'context> Elaborator<'context> { .lookup_id(definition_id, location) .expect("The global should be defined since evaluate_let did not error"); + self.debug_comptime(location, |interner| { + interner.get_global(global_id).let_statement.to_display_ast(interner).kind + }); + self.interner.get_global_mut(global_id).value = Some(value); } + self.include_interpreter_errors(interpreter_errors); } fn define_function_metas( @@ -1451,13 +1545,11 @@ impl<'context> Elaborator<'context> { if let Some(trait_id) = trait_id { let trait_name = trait_impl.trait_path.last_segment(); - - let referenced = ReferenceId::Trait(trait_id); - let reference = ReferenceId::Reference( + self.interner.add_trait_reference( + trait_id, Location::new(trait_name.span(), trait_impl.file_id), trait_name.is_self_type_name(), ); - self.interner.add_reference(referenced, reference); } } } @@ -1473,6 +1565,10 @@ impl<'context> Elaborator<'context> { } } + fn include_interpreter_errors(&mut self, errors: Vec) { + self.errors.extend(errors.into_iter().map(InterpreterError::into_compilation_error_pair)); + } + /// True if we're currently within a `comptime` block, function, or global fn in_comptime_context(&self) -> bool { // The first context is the global context, followed by the function-specific context. @@ -1623,4 +1719,50 @@ impl<'context> Elaborator<'context> { } } } + + fn setup_interpreter<'a>( + &'a mut self, + interpreter_errors: &'a mut Vec, + ) -> Interpreter { + Interpreter::new( + self.interner, + &mut self.comptime_scopes, + self.crate_id, + self.debug_comptime_in_file, + interpreter_errors, + ) + } + + fn debug_comptime T>( + &mut self, + location: Location, + mut expr_f: F, + ) { + if Some(location.file) == self.debug_comptime_in_file { + let displayed_expr = expr_f(self.interner); + self.errors.push(( + InterpreterError::debug_evaluate_comptime(displayed_expr, location).into(), + location.file, + )); + } + } + + fn run_attributes_on_functions( + &mut self, + function_sets: &[UnresolvedFunctions], + generated_items: &mut CollectedItems, + ) { + for function_set in function_sets { + self.file = function_set.file_id; + self.self_type = function_set.self_type.clone(); + + for (local_module, function_id, function) in &function_set.functions { + self.local_module = *local_module; + let attributes = function.secondary_attributes(); + let item = Value::FunctionDefinition(*function_id); + let span = function.span(); + self.run_comptime_attributes_on_item(attributes, item, span, generated_items); + } + } + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs index b1c9b1b37cc..d9576c77666 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -15,9 +15,7 @@ use crate::{ stmt::HirPattern, }, macros_api::{HirExpression, Ident, Path, Pattern}, - node_interner::{ - DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, ReferenceId, TraitImplKind, - }, + node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, Shared, StructType, Type, TypeBindings, }; @@ -202,9 +200,15 @@ impl<'context> Elaborator<'context> { new_definitions, ); - let referenced = ReferenceId::Struct(struct_type.borrow().id); - let reference = ReferenceId::Reference(Location::new(name_span, self.file), is_self_type); - self.interner.add_reference(referenced, reference); + let struct_id = struct_type.borrow().id; + + let reference_location = Location::new(name_span, self.file); + self.interner.add_struct_reference(struct_id, reference_location, is_self_type); + + for (field_index, field) in fields.iter().enumerate() { + let reference_location = Location::new(field.0.span(), self.file); + self.interner.add_struct_member_reference(struct_id, field_index, reference_location); + } HirPattern::Struct(expected_type, fields, location) } @@ -456,9 +460,16 @@ impl<'context> Elaborator<'context> { // Comptime variables must be replaced with their values if let Some(definition) = self.interner.try_definition(definition_id) { if definition.comptime && !self.in_comptime_context() { - let mut interpreter = - Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id); + let mut interpreter_errors = vec![]; + let mut interpreter = Interpreter::new( + self.interner, + &mut self.comptime_scopes, + self.crate_id, + self.debug_comptime_in_file, + &mut interpreter_errors, + ); let value = interpreter.evaluate(id); + self.include_interpreter_errors(interpreter_errors); return self.inline_comptime_value(value, span); } } @@ -479,7 +490,6 @@ impl<'context> Elaborator<'context> { // This lookup allows support of such statements: let x = foo::bar::SOME_GLOBAL + 10; // If the expression is a singular indent, we search the resolver's current scope as normal. let span = path.span(); - let is_self_type_name = path.last_segment().is_self_type_name(); let (hir_ident, var_scope_index) = self.get_ident_from_path(path); if hir_ident.id != DefinitionId::dummy_id() { @@ -489,10 +499,7 @@ impl<'context> Elaborator<'context> { self.interner.add_function_dependency(current_item, func_id); } - let variable = - ReferenceId::Reference(hir_ident.location, is_self_type_name); - let function = ReferenceId::Function(func_id); - self.interner.add_reference(function, variable); + self.interner.add_function_reference(func_id, hir_ident.location); } DefinitionKind::Global(global_id) => { if let Some(global) = self.unresolved_globals.remove(&global_id) { @@ -502,10 +509,7 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, global_id); } - let variable = - ReferenceId::Reference(hir_ident.location, is_self_type_name); - let global = ReferenceId::Global(global_id); - self.interner.add_reference(global, variable); + self.interner.add_global_reference(global_id, hir_ident.location); } DefinitionKind::GenericType(_) => { // Initialize numeric generics to a polymorphic integer type in case @@ -521,10 +525,8 @@ impl<'context> Elaborator<'context> { // only local variables can be captured by closures. self.resolve_local_variable(hir_ident.clone(), var_scope_index); - let referenced = ReferenceId::Local(hir_ident.id); - let reference = - ReferenceId::Reference(Location::new(span, self.file), false); - self.interner.add_reference(referenced, reference); + let reference_location = Location::new(span, self.file); + self.interner.add_local_reference(hir_ident.id, reference_location); } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs index b7016280453..e01c7e997d4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -6,7 +6,6 @@ use crate::hir::resolution::path_resolver::{PathResolver, StandardPathResolver}; use crate::hir::resolution::resolver::SELF_TYPE_NAME; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; use crate::macros_api::Ident; -use crate::node_interner::ReferenceId; use crate::{ hir::{ def_map::{ModuleDefId, TryFromModuleDefId}, @@ -48,17 +47,30 @@ impl<'context> Elaborator<'context> { let path_resolution; if self.interner.track_references { - let mut references: Vec = Vec::new(); + let last_segment = path.last_segment(); + let location = Location::new(last_segment.span(), self.file); + let is_self_type_name = last_segment.is_self_type_name(); + + let mut references: Vec<_> = Vec::new(); path_resolution = resolver.resolve(self.def_maps, path.clone(), &mut Some(&mut references))?; for (referenced, ident) in references.iter().zip(path.segments) { - let reference = ReferenceId::Reference( + let Some(referenced) = referenced else { + continue; + }; + self.interner.add_reference( + *referenced, Location::new(ident.span(), self.file), ident.is_self_type_name(), ); - self.interner.add_reference(*referenced, reference); } + + self.interner.add_module_def_id_reference( + path_resolution.module_def_id, + location, + is_self_type_name, + ); } else { path_resolution = resolver.resolve(self.def_maps, path, &mut None)?; } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs index 1fc92ad28ba..ba3dcccca99 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/statements.rs @@ -3,7 +3,6 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{AssignStatement, ConstrainStatement, LValue}, hir::{ - comptime::Interpreter, resolution::errors::ResolverError, type_check::{Source, TypeCheckError}, }, @@ -16,7 +15,7 @@ use crate::{ macros_api::{ ForLoopStatement, ForRange, HirStatement, LetStatement, Path, Statement, StatementKind, }, - node_interner::{DefinitionId, DefinitionKind, GlobalId, ReferenceId, StmtId}, + node_interner::{DefinitionId, DefinitionKind, GlobalId, StmtId}, Type, }; @@ -256,9 +255,8 @@ impl<'context> Elaborator<'context> { typ.follow_bindings() }; - let referenced = ReferenceId::Local(ident.id); - let reference = ReferenceId::Reference(Location::new(span, self.file), false); - self.interner.add_reference(referenced, reference); + let reference_location = Location::new(span, self.file); + self.interner.add_local_reference(ident.id, reference_location); (HirLValue::Ident(ident.clone(), typ.clone()), typ, mutable) } @@ -381,6 +379,9 @@ impl<'context> Elaborator<'context> { Type::Struct(s, args) => { let s = s.borrow(); if let Some((field, index)) = s.get_field(field_name, args) { + let reference_location = Location::new(span, self.file); + self.interner.add_struct_member_reference(s.id, index, reference_location); + return Some((field, index)); } } @@ -444,11 +445,15 @@ impl<'context> Elaborator<'context> { let span = statement.span; let (hir_statement, _typ) = self.elaborate_statement(statement); self.check_and_pop_function_context(); - - let mut interpreter = - Interpreter::new(self.interner, &mut self.comptime_scopes, self.crate_id); + let mut interpreter_errors = vec![]; + let mut interpreter = self.setup_interpreter(&mut interpreter_errors); let value = interpreter.evaluate_statement(hir_statement); let (expr, typ) = self.inline_comptime_value(value, span); + self.include_interpreter_errors(interpreter_errors); + + let location = self.interner.id_location(hir_statement); + self.debug_comptime(location, |interner| expr.to_display_ast(interner).kind); + (HirStatement::Expression(expr), typ) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index 77ac8e476f8..0627121f836 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -7,7 +7,7 @@ use crate::{ ast::{ FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, }, - hir::def_collector::dc_crate::UnresolvedTrait, + hir::def_collector::dc_crate::{CollectedItems, UnresolvedTrait}, hir_def::traits::{TraitConstant, TraitFunction, TraitType}, macros_api::{ BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, @@ -21,7 +21,11 @@ use crate::{ use super::Elaborator; impl<'context> Elaborator<'context> { - pub fn collect_traits(&mut self, traits: BTreeMap) { + pub fn collect_traits( + &mut self, + traits: BTreeMap, + generated_items: &mut CollectedItems, + ) { for (trait_id, unresolved_trait) in traits { self.recover_generics(|this| { let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); @@ -41,13 +45,19 @@ impl<'context> Elaborator<'context> { this.interner.update_trait(trait_id, |trait_def| { trait_def.set_methods(methods); }); + + let attributes = &unresolved_trait.trait_def.attributes; + let item = crate::hir::comptime::Value::TraitDefinition(trait_id); + let span = unresolved_trait.trait_def.span; + this.run_comptime_attributes_on_item(attributes, item, span, generated_items); }); // This check needs to be after the trait's methods are set since // the interner may set `interner.ordering_type` based on the result type // of the Cmp trait, if this is it. if self.crate_id.is_stdlib() { - self.interner.try_add_operator_trait(trait_id); + self.interner.try_add_infix_operator_trait(trait_id); + self.interner.try_add_prefix_operator_trait(trait_id); } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs index 698114cfb5e..35ff421ed32 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -31,8 +31,7 @@ use crate::{ UnaryOp, UnresolvedType, UnresolvedTypeData, }, node_interner::{ - DefinitionKind, DependencyId, ExprId, GlobalId, ReferenceId, TraitId, TraitImplKind, - TraitMethodId, + DefinitionKind, DependencyId, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId, }, Generics, Kind, ResolvedGeneric, Type, TypeBinding, TypeVariable, TypeVariableKind, }; @@ -58,12 +57,16 @@ impl<'context> Elaborator<'context> { use crate::ast::UnresolvedTypeData::*; let span = typ.span; - let (is_self_type_name, is_synthetic) = if let Named(ref named_path, _, synthetic) = typ.typ - { - (named_path.last_segment().is_self_type_name(), synthetic) - } else { - (false, false) - }; + let (named_path_span, is_self_type_name, is_synthetic) = + if let Named(ref named_path, _, synthetic) = typ.typ { + ( + Some(named_path.last_segment().span()), + named_path.last_segment().is_self_type_name(), + synthetic, + ) + } else { + (None, false, false) + }; let resolved_type = match typ.typ { FieldElement => Type::FieldElement, @@ -154,30 +157,23 @@ impl<'context> Elaborator<'context> { }; if let Some(unresolved_span) = typ.span { + let location = Location::new(named_path_span.unwrap_or(unresolved_span), self.file); + match resolved_type { Type::Struct(ref struct_type, _) => { // Record the location of the type reference - self.interner.push_type_ref_location( - resolved_type.clone(), - Location::new(unresolved_span, self.file), - ); + self.interner.push_type_ref_location(resolved_type.clone(), location); if !is_synthetic { - let referenced = ReferenceId::Struct(struct_type.borrow().id); - let reference = ReferenceId::Reference( - Location::new(unresolved_span, self.file), + self.interner.add_struct_reference( + struct_type.borrow().id, + location, is_self_type_name, ); - self.interner.add_reference(referenced, reference); } } Type::Alias(ref alias_type, _) => { - let referenced = ReferenceId::Alias(alias_type.borrow().id); - let reference = ReferenceId::Reference( - Location::new(unresolved_span, self.file), - is_self_type_name, - ); - self.interner.add_reference(referenced, reference); + self.interner.add_alias_reference(alias_type.borrow().id, location); } _ => (), } @@ -369,10 +365,8 @@ impl<'context> Elaborator<'context> { self.interner.add_global_dependency(current_item, id); } - let referenced = ReferenceId::Global(id); - let reference = - ReferenceId::Reference(Location::new(path.span(), self.file), false); - self.interner.add_reference(referenced, reference); + let reference_location = Location::new(path.span(), self.file); + self.interner.add_global_reference(id, reference_location); Some(Type::Constant(self.eval_global_as_array_length(id, path))) } @@ -670,57 +664,6 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn type_check_prefix_operand( - &mut self, - op: &crate::ast::UnaryOp, - rhs_type: &Type, - span: Span, - ) -> Type { - let unify = |this: &mut Self, expected| { - this.unify(rhs_type, &expected, || TypeCheckError::TypeMismatch { - expr_typ: rhs_type.to_string(), - expected_typ: expected.to_string(), - expr_span: span, - }); - expected - }; - - match op { - crate::ast::UnaryOp::Minus => { - if rhs_type.is_unsigned() { - self.push_err(TypeCheckError::InvalidUnaryOp { - kind: rhs_type.to_string(), - span, - }); - } - let expected = self.polymorphic_integer_or_field(); - self.unify(rhs_type, &expected, || TypeCheckError::InvalidUnaryOp { - kind: rhs_type.to_string(), - span, - }); - expected - } - crate::ast::UnaryOp::Not => { - let rhs_type = rhs_type.follow_bindings(); - - // `!` can work on booleans or integers - if matches!(rhs_type, Type::Integer(..)) { - return rhs_type; - } - - unify(self, Type::Bool) - } - crate::ast::UnaryOp::MutableReference => { - Type::MutableReference(Box::new(rhs_type.follow_bindings())) - } - crate::ast::UnaryOp::Dereference { implicitly_added: _ } => { - let element_type = self.interner.next_type_variable(); - unify(self, Type::MutableReference(Box::new(element_type.clone()))); - element_type - } - } - } - /// Insert as many dereference operations as necessary to automatically dereference a method /// call object to its base value type T. pub(super) fn insert_auto_dereferences(&mut self, object: ExprId, typ: Type) -> (ExprId, Type) { @@ -730,6 +673,7 @@ impl<'context> Elaborator<'context> { let object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::Dereference { implicitly_added: true }, rhs: object, + trait_method_id: None, })); self.interner.push_expr_type(object, element.as_ref().clone()); self.interner.push_expr_location(object, location.span, location.file); @@ -1073,6 +1017,84 @@ impl<'context> Elaborator<'context> { } } + // Given a unary operator and a type, this method will produce the output type + // and a boolean indicating whether to use the trait impl corresponding to the operator + // or not. A value of false indicates the caller to use a primitive operation for this + // operator, while a true value indicates a user-provided trait impl is required. + pub(super) fn prefix_operand_type_rules( + &mut self, + op: &UnaryOp, + rhs_type: &Type, + span: Span, + ) -> Result<(Type, bool), TypeCheckError> { + use Type::*; + + match op { + crate::ast::UnaryOp::Minus | crate::ast::UnaryOp::Not => { + match rhs_type { + // An error type will always return an error + Error => Ok((Error, false)), + Alias(alias, args) => { + let alias = alias.borrow().get_type(args); + self.prefix_operand_type_rules(op, &alias, span) + } + + // Matches on TypeVariable must be first so that we follow any type + // bindings. + TypeVariable(int, _) => { + if let TypeBinding::Bound(binding) = &*int.borrow() { + return self.prefix_operand_type_rules(op, binding, span); + } + + // The `!` prefix operator is not valid for Field, so if this is a numeric + // type we constrain it to just (non-Field) integer types. + if matches!(op, crate::ast::UnaryOp::Not) && rhs_type.is_numeric() { + let integer_type = Type::polymorphic_integer(self.interner); + self.unify(rhs_type, &integer_type, || { + TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span } + }); + } + + Ok((rhs_type.clone(), !rhs_type.is_numeric())) + } + Integer(sign_x, bit_width_x) => { + if *op == UnaryOp::Minus && *sign_x == Signedness::Unsigned { + return Err(TypeCheckError::InvalidUnaryOp { + kind: rhs_type.to_string(), + span, + }); + } + Ok((Integer(*sign_x, *bit_width_x), false)) + } + // The result of a Field is always a witness + FieldElement => { + if *op == UnaryOp::Not { + return Err(TypeCheckError::FieldNot { span }); + } + Ok((FieldElement, false)) + } + + Bool => Ok((Bool, false)), + + _ => Ok((rhs_type.clone(), true)), + } + } + crate::ast::UnaryOp::MutableReference => { + Ok((Type::MutableReference(Box::new(rhs_type.follow_bindings())), false)) + } + crate::ast::UnaryOp::Dereference { implicitly_added: _ } => { + let element_type = self.interner.next_type_variable(); + let expected = Type::MutableReference(Box::new(element_type.clone())); + self.unify(rhs_type, &expected, || TypeCheckError::TypeMismatch { + expr_typ: rhs_type.to_string(), + expected_typ: expected.to_string(), + expr_span: span, + }); + Ok((element_type, false)) + } + } + } + /// Prerequisite: verify_trait_constraint of the operator's trait constraint. /// /// Although by this point the operator is expected to already have a trait impl, @@ -1140,6 +1162,7 @@ impl<'context> Elaborator<'context> { *access_lhs = this.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: crate::ast::UnaryOp::Dereference { implicitly_added: true }, rhs: old_lhs, + trait_method_id: None, })); this.interner.push_expr_type(old_lhs, lhs_type); this.interner.push_expr_type(*access_lhs, element); @@ -1362,6 +1385,7 @@ impl<'context> Elaborator<'context> { self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::MutableReference, rhs: *object, + trait_method_id: None, })); self.interner.push_expr_type(new_object, new_type); self.interner.push_expr_location(new_object, location.span, location.file); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs index 25ff50085fe..0472b0040e5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::rc::Rc; use crate::{ @@ -45,6 +46,7 @@ pub enum InterpreterError { NonStructInConstructor { typ: Type, location: Location }, CannotInlineMacro { value: Value, location: Location }, UnquoteFoundDuringEvaluation { location: Location }, + DebugEvaluateComptime { diagnostic: CustomDiagnostic, location: Location }, FailedToParseMacro { error: ParserError, tokens: Rc, rule: &'static str, file: FileId }, UnsupportedTopLevelItemUnquote { item: String, location: Location }, NonComptimeFnCallInSameCrate { function: String, location: Location }, @@ -117,7 +119,8 @@ impl InterpreterError { | InterpreterError::NoImpl { location, .. } | InterpreterError::ImplMethodTypeMismatch { location, .. } | InterpreterError::BreakNotInLoop { location, .. } - | InterpreterError::ContinueNotInLoop { location, .. } => *location, + | InterpreterError::DebugEvaluateComptime { location, .. } => *location, + InterpreterError::ContinueNotInLoop { location, .. } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) } @@ -129,6 +132,20 @@ impl InterpreterError { } } } + + pub(crate) fn debug_evaluate_comptime(expr: impl Display, location: Location) -> Self { + let mut formatted_result = format!("{}", expr); + // if multi-line, display on a separate line from the message + if formatted_result.contains('\n') { + formatted_result.insert(0, '\n'); + } + let diagnostic = CustomDiagnostic::simple_info( + "`comptime` expression ran:".to_string(), + format!("After evaluation: {}", formatted_result), + location.span, + ); + InterpreterError::DebugEvaluateComptime { diagnostic, location } + } } impl<'a> From<&'a InterpreterError> for CustomDiagnostic { @@ -291,6 +308,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { let secondary = "This is a bug".into(); CustomDiagnostic::simple_error(msg, secondary, location.span) } + InterpreterError::DebugEvaluateComptime { diagnostic, .. } => diagnostic.clone(), InterpreterError::FailedToParseMacro { error, tokens, rule, file: _ } => { let message = format!("Failed to parse macro's token stream into {rule}"); let tokens = vecmap(&tokens.0, ToString::to_string).join(" "); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs new file mode 100644 index 00000000000..22763c9cb64 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs @@ -0,0 +1,428 @@ +use iter_extended::vecmap; +use noirc_errors::{Span, Spanned}; + +use crate::ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, + ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, Ident, IfExpression, + IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, UnresolvedType, + UnresolvedTypeData, UnresolvedTypeExpression, +}; +use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; +use crate::hir_def::expr::{HirArrayLiteral, HirBlockExpression, HirExpression, HirIdent}; +use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; +use crate::hir_def::types::{Type, TypeBinding, TypeVariableKind}; +use crate::macros_api::HirLiteral; +use crate::node_interner::{ExprId, NodeInterner, StmtId}; + +// TODO: +// - Full path for idents & types +// - Assert/AssertEq information lost +// - The type name span is lost in constructor patterns & expressions +// - All type spans are lost +// - Type::TypeVariable has no equivalent in the Ast + +impl HirStatement { + pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Statement { + let kind = match self { + HirStatement::Let(let_stmt) => { + let pattern = let_stmt.pattern.to_display_ast(interner); + let r#type = interner.id_type(let_stmt.expression).to_display_ast(); + let expression = let_stmt.expression.to_display_ast(interner); + StatementKind::Let(LetStatement { + pattern, + r#type, + expression, + comptime: false, + attributes: Vec::new(), + }) + } + HirStatement::Constrain(constrain) => { + let expr = constrain.0.to_display_ast(interner); + let message = constrain.2.map(|message| message.to_display_ast(interner)); + + // TODO: Find difference in usage between Assert & AssertEq + StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) + } + HirStatement::Assign(assign) => StatementKind::Assign(AssignStatement { + lvalue: assign.lvalue.to_display_ast(interner), + expression: assign.expression.to_display_ast(interner), + }), + HirStatement::For(for_stmt) => StatementKind::For(ForLoopStatement { + identifier: for_stmt.identifier.to_display_ast(interner), + range: ForRange::Range( + for_stmt.start_range.to_display_ast(interner), + for_stmt.end_range.to_display_ast(interner), + ), + block: for_stmt.block.to_display_ast(interner), + span, + }), + HirStatement::Break => StatementKind::Break, + HirStatement::Continue => StatementKind::Continue, + HirStatement::Expression(expr) => { + StatementKind::Expression(expr.to_display_ast(interner)) + } + HirStatement::Semi(expr) => StatementKind::Semi(expr.to_display_ast(interner)), + HirStatement::Error => StatementKind::Error, + HirStatement::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement.to_display_ast(interner))) + } + }; + + Statement { kind, span } + } +} + +impl StmtId { + /// Convert to AST for display (some details lost) + pub fn to_display_ast(self, interner: &NodeInterner) -> Statement { + let statement = interner.statement(&self); + let span = interner.statement_span(self); + + statement.to_display_ast(interner, span) + } +} + +impl HirExpression { + /// Convert to AST for display (some details lost) + pub fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> Expression { + let kind = match self { + HirExpression::Ident(ident, generics) => { + let path = Path::from_ident(ident.to_display_ast(interner)); + ExpressionKind::Variable( + path, + generics.as_ref().map(|option| { + option.iter().map(|generic| generic.to_display_ast()).collect() + }), + ) + } + HirExpression::Literal(HirLiteral::Array(array)) => { + let array = array.to_display_ast(interner, span); + ExpressionKind::Literal(Literal::Array(array)) + } + HirExpression::Literal(HirLiteral::Slice(array)) => { + let array = array.to_display_ast(interner, span); + ExpressionKind::Literal(Literal::Slice(array)) + } + HirExpression::Literal(HirLiteral::Bool(value)) => { + ExpressionKind::Literal(Literal::Bool(*value)) + } + HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + ExpressionKind::Literal(Literal::Integer(*value, *sign)) + } + HirExpression::Literal(HirLiteral::Str(string)) => { + ExpressionKind::Literal(Literal::Str(string.clone())) + } + HirExpression::Literal(HirLiteral::FmtStr(string, _exprs)) => { + // TODO: Is throwing away the exprs here valid? + ExpressionKind::Literal(Literal::FmtStr(string.clone())) + } + HirExpression::Literal(HirLiteral::Unit) => ExpressionKind::Literal(Literal::Unit), + HirExpression::Block(expr) => ExpressionKind::Block(expr.to_display_ast(interner)), + HirExpression::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { + operator: prefix.operator, + rhs: prefix.rhs.to_display_ast(interner), + })), + HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { + lhs: infix.lhs.to_display_ast(interner), + operator: Spanned::from(infix.operator.location.span, infix.operator.kind), + rhs: infix.rhs.to_display_ast(interner), + })), + HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { + collection: index.collection.to_display_ast(interner), + index: index.index.to_display_ast(interner), + })), + HirExpression::Constructor(constructor) => { + let type_name = constructor.r#type.borrow().name.to_string(); + let type_name = Path::from_single(type_name, span); + let fields = vecmap(constructor.fields.clone(), |(name, expr): (Ident, ExprId)| { + (name, expr.to_display_ast(interner)) + }); + let struct_type = None; + + ExpressionKind::Constructor(Box::new(ConstructorExpression { + type_name, + fields, + struct_type, + })) + } + HirExpression::MemberAccess(access) => { + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: access.lhs.to_display_ast(interner), + rhs: access.rhs.clone(), + })) + } + HirExpression::Call(call) => { + let func = Box::new(call.func.to_display_ast(interner)); + let arguments = vecmap(call.arguments.clone(), |arg| arg.to_display_ast(interner)); + let is_macro_call = false; + ExpressionKind::Call(Box::new(CallExpression { func, arguments, is_macro_call })) + } + HirExpression::MethodCall(method_call) => { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: method_call.object.to_display_ast(interner), + method_name: method_call.method.clone(), + arguments: vecmap(method_call.arguments.clone(), |arg| { + arg.to_display_ast(interner) + }), + generics: method_call.generics.clone().map(|option| { + option.iter().map(|generic| generic.to_display_ast()).collect() + }), + is_macro_call: false, + })) + } + HirExpression::Cast(cast) => { + let lhs = cast.lhs.to_display_ast(interner); + let r#type = cast.r#type.to_display_ast(); + ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type })) + } + HirExpression::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { + condition: if_expr.condition.to_display_ast(interner), + consequence: if_expr.consequence.to_display_ast(interner), + alternative: if_expr.alternative.map(|expr| expr.to_display_ast(interner)), + })), + HirExpression::Tuple(fields) => { + ExpressionKind::Tuple(vecmap(fields, |field| field.to_display_ast(interner))) + } + HirExpression::Lambda(lambda) => { + let parameters = vecmap(lambda.parameters.clone(), |(pattern, typ)| { + (pattern.to_display_ast(interner), typ.to_display_ast()) + }); + let return_type = lambda.return_type.to_display_ast(); + let body = lambda.body.to_display_ast(interner); + ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) + } + HirExpression::Error => ExpressionKind::Error, + HirExpression::Comptime(block) => { + ExpressionKind::Comptime(block.to_display_ast(interner), span) + } + HirExpression::Quote(block) => ExpressionKind::Quote(block.clone()), + + // A macro was evaluated here: return the quoted result + HirExpression::Unquote(block) => ExpressionKind::Quote(block.clone()), + }; + + Expression::new(kind, span) + } +} + +impl ExprId { + /// Convert to AST for display (some details lost) + pub fn to_display_ast(self, interner: &NodeInterner) -> Expression { + let expression = interner.expression(&self); + // TODO: empty 0 span + let span = interner.try_expr_span(&self).unwrap_or_else(|| Span::empty(0)); + expression.to_display_ast(interner, span) + } +} + +impl HirPattern { + /// Convert to AST for display (some details lost) + fn to_display_ast(&self, interner: &NodeInterner) -> Pattern { + match self { + HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_display_ast(interner)), + HirPattern::Mutable(pattern, location) => { + let pattern = Box::new(pattern.to_display_ast(interner)); + Pattern::Mutable(pattern, location.span, false) + } + HirPattern::Tuple(patterns, location) => { + let patterns = vecmap(patterns, |pattern| pattern.to_display_ast(interner)); + Pattern::Tuple(patterns, location.span) + } + HirPattern::Struct(typ, patterns, location) => { + let patterns = vecmap(patterns, |(name, pattern)| { + (name.clone(), pattern.to_display_ast(interner)) + }); + let name = match typ.follow_bindings() { + Type::Struct(struct_def, _) => { + let struct_def = struct_def.borrow(); + struct_def.name.0.contents.clone() + } + // This pass shouldn't error so if the type isn't a struct we just get a string + // representation of any other type and use that. We're relying on name + // resolution to fail later when this Ast is re-converted to Hir. + other => other.to_string(), + }; + // The name span is lost here + let path = Path::from_single(name, location.span); + Pattern::Struct(path, patterns, location.span) + } + } + } +} + +impl HirIdent { + /// Convert to AST for display (some details lost) + fn to_display_ast(&self, interner: &NodeInterner) -> Ident { + let name = interner.definition_name(self.id).to_owned(); + Ident(Spanned::from(self.location.span, name)) + } +} + +impl Type { + /// Convert to AST for display (some details lost) + fn to_display_ast(&self) -> UnresolvedType { + let typ = match self { + Type::FieldElement => UnresolvedTypeData::FieldElement, + Type::Array(length, element) => { + let length = length.to_type_expression(); + let element = Box::new(element.to_display_ast()); + UnresolvedTypeData::Array(length, element) + } + Type::Slice(element) => { + let element = Box::new(element.to_display_ast()); + UnresolvedTypeData::Slice(element) + } + Type::Integer(sign, bit_size) => UnresolvedTypeData::Integer(*sign, *bit_size), + Type::Bool => UnresolvedTypeData::Bool, + Type::String(length) => { + let length = length.to_type_expression(); + UnresolvedTypeData::String(length) + } + Type::FmtString(length, element) => { + let length = length.to_type_expression(); + let element = Box::new(element.to_display_ast()); + UnresolvedTypeData::FormatString(length, element) + } + Type::Unit => UnresolvedTypeData::Unit, + Type::Tuple(fields) => { + let fields = vecmap(fields, |field| field.to_display_ast()); + UnresolvedTypeData::Tuple(fields) + } + Type::Struct(def, generics) => { + let struct_def = def.borrow(); + let generics = vecmap(generics, |generic| generic.to_display_ast()); + let name = Path::from_ident(struct_def.name.clone()); + UnresolvedTypeData::Named(name, generics, false) + } + Type::Alias(type_def, generics) => { + // Keep the alias name instead of expanding this in case the + // alias' definition was changed + let type_def = type_def.borrow(); + let generics = vecmap(generics, |generic| generic.to_display_ast()); + let name = Path::from_ident(type_def.name.clone()); + UnresolvedTypeData::Named(name, generics, false) + } + Type::TypeVariable(binding, kind) => { + match &*binding.borrow() { + TypeBinding::Bound(typ) => return typ.to_display_ast(), + TypeBinding::Unbound(id) => { + let expression = match kind { + // TODO: fix span or make Option + TypeVariableKind::Constant(value) => { + UnresolvedTypeExpression::Constant(*value, Span::empty(0)) + } + other_kind => { + let name = format!("var_{:?}_{}", other_kind, id); + + // TODO: fix span or make Option + let path = Path::from_single(name, Span::empty(0)); + UnresolvedTypeExpression::Variable(path) + } + }; + + UnresolvedTypeData::Expression(expression) + } + } + } + Type::TraitAsType(_, name, generics) => { + let generics = vecmap(generics, |generic| generic.to_display_ast()); + let name = Path::from_single(name.as_ref().clone(), Span::default()); + UnresolvedTypeData::TraitAsType(name, generics) + } + Type::NamedGeneric(_var, name, _kind) => { + let name = Path::from_single(name.as_ref().clone(), Span::default()); + UnresolvedTypeData::TraitAsType(name, Vec::new()) + } + Type::Function(args, ret, env) => { + let args = vecmap(args, |arg| arg.to_display_ast()); + let ret = Box::new(ret.to_display_ast()); + let env = Box::new(env.to_display_ast()); + UnresolvedTypeData::Function(args, ret, env) + } + Type::MutableReference(element) => { + let element = Box::new(element.to_display_ast()); + UnresolvedTypeData::MutableReference(element) + } + // Type::Forall is only for generic functions which don't store a type + // in their Ast so they don't need to call to_display_ast for their Forall type. + // Since there is no UnresolvedTypeData equivalent for Type::Forall, we use + // this to ignore this case since it shouldn't be needed anyway. + Type::Forall(_, typ) => return typ.to_display_ast(), + Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), + Type::Quoted(quoted_type) => UnresolvedTypeData::Quoted(*quoted_type), + Type::Error => UnresolvedTypeData::Error, + }; + + UnresolvedType { typ, span: None } + } + + /// Convert to AST for display (some details lost) + fn to_type_expression(&self) -> UnresolvedTypeExpression { + let span = Span::default(); + + match self.follow_bindings() { + Type::Constant(length) => UnresolvedTypeExpression::Constant(length, span), + Type::NamedGeneric(_var, name, _kind) => { + let path = Path::from_single(name.as_ref().clone(), span); + UnresolvedTypeExpression::Variable(path) + } + // TODO: This should be turned into a proper error. + other => panic!("Cannot represent {other:?} as type expression"), + } + } +} + +impl HirLValue { + /// Convert to AST for display (some details lost) + fn to_display_ast(&self, interner: &NodeInterner) -> LValue { + match self { + HirLValue::Ident(ident, _) => LValue::Ident(ident.to_display_ast(interner)), + HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { + let object = Box::new(object.to_display_ast(interner)); + LValue::MemberAccess { object, field_name: field_name.clone(), span: location.span } + } + HirLValue::Index { array, index, typ: _, location } => { + let array = Box::new(array.to_display_ast(interner)); + let index = index.to_display_ast(interner); + LValue::Index { array, index, span: location.span } + } + HirLValue::Dereference { lvalue, element_type: _, location } => { + let lvalue = Box::new(lvalue.to_display_ast(interner)); + LValue::Dereference(lvalue, location.span) + } + } + } +} + +impl HirArrayLiteral { + /// Convert to AST for display (some details lost) + fn to_display_ast(&self, interner: &NodeInterner, span: Span) -> ArrayLiteral { + match self { + HirArrayLiteral::Standard(elements) => { + ArrayLiteral::Standard(vecmap(elements, |element| element.to_display_ast(interner))) + } + HirArrayLiteral::Repeated { repeated_element, length } => { + let repeated_element = Box::new(repeated_element.to_display_ast(interner)); + let length = match length { + Type::Constant(length) => { + let literal = Literal::Integer((*length as u128).into(), false); + let kind = ExpressionKind::Literal(literal); + Box::new(Expression::new(kind, span)) + } + other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), + }; + ArrayLiteral::Repeated { repeated_element, length } + } + } + } +} + +impl HirBlockExpression { + /// Convert to AST for display (some details lost) + fn to_display_ast(&self, interner: &NodeInterner) -> BlockExpression { + let statements = + vecmap(self.statements.clone(), |statement| statement.to_display_ast(interner)); + BlockExpression { statements } + } +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 605a35780ed..02714f77605 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -1,6 +1,7 @@ use std::{collections::hash_map::Entry, rc::Rc}; use acvm::{acir::AcirField, FieldElement}; +use fm::FileId; use im::Vector; use iter_extended::try_vecmap; use noirc_errors::Location; @@ -19,7 +20,7 @@ use crate::{ hir_def::{ expr::{ HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, - HirConstructorExpression, HirIdent, HirIfExpression, HirIndexExpression, + HirConstructorExpression, HirExpression, HirIdent, HirIfExpression, HirIndexExpression, HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, HirPrefixExpression, }, @@ -28,7 +29,7 @@ use crate::{ HirPattern, }, }, - macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, + macros_api::{HirLiteral, HirStatement, NodeInterner}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, Shared, Type, TypeBinding, TypeBindings, TypeVariableKind, }; @@ -51,6 +52,10 @@ pub struct Interpreter<'interner> { crate_id: CrateId, + /// The scope of --debug-comptime, or None if unset + pub(super) debug_comptime_in_file: Option, + pub(super) debug_comptime_evaluations: &'interner mut Vec, + in_loop: bool, } @@ -60,8 +65,17 @@ impl<'a> Interpreter<'a> { interner: &'a mut NodeInterner, scopes: &'a mut Vec>, crate_id: CrateId, + debug_comptime_in_file: Option, + debug_comptime_evaluations: &'a mut Vec, ) -> Self { - Self { interner, scopes, crate_id, in_loop: false } + Self { + interner, + scopes, + crate_id, + debug_comptime_in_file, + debug_comptime_evaluations, + in_loop: false, + } } pub(crate) fn call_function( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs index ab984d2f2be..3cc7b5f7e98 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,4 +1,5 @@ mod errors; +mod hir_to_display_ast; mod interpreter; mod scan; mod tests; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs index 770c523bd7f..f9cc54ef9e4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/scan.rs @@ -29,6 +29,8 @@ use super::{ Value, }; +use noirc_errors::Location; + #[allow(dead_code)] impl<'interner> Interpreter<'interner> { /// Scan through a function, evaluating any Comptime nodes found. @@ -80,9 +82,10 @@ impl<'interner> Interpreter<'interner> { HirExpression::Lambda(lambda) => self.scan_lambda(lambda), HirExpression::Comptime(block) => { let location = self.interner.expr_location(&expr); - let new_expr = + let new_expr_id = self.evaluate_block(block)?.into_hir_expression(self.interner, location)?; - let new_expr = self.interner.expression(&new_expr); + let new_expr = self.interner.expression(&new_expr_id); + self.debug_comptime(new_expr_id, location); self.interner.replace_expr(&expr, new_expr); Ok(()) } @@ -118,7 +121,9 @@ impl<'interner> Interpreter<'interner> { if let Ok(value) = self.evaluate_ident(ident, id) { // TODO(#4922): Inlining closures is currently unimplemented if !matches!(value, Value::Closure(..)) { - self.inline_expression(value, id)?; + let new_expr = self.inline_expression(value, id)?; + let location = self.interner.id_location(id); + self.debug_comptime(new_expr, location); } } Ok(()) @@ -231,6 +236,7 @@ impl<'interner> Interpreter<'interner> { let new_expr = self .evaluate_comptime(comptime)? .into_hir_expression(self.interner, location)?; + self.debug_comptime(new_expr, location); self.interner.replace_statement(statement, HirStatement::Expression(new_expr)); Ok(()) } @@ -247,11 +253,19 @@ impl<'interner> Interpreter<'interner> { Ok(()) } - fn inline_expression(&mut self, value: Value, expr: ExprId) -> IResult<()> { + fn inline_expression(&mut self, value: Value, expr: ExprId) -> IResult { let location = self.interner.expr_location(&expr); - let new_expr = value.into_hir_expression(self.interner, location)?; - let new_expr = self.interner.expression(&new_expr); + let new_expr_id = value.into_hir_expression(self.interner, location)?; + let new_expr = self.interner.expression(&new_expr_id); self.interner.replace_expr(&expr, new_expr); - Ok(()) + Ok(new_expr_id) + } + + fn debug_comptime(&mut self, expr: ExprId, location: Location) { + if Some(location.file) == self.debug_comptime_in_file { + let expr = expr.to_display_ast(self.interner); + self.debug_comptime_evaluations + .push(InterpreterError::debug_evaluate_comptime(expr, location)); + } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs index 870f2bc458a..e8e05506c94 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -13,7 +13,15 @@ use crate::hir::type_check::test::type_check_src_code; fn interpret_helper(src: &str, func_namespace: Vec) -> Result { let (mut interner, main_id) = type_check_src_code(src, func_namespace); let mut scopes = vec![HashMap::default()]; - let mut interpreter = Interpreter::new(&mut interner, &mut scopes, CrateId::Root(0)); + let no_debug_evaluate_comptime = None; + let mut interpreter_errors = vec![]; + let mut interpreter = Interpreter::new( + &mut interner, + &mut scopes, + CrateId::Root(0), + no_debug_evaluate_comptime, + &mut interpreter_errors, + ); let no_location = Location::dummy(); interpreter.call_function(main_id, Vec::new(), HashMap::new(), no_location) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs index 9e15b73324f..46d1fc23973 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -8,12 +8,13 @@ use noirc_errors::Location; use crate::{ ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness}, + hir::def_map::ModuleId, hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, - node_interner::{ExprId, FuncId}, + node_interner::{ExprId, FuncId, TraitId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, TypeBindings, @@ -45,6 +46,9 @@ pub enum Value { Slice(Vector, Type), Code(Rc), StructDefinition(StructId), + TraitDefinition(TraitId), + FunctionDefinition(FuncId), + ModuleDefinition(ModuleId), } impl Value { @@ -79,6 +83,9 @@ impl Value { let element = element.borrow().get_type().into_owned(); Type::MutableReference(Box::new(element)) } + Value::TraitDefinition(_) => Type::Quoted(QuotedType::TraitDefinition), + Value::FunctionDefinition(_) => Type::Quoted(QuotedType::FunctionDefinition), + Value::ModuleDefinition(_) => Type::Quoted(QuotedType::Module), }) } @@ -192,7 +199,11 @@ impl Value { } }; } - Value::Pointer(_) | Value::StructDefinition(_) => { + Value::Pointer(_) + | Value::StructDefinition(_) + | Value::TraitDefinition(_) + | Value::FunctionDefinition(_) + | Value::ModuleDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } }; @@ -298,7 +309,11 @@ impl Value { HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } Value::Code(block) => HirExpression::Unquote(unwrap_rc(block)), - Value::Pointer(_) | Value::StructDefinition(_) => { + Value::Pointer(_) + | Value::StructDefinition(_) + | Value::TraitDefinition(_) + | Value::FunctionDefinition(_) + | Value::ModuleDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } }; @@ -402,6 +417,9 @@ impl Display for Value { write!(f, " }}") } Value::StructDefinition(_) => write!(f, "(struct definition)"), + Value::TraitDefinition(_) => write!(f, "(trait definition)"), + Value::FunctionDefinition(_) => write!(f, "(function definition)"), + Value::ModuleDefinition(_) => write!(f, "(module)"), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 43ab6224ea7..f42819dab1f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -29,12 +29,14 @@ use crate::ast::{ }; use crate::parser::{ParserError, SortedModule}; +use noirc_errors::{CustomDiagnostic, Location, Span}; + use fm::FileId; use iter_extended::vecmap; -use noirc_errors::{CustomDiagnostic, Location, Span}; use rustc_hash::FxHashMap as HashMap; use std::collections::BTreeMap; - +use std::fmt::Write; +use std::path::PathBuf; use std::vec; #[derive(Default)] @@ -42,6 +44,7 @@ pub struct ResolvedModule { pub globals: Vec<(FileId, GlobalId)>, pub functions: Vec<(FileId, FuncId)>, pub trait_impl_functions: Vec<(FileId, FuncId)>, + pub debug_comptime_in_file: Option, pub errors: Vec<(CompilationError, FileId)>, } @@ -195,10 +198,11 @@ pub enum CompilationError { ResolverError(ResolverError), TypeError(TypeCheckError), InterpreterError(InterpreterError), + DebugComptimeScopeNotFound(Vec), } impl CompilationError { - fn is_error(&self) -> bool { + pub fn is_error(&self) -> bool { let diagnostic = CustomDiagnostic::from(self); diagnostic.is_error() } @@ -212,6 +216,16 @@ impl<'a> From<&'a CompilationError> for CustomDiagnostic { CompilationError::ResolverError(error) => error.into(), CompilationError::TypeError(error) => error.into(), CompilationError::InterpreterError(error) => error.into(), + CompilationError::DebugComptimeScopeNotFound(error) => { + let msg = "multiple files found matching --debug-comptime path".into(); + let secondary = error.iter().fold(String::new(), |mut output, path| { + let _ = writeln!(output, " {}", path.display()); + output + }); + // NOTE: this span is empty as it is not expected to be displayed + let dummy_span = Span::default(); + CustomDiagnostic::simple_error(msg, secondary, dummy_span) + } } } } @@ -271,6 +285,7 @@ impl DefCollector { ast: SortedModule, root_file_id: FileId, use_legacy: bool, + debug_comptime_in_file: Option<&str>, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; @@ -288,6 +303,7 @@ impl DefCollector { dep.crate_id, context, use_legacy, + debug_comptime_in_file, macro_processors, )); @@ -331,7 +347,7 @@ impl DefCollector { for collected_import in std::mem::take(&mut def_collector.imports) { let module_id = collected_import.module_id; let resolved_import = if context.def_interner.track_references { - let mut references: Vec = Vec::new(); + let mut references: Vec> = Vec::new(); let resolved_import = resolve_import( crate_id, &collected_import, @@ -343,9 +359,14 @@ impl DefCollector { let file_id = current_def_map.file_id(module_id); for (referenced, ident) in references.iter().zip(&collected_import.path.segments) { - let reference = - ReferenceId::Reference(Location::new(ident.span(), file_id), false); - context.def_interner.add_reference(*referenced, reference); + let Some(referenced) = referenced else { + continue; + }; + context.def_interner.add_reference( + *referenced, + Location::new(ident.span(), file_id), + false, + ); } resolved_import @@ -396,13 +417,35 @@ impl DefCollector { } } + let debug_comptime_in_file = debug_comptime_in_file.and_then(|debug_comptime_in_file| { + let file = context.file_manager.find_by_path_suffix(debug_comptime_in_file); + file.unwrap_or_else(|error| { + errors.push((CompilationError::DebugComptimeScopeNotFound(error), root_file_id)); + None + }) + }); + if !use_legacy { - let mut more_errors = Elaborator::elaborate(context, crate_id, def_collector.items); + let mut more_errors = Elaborator::elaborate( + context, + crate_id, + def_collector.items, + debug_comptime_in_file, + ); errors.append(&mut more_errors); + + for macro_processor in macro_processors { + macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( + |(macro_err, file_id)| { + errors.push((macro_err.into(), file_id)); + }, + ); + } return errors; } - let mut resolved_module = ResolvedModule { errors, ..Default::default() }; + let mut resolved_module = + ResolvedModule { errors, debug_comptime_in_file, ..Default::default() }; // We must first resolve and intern the globals before we can resolve any stmts inside each function. // Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope @@ -511,18 +554,8 @@ fn add_import_reference( return; } - let referenced = match def_id { - crate::macros_api::ModuleDefId::ModuleId(module_id) => ReferenceId::Module(module_id), - crate::macros_api::ModuleDefId::FunctionId(func_id) => ReferenceId::Function(func_id), - crate::macros_api::ModuleDefId::TypeId(struct_id) => ReferenceId::Struct(struct_id), - crate::macros_api::ModuleDefId::TraitId(trait_id) => ReferenceId::Trait(trait_id), - crate::macros_api::ModuleDefId::TypeAliasId(type_alias_id) => { - ReferenceId::Alias(type_alias_id) - } - crate::macros_api::ModuleDefId::GlobalId(global_id) => ReferenceId::Global(global_id), - }; - let reference = ReferenceId::Reference(Location::new(name.span(), file_id), false); - interner.add_reference(referenced, reference); + let location = Location::new(name.span(), file_id); + interner.add_module_def_id_reference(def_id, location, false); } fn inject_prelude( @@ -621,7 +654,14 @@ impl ResolvedModule { fn evaluate_comptime(&mut self, interner: &mut NodeInterner, crate_id: CrateId) { if self.count_errors() == 0 { let mut scopes = vec![HashMap::default()]; - let mut interpreter = Interpreter::new(interner, &mut scopes, crate_id); + let mut interpreter_errors = vec![]; + let mut interpreter = Interpreter::new( + interner, + &mut scopes, + crate_id, + self.debug_comptime_in_file, + &mut interpreter_errors, + ); for (_file, global) in &self.globals { if let Err(error) = interpreter.scan_global(*global) { @@ -636,6 +676,9 @@ impl ResolvedModule { self.errors.push(error.into_compilation_error_pair()); } } + self.errors.extend( + interpreter_errors.into_iter().map(InterpreterError::into_compilation_error_pair), + ); } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 48985116f4f..1f30b4388b6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -14,7 +14,7 @@ use crate::ast::{ TypeImpl, }; use crate::macros_api::NodeInterner; -use crate::node_interner::ReferenceId; +use crate::node_interner::{ModuleAttributes, ReferenceId}; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, @@ -90,7 +90,7 @@ pub fn collect_defs( errors.extend(collector.collect_structs(context, ast.types, crate_id)); - errors.extend(collector.collect_type_aliases(context, ast.type_aliases)); + errors.extend(collector.collect_type_aliases(context, ast.type_aliases, crate_id)); errors.extend(collector.collect_functions(context, ast.functions, crate_id)); @@ -318,7 +318,10 @@ impl<'a> ModCollector<'a> { // And store the TypeId -> StructType mapping somewhere it is reachable self.def_collector.items.types.insert(id, unresolved); - context.def_interner.add_definition_location(ReferenceId::Struct(id)); + context.def_interner.add_definition_location( + ReferenceId::Struct(id), + Some(ModuleId { krate, local_id: self.module_id }), + ); } definition_errors } @@ -329,6 +332,7 @@ impl<'a> ModCollector<'a> { &mut self, context: &mut Context, type_aliases: Vec, + krate: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; for type_alias in type_aliases { @@ -365,7 +369,10 @@ impl<'a> ModCollector<'a> { self.def_collector.items.type_aliases.insert(type_alias_id, unresolved); - context.def_interner.add_definition_location(ReferenceId::Alias(type_alias_id)); + context.def_interner.add_definition_location( + ReferenceId::Alias(type_alias_id), + Some(ModuleId { krate, local_id: self.module_id }), + ); } errors } @@ -532,7 +539,10 @@ impl<'a> ModCollector<'a> { }; context.def_interner.push_empty_trait(trait_id, &unresolved, resolved_generics); - context.def_interner.add_definition_location(ReferenceId::Trait(trait_id)); + context.def_interner.add_definition_location( + ReferenceId::Trait(trait_id), + Some(ModuleId { krate, local_id: self.module_id }), + ); self.def_collector.items.traits.insert(trait_id, unresolved); } @@ -649,9 +659,7 @@ impl<'a> ModCollector<'a> { ) { Ok(child_mod_id) => { // Track that the "foo" in `mod foo;` points to the module "foo" - let referenced = ReferenceId::Module(child_mod_id); - let reference = ReferenceId::Reference(location, false); - context.def_interner.add_reference(referenced, reference); + context.def_interner.add_module_reference(child_mod_id, location); errors.extend(collect_defs( self.def_collector, @@ -722,7 +730,14 @@ impl<'a> ModCollector<'a> { return Err(err); } - context.def_interner.add_module_location(mod_id, mod_location); + context.def_interner.add_module_attributes( + mod_id, + ModuleAttributes { + name: mod_name.0.contents.clone(), + location: mod_location, + parent: self.module_id, + }, + ); } Ok(mod_id) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs index 59205f74d89..43d1548dc29 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -74,6 +74,7 @@ impl CrateDefMap { crate_id: CrateId, context: &mut Context, use_legacy: bool, + debug_comptime_in_file: Option<&str>, macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled @@ -123,6 +124,7 @@ impl CrateDefMap { ast, root_file_id, use_legacy, + debug_comptime_in_file, macro_processors, )); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs index 710c12a91bf..e0c87e2d9e4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -81,7 +81,7 @@ pub fn resolve_import( crate_id: CrateId, import_directive: &ImportDirective, def_maps: &BTreeMap, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> Result { let module_scope = import_directive.module_id; let NamespaceResolution { @@ -126,7 +126,7 @@ fn resolve_path_to_ns( crate_id: CrateId, importing_crate: CrateId, def_maps: &BTreeMap, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> NamespaceResolutionResult { let import_path = &import_directive.path.segments; let def_map = &def_maps[&crate_id]; @@ -196,7 +196,7 @@ fn resolve_path_from_crate_root( import_path: &[Ident], def_maps: &BTreeMap, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> NamespaceResolutionResult { resolve_name_in_module( crate_id, @@ -214,7 +214,7 @@ fn resolve_name_in_module( import_path: &[Ident], starting_mod: LocalModuleId, def_maps: &BTreeMap, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> NamespaceResolutionResult { let def_map = &def_maps[&krate]; let mut current_mod_id = ModuleId { krate, local_id: starting_mod }; @@ -247,7 +247,7 @@ fn resolve_name_in_module( current_mod_id = match typ { ModuleDefId::ModuleId(id) => { if let Some(path_references) = path_references { - path_references.push(ReferenceId::Module(id)); + path_references.push(Some(ReferenceId::Module(id))); } id } @@ -255,14 +255,14 @@ fn resolve_name_in_module( // TODO: If impls are ever implemented, types can be used in a path ModuleDefId::TypeId(id) => { if let Some(path_references) = path_references { - path_references.push(ReferenceId::Struct(id)); + path_references.push(Some(ReferenceId::Struct(id))); } id.module_id() } ModuleDefId::TypeAliasId(_) => panic!("type aliases cannot be used in type namespace"), ModuleDefId::TraitId(id) => { if let Some(path_references) = path_references { - path_references.push(ReferenceId::Trait(id)); + path_references.push(Some(ReferenceId::Trait(id))); } id.0 } @@ -309,7 +309,7 @@ fn resolve_external_dep( current_def_map: &CrateDefMap, directive: &ImportDirective, def_maps: &BTreeMap, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, importing_crate: CrateId, ) -> NamespaceResolutionResult { // Use extern_prelude to get the dep @@ -327,6 +327,11 @@ fn resolve_external_dep( // See `singleton_import.nr` test case for a check that such cases are handled elsewhere. let path_without_crate_name = &path[1..]; + // Given that we skipped the first segment, record that it doesn't refer to any module or type. + if let Some(path_references) = path_references { + path_references.push(None); + } + let path = Path { segments: path_without_crate_name.to_vec(), kind: PathKind::Plain, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs index c3dc76b635f..7cd44a84018 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs @@ -9,12 +9,13 @@ use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; pub trait PathResolver { /// Resolve the given path returning the resolved ModuleDefId. /// If `path_references` is `Some`, a `ReferenceId` for each segment in `path` - /// will be resolved and pushed. + /// will be resolved and pushed (some entries will be None if they don't refer to + /// a module or type). fn resolve( &self, def_maps: &BTreeMap, path: Path, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> PathResolutionResult; fn local_module_id(&self) -> LocalModuleId; @@ -38,7 +39,7 @@ impl PathResolver for StandardPathResolver { &self, def_maps: &BTreeMap, path: Path, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> PathResolutionResult { resolve_path(def_maps, self.module_id, path, path_references) } @@ -58,7 +59,7 @@ pub fn resolve_path( def_maps: &BTreeMap, module_id: ModuleId, path: Path, - path_references: &mut Option<&mut Vec>, + path_references: &mut Option<&mut Vec>>, ) -> PathResolutionResult { // lets package up the path into an ImportDirective and resolve it using that let import = diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 793362bb3d6..55cb2145ba8 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1105,6 +1105,7 @@ impl<'a> Resolver<'a> { location, typ, direct_generics, + struct_id: None, trait_impl: self.current_trait_impl, parameters: parameters.into(), return_type: func.def.return_type.clone(), @@ -1548,6 +1549,7 @@ impl<'a> Resolver<'a> { ExpressionKind::Prefix(prefix) => { let operator = prefix.operator; let rhs = self.resolve_expression(prefix.rhs); + let trait_method_id = self.interner.get_prefix_operator_trait_method(&operator); if operator == UnaryOp::MutableReference { if let Err(error) = verify_mutable_reference(self.interner, rhs) { @@ -1555,7 +1557,7 @@ impl<'a> Resolver<'a> { } } - HirExpression::Prefix(HirPrefixExpression { operator, rhs }) + HirExpression::Prefix(HirPrefixExpression { operator, rhs, trait_method_id }) } ExpressionKind::Infix(infix) => { let lhs = self.resolve_expression(infix.lhs); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs index 6781c2833c4..28ee70393cd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -72,7 +72,8 @@ pub(crate) fn resolve_traits( // the interner may set `interner.ordering_type` based on the result type // of the Cmp trait, if this is it. if crate_id.is_stdlib() { - context.def_interner.try_add_operator_trait(trait_id); + context.def_interner.try_add_infix_operator_trait(trait_id); + context.def_interner.try_add_prefix_operator_trait(trait_id); } } all_errors diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs index e5c52213056..af168a10df9 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -83,6 +83,8 @@ pub enum TypeCheckError { IntegerAndFieldBinaryOperation { span: Span }, #[error("Cannot do modulo on Fields, try casting to an integer first")] FieldModulo { span: Span }, + #[error("Cannot do not (`!`) on Fields, try casting to an integer first")] + FieldNot { span: Span }, #[error("Fields cannot be compared, try casting to an integer first")] FieldComparison { span: Span }, #[error("The bit count in a bit-shift operation must fit in a u8, try casting the right hand side into a u8 first")] @@ -256,6 +258,7 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { | TypeCheckError::IntegerAndFieldBinaryOperation { span } | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } + | TypeCheckError::FieldNot { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs index 1f3e9103cde..9dfe0901016 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -578,6 +578,7 @@ impl<'interner> TypeChecker<'interner> { self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::MutableReference, rhs: method_call.object, + trait_method_id: None, })); self.interner.push_expr_type(new_object, new_type); self.interner.push_expr_location(new_object, location.span, location.file); @@ -604,6 +605,7 @@ impl<'interner> TypeChecker<'interner> { let object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::Dereference { implicitly_added: true }, rhs: object, + trait_method_id: None, })); self.interner.push_expr_type(object, element.as_ref().clone()); self.interner.push_expr_location(object, location.span, location.file); @@ -799,9 +801,11 @@ impl<'interner> TypeChecker<'interner> { let dereference_lhs = |this: &mut Self, lhs_type, element| { let old_lhs = *access_lhs; + *access_lhs = this.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: crate::ast::UnaryOp::Dereference { implicitly_added: true }, rhs: old_lhs, + trait_method_id: None, })); this.interner.push_expr_type(old_lhs, lhs_type); this.interner.push_expr_type(*access_lhs, element); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs index 1a70bade863..57125e1e2dd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -570,6 +570,7 @@ pub mod test { .into(), return_visibility: Visibility::Private, has_body: true, + struct_id: None, trait_impl: None, return_type: FunctionReturnType::Default(Span::default()), trait_constraints: Vec::new(), @@ -696,7 +697,7 @@ pub mod test { &self, _def_maps: &BTreeMap, path: Path, - _path_references: &mut Option<&mut Vec>, + _path_references: &mut Option<&mut Vec>>, ) -> PathResolutionResult { // Not here that foo::bar and hello::foo::bar would fetch the same thing let name = path.segments.last().unwrap(); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs index 8de4f118774..ab2344746d1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/expr.rs @@ -124,6 +124,10 @@ pub enum HirArrayLiteral { pub struct HirPrefixExpression { pub operator: UnaryOp, pub rhs: ExprId, + + /// The trait method id for the operator trait method that corresponds to this operator, + /// if such a trait exists (for example, there's no trait for the dereference operator). + pub trait_method_id: Option, } #[derive(Debug, Clone)] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs index fa8bb55abee..e666f996231 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs @@ -6,7 +6,7 @@ use super::stmt::HirPattern; use super::traits::TraitConstraint; use crate::ast::{FunctionKind, FunctionReturnType, Visibility}; use crate::graph::CrateId; -use crate::macros_api::BlockExpression; +use crate::macros_api::{BlockExpression, StructId}; use crate::node_interner::{ExprId, NodeInterner, TraitImplId}; use crate::{ResolvedGeneric, Type}; @@ -126,6 +126,9 @@ pub struct FuncMeta { pub trait_constraints: Vec, + /// The struct this function belongs to, if any + pub struct_id: Option, + /// The trait impl this function belongs to, if any pub trait_impl: Option, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs index 6777c0d1c79..91a374a0787 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/types.rs @@ -114,79 +114,6 @@ pub enum Type { Error, } -impl Type { - /// Returns the number of field elements required to represent the type once encoded. - pub fn field_count(&self) -> u32 { - match self { - Type::FieldElement | Type::Integer { .. } | Type::Bool => 1, - Type::Array(size, typ) => { - let length = size - .evaluate_to_u32() - .expect("Cannot have variable sized arrays as a parameter to main"); - let typ = typ.as_ref(); - length * typ.field_count() - } - Type::Struct(def, args) => { - let struct_type = def.borrow(); - let fields = struct_type.get_fields(args); - fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count()) - } - Type::Alias(def, generics) => def.borrow().get_type(generics).field_count(), - Type::Tuple(fields) => { - fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count()) - } - Type::String(size) => size - .evaluate_to_u32() - .expect("Cannot have variable sized strings as a parameter to main"), - Type::FmtString(_, _) - | Type::Unit - | Type::TypeVariable(_, _) - | Type::TraitAsType(..) - | Type::NamedGeneric(_, _, _) - | Type::Function(_, _, _) - | Type::MutableReference(_) - | Type::Forall(_, _) - | Type::Constant(_) - | Type::Quoted(_) - | Type::Slice(_) - | Type::Error => unreachable!("This type cannot exist as a parameter to main"), - } - } - - pub(crate) fn is_nested_slice(&self) -> bool { - match self { - Type::Slice(elem) => elem.as_ref().contains_slice(), - Type::Array(_, elem) => elem.as_ref().contains_slice(), - Type::Alias(alias, generics) => alias.borrow().get_type(generics).is_nested_slice(), - _ => false, - } - } - - pub(crate) fn contains_slice(&self) -> bool { - match self { - Type::Slice(_) => true, - Type::Struct(struct_typ, generics) => { - let fields = struct_typ.borrow().get_fields(generics); - for field in fields.iter() { - if field.1.contains_slice() { - return true; - } - } - false - } - Type::Tuple(types) => { - for typ in types.iter() { - if typ.contains_slice() { - return true; - } - } - false - } - _ => false, - } - } -} - /// A Kind is the type of a Type. These are used since only certain kinds of types are allowed in /// certain positions. /// @@ -215,6 +142,9 @@ pub enum QuotedType { TopLevelItem, Type, StructDefinition, + TraitDefinition, + FunctionDefinition, + Module, } /// A list of TypeVariableIds to bind to a type. Storing the @@ -342,6 +272,11 @@ impl StructType { vecmap(&self.fields, |(name, typ)| (name.0.contents.clone(), typ.clone())) } + /// Returns the field at the given index. Panics if no field exists at the given index. + pub fn field_at(&self, index: usize) -> &(Ident, Type) { + &self.fields[index] + } + pub fn field_names(&self) -> BTreeSet { self.fields.iter().map(|(name, _)| name.clone()).collect() } @@ -589,7 +524,7 @@ impl TypeVariable { /// TypeBindings are the mutable insides of a TypeVariable. /// They are either bound to some type, or are unbound. -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum TypeBinding { Bound(Type), Unbound(TypeVariableId), @@ -605,6 +540,155 @@ impl TypeBinding { #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct TypeVariableId(pub usize); +impl std::fmt::Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Type::FieldElement => { + write!(f, "Field") + } + Type::Array(len, typ) => { + write!(f, "[{typ}; {len}]") + } + Type::Slice(typ) => { + write!(f, "[{typ}]") + } + Type::Integer(sign, num_bits) => match sign { + Signedness::Signed => write!(f, "i{num_bits}"), + Signedness::Unsigned => write!(f, "u{num_bits}"), + }, + Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), + Type::TypeVariable(binding, TypeVariableKind::Integer) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + write!(f, "{}", Type::default_int_type()) + } else { + write!(f, "{}", binding.borrow()) + } + } + Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + // Show a Field by default if this TypeVariableKind::IntegerOrField is unbound, since that is + // what they bind to by default anyway. It is less confusing than displaying it + // as a generic. + write!(f, "Field") + } else { + write!(f, "{}", binding.borrow()) + } + } + Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => { + if let TypeBinding::Unbound(_) = &*binding.borrow() { + // TypeVariableKind::Constant(n) binds to Type::Constant(n) by default, so just show that. + write!(f, "{n}") + } else { + write!(f, "{}", binding.borrow()) + } + } + Type::Struct(s, args) => { + let args = vecmap(args, |arg| arg.to_string()); + if args.is_empty() { + write!(f, "{}", s.borrow()) + } else { + write!(f, "{}<{}>", s.borrow(), args.join(", ")) + } + } + Type::Alias(alias, args) => { + let args = vecmap(args, |arg| arg.to_string()); + if args.is_empty() { + write!(f, "{}", alias.borrow()) + } else { + write!(f, "{}<{}>", alias.borrow(), args.join(", ")) + } + } + Type::TraitAsType(_id, name, generics) => { + write!(f, "impl {}", name)?; + if !generics.is_empty() { + let generics = vecmap(generics, ToString::to_string).join(", "); + write!(f, "<{generics}>")?; + } + Ok(()) + } + Type::Tuple(elements) => { + let elements = vecmap(elements, ToString::to_string); + write!(f, "({})", elements.join(", ")) + } + Type::Bool => write!(f, "bool"), + Type::String(len) => write!(f, "str<{len}>"), + Type::FmtString(len, elements) => { + write!(f, "fmtstr<{len}, {elements}>") + } + Type::Unit => write!(f, "()"), + Type::Error => write!(f, "error"), + Type::NamedGeneric(binding, name, _) => match &*binding.borrow() { + TypeBinding::Bound(binding) => binding.fmt(f), + TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), + TypeBinding::Unbound(_) => write!(f, "{name}"), + }, + Type::Constant(x) => x.fmt(f), + Type::Forall(typevars, typ) => { + let typevars = vecmap(typevars, |var| var.id().to_string()); + write!(f, "forall {}. {}", typevars.join(" "), typ) + } + Type::Function(args, ret, env) => { + let closure_env_text = match **env { + Type::Unit => "".to_string(), + _ => format!(" with env {env}"), + }; + + let args = vecmap(args.iter(), ToString::to_string); + + write!(f, "fn({}) -> {ret}{closure_env_text}", args.join(", ")) + } + Type::MutableReference(element) => { + write!(f, "&mut {element}") + } + Type::Quoted(quoted) => write!(f, "{}", quoted), + } + } +} + +impl std::fmt::Display for BinaryTypeOperator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BinaryTypeOperator::Addition => write!(f, "+"), + BinaryTypeOperator::Subtraction => write!(f, "-"), + BinaryTypeOperator::Multiplication => write!(f, "*"), + BinaryTypeOperator::Division => write!(f, "/"), + BinaryTypeOperator::Modulo => write!(f, "%"), + } + } +} + +impl std::fmt::Display for TypeVariableId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "_") + } +} + +impl std::fmt::Display for TypeBinding { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TypeBinding::Bound(typ) => typ.fmt(f), + TypeBinding::Unbound(id) => id.fmt(f), + } + } +} + +impl std::fmt::Display for QuotedType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QuotedType::Expr => write!(f, "Expr"), + QuotedType::Quoted => write!(f, "Quoted"), + QuotedType::TopLevelItem => write!(f, "TopLevelItem"), + QuotedType::Type => write!(f, "Type"), + QuotedType::StructDefinition => write!(f, "StructDefinition"), + QuotedType::TraitDefinition => write!(f, "TraitDefinition"), + QuotedType::FunctionDefinition => write!(f, "FunctionDefinition"), + QuotedType::Module => write!(f, "Module"), + } + } +} + +pub struct UnificationError; + impl Type { pub fn default_int_or_field_type() -> Type { Type::FieldElement @@ -1039,155 +1123,78 @@ impl Type { // | Type::Error => Kind::Normal, // } // } -} -impl std::fmt::Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + /// Returns the number of field elements required to represent the type once encoded. + pub fn field_count(&self) -> u32 { match self { - Type::FieldElement => { - write!(f, "Field") - } - Type::Array(len, typ) => { - write!(f, "[{typ}; {len}]") - } - Type::Slice(typ) => { - write!(f, "[{typ}]") - } - Type::Integer(sign, num_bits) => match sign { - Signedness::Signed => write!(f, "i{num_bits}"), - Signedness::Unsigned => write!(f, "u{num_bits}"), - }, - Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), - Type::TypeVariable(binding, TypeVariableKind::Integer) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", Type::default_int_type()) - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - // Show a Field by default if this TypeVariableKind::IntegerOrField is unbound, since that is - // what they bind to by default anyway. It is less confusing than displaying it - // as a generic. - write!(f, "Field") - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => { - if let TypeBinding::Unbound(_) = &*binding.borrow() { - // TypeVariableKind::Constant(n) binds to Type::Constant(n) by default, so just show that. - write!(f, "{n}") - } else { - write!(f, "{}", binding.borrow()) - } - } - Type::Struct(s, args) => { - let args = vecmap(args, |arg| arg.to_string()); - if args.is_empty() { - write!(f, "{}", s.borrow()) - } else { - write!(f, "{}<{}>", s.borrow(), args.join(", ")) - } - } - Type::Alias(alias, args) => { - let args = vecmap(args, |arg| arg.to_string()); - if args.is_empty() { - write!(f, "{}", alias.borrow()) - } else { - write!(f, "{}<{}>", alias.borrow(), args.join(", ")) - } - } - Type::TraitAsType(_id, name, generics) => { - write!(f, "impl {}", name)?; - if !generics.is_empty() { - let generics = vecmap(generics, ToString::to_string).join(", "); - write!(f, "<{generics}>")?; - } - Ok(()) - } - Type::Tuple(elements) => { - let elements = vecmap(elements, ToString::to_string); - write!(f, "({})", elements.join(", ")) - } - Type::Bool => write!(f, "bool"), - Type::String(len) => write!(f, "str<{len}>"), - Type::FmtString(len, elements) => { - write!(f, "fmtstr<{len}, {elements}>") - } - Type::Unit => write!(f, "()"), - Type::Error => write!(f, "error"), - Type::NamedGeneric(binding, name, _) => match &*binding.borrow() { - TypeBinding::Bound(binding) => binding.fmt(f), - TypeBinding::Unbound(_) if name.is_empty() => write!(f, "_"), - TypeBinding::Unbound(_) => write!(f, "{name}"), - }, - Type::Constant(x) => x.fmt(f), - Type::Forall(typevars, typ) => { - let typevars = vecmap(typevars, |var| var.id().to_string()); - write!(f, "forall {}. {}", typevars.join(" "), typ) + Type::FieldElement | Type::Integer { .. } | Type::Bool => 1, + Type::Array(size, typ) => { + let length = size + .evaluate_to_u32() + .expect("Cannot have variable sized arrays as a parameter to main"); + let typ = typ.as_ref(); + length * typ.field_count() } - Type::Function(args, ret, env) => { - let closure_env_text = match **env { - Type::Unit => "".to_string(), - _ => format!(" with env {env}"), - }; - - let args = vecmap(args.iter(), ToString::to_string); - - write!(f, "fn({}) -> {ret}{closure_env_text}", args.join(", ")) + Type::Struct(def, args) => { + let struct_type = def.borrow(); + let fields = struct_type.get_fields(args); + fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count()) } - Type::MutableReference(element) => { - write!(f, "&mut {element}") + Type::Alias(def, generics) => def.borrow().get_type(generics).field_count(), + Type::Tuple(fields) => { + fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count()) } - Type::Quoted(quoted) => write!(f, "{}", quoted), - } - } -} - -impl std::fmt::Display for BinaryTypeOperator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - BinaryTypeOperator::Addition => write!(f, "+"), - BinaryTypeOperator::Subtraction => write!(f, "-"), - BinaryTypeOperator::Multiplication => write!(f, "*"), - BinaryTypeOperator::Division => write!(f, "/"), - BinaryTypeOperator::Modulo => write!(f, "%"), + Type::String(size) => size + .evaluate_to_u32() + .expect("Cannot have variable sized strings as a parameter to main"), + Type::FmtString(_, _) + | Type::Unit + | Type::TypeVariable(_, _) + | Type::TraitAsType(..) + | Type::NamedGeneric(_, _, _) + | Type::Function(_, _, _) + | Type::MutableReference(_) + | Type::Forall(_, _) + | Type::Constant(_) + | Type::Quoted(_) + | Type::Slice(_) + | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } } -} - -impl std::fmt::Display for TypeVariableId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "_") - } -} -impl std::fmt::Display for TypeBinding { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + pub(crate) fn is_nested_slice(&self) -> bool { match self { - TypeBinding::Bound(typ) => typ.fmt(f), - TypeBinding::Unbound(id) => id.fmt(f), + Type::Slice(elem) => elem.as_ref().contains_slice(), + Type::Array(_, elem) => elem.as_ref().contains_slice(), + Type::Alias(alias, generics) => alias.borrow().get_type(generics).is_nested_slice(), + _ => false, } } -} -impl std::fmt::Display for QuotedType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + pub(crate) fn contains_slice(&self) -> bool { match self { - QuotedType::Expr => write!(f, "Expr"), - QuotedType::Quoted => write!(f, "Quoted"), - QuotedType::TopLevelItem => write!(f, "TopLevelItem"), - QuotedType::Type => write!(f, "Type"), - QuotedType::StructDefinition => write!(f, "StructDefinition"), + Type::Slice(_) => true, + Type::Struct(struct_typ, generics) => { + let fields = struct_typ.borrow().get_fields(generics); + for field in fields.iter() { + if field.1.contains_slice() { + return true; + } + } + false + } + Type::Tuple(types) => { + for typ in types.iter() { + if typ.contains_slice() { + return true; + } + } + false + } + _ => false, } } -} -pub struct UnificationError; - -impl Type { /// Try to bind a MaybeConstant variable to self, succeeding if self is a Constant, /// MaybeConstant, or type variable. If successful, the binding is placed in the /// given TypeBindings map rather than linked immediately. @@ -2002,6 +2009,83 @@ impl Type { pub fn from_generics(generics: &GenericTypeVars) -> Vec { vecmap(generics, |var| Type::TypeVariable(var.clone(), TypeVariableKind::Normal)) } + + /// Replace any `Type::NamedGeneric` in this type with a `Type::TypeVariable` + /// using to the same inner `TypeVariable`. This is used during monomorphization + /// to bind to named generics since they are unbindable during type checking. + pub fn replace_named_generics_with_type_variables(&mut self) { + match self { + Type::FieldElement + | Type::Constant(_) + | Type::Integer(_, _) + | Type::Bool + | Type::Unit + | Type::Error + | Type::Quoted(_) => (), + + Type::Array(len, elem) => { + len.replace_named_generics_with_type_variables(); + elem.replace_named_generics_with_type_variables(); + } + + Type::Slice(elem) => elem.replace_named_generics_with_type_variables(), + Type::String(len) => len.replace_named_generics_with_type_variables(), + Type::FmtString(len, captures) => { + len.replace_named_generics_with_type_variables(); + captures.replace_named_generics_with_type_variables(); + } + Type::Tuple(fields) => { + for field in fields { + field.replace_named_generics_with_type_variables(); + } + } + Type::Struct(_, generics) => { + for generic in generics { + generic.replace_named_generics_with_type_variables(); + } + } + Type::Alias(alias, generics) => { + let mut typ = alias.borrow().get_type(generics); + typ.replace_named_generics_with_type_variables(); + *self = typ; + } + Type::TypeVariable(var, _) => { + let var = var.borrow(); + if let TypeBinding::Bound(binding) = &*var { + let mut binding = binding.clone(); + drop(var); + binding.replace_named_generics_with_type_variables(); + *self = binding; + } + } + Type::TraitAsType(_, _, generics) => { + for generic in generics { + generic.replace_named_generics_with_type_variables(); + } + } + Type::NamedGeneric(var, _, _) => { + let type_binding = var.borrow(); + if let TypeBinding::Bound(binding) = &*type_binding { + let mut binding = binding.clone(); + drop(type_binding); + binding.replace_named_generics_with_type_variables(); + *self = binding; + } else { + drop(type_binding); + *self = Type::TypeVariable(var.clone(), TypeVariableKind::Normal); + } + } + Type::Function(args, ret, env) => { + for arg in args { + arg.replace_named_generics_with_type_variables(); + } + ret.replace_named_generics_with_type_variables(); + env.replace_named_generics_with_type_variables(); + } + Type::MutableReference(elem) => elem.replace_named_generics_with_type_variables(), + Type::Forall(_, typ) => typ.replace_named_generics_with_type_variables(), + } + } } /// Wraps a given `expression` in `expression.as_slice()` @@ -2198,10 +2282,10 @@ impl std::fmt::Debug for Type { Type::Error => write!(f, "error"), Type::NamedGeneric(binding, name, kind) => match kind { Kind::Normal => { - write!(f, "{} -> {:?}", name, binding) + write!(f, "{}{:?}", name, binding) } Kind::Numeric(typ) => { - write!(f, "({} : {}) -> {:?}", name, typ, binding) + write!(f, "({} : {}){:?}", name, typ, binding) } }, Type::Constant(x) => x.fmt(f), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs index 41de13fb17e..59ae3d8fd7a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -895,12 +895,14 @@ pub enum Keyword { Fn, For, FormatString, + FunctionDefinition, Global, If, Impl, In, Let, Mod, + Module, Mut, Pub, Quoted, @@ -908,12 +910,13 @@ pub enum Keyword { ReturnData, String, Struct, + StructDefinition, Super, TopLevelItem, Trait, + TraitDefinition, Type, TypeType, - StructDefinition, Unchecked, Unconstrained, Use, @@ -943,12 +946,14 @@ impl fmt::Display for Keyword { Keyword::Fn => write!(f, "fn"), Keyword::For => write!(f, "for"), Keyword::FormatString => write!(f, "fmtstr"), + Keyword::FunctionDefinition => write!(f, "FunctionDefinition"), Keyword::Global => write!(f, "global"), Keyword::If => write!(f, "if"), Keyword::Impl => write!(f, "impl"), Keyword::In => write!(f, "in"), Keyword::Let => write!(f, "let"), Keyword::Mod => write!(f, "mod"), + Keyword::Module => write!(f, "Module"), Keyword::Mut => write!(f, "mut"), Keyword::Pub => write!(f, "pub"), Keyword::Quoted => write!(f, "Quoted"), @@ -956,12 +961,13 @@ impl fmt::Display for Keyword { Keyword::ReturnData => write!(f, "return_data"), Keyword::String => write!(f, "str"), Keyword::Struct => write!(f, "struct"), + Keyword::StructDefinition => write!(f, "StructDefinition"), Keyword::Super => write!(f, "super"), Keyword::TopLevelItem => write!(f, "TopLevelItem"), Keyword::Trait => write!(f, "trait"), + Keyword::TraitDefinition => write!(f, "TraitDefinition"), Keyword::Type => write!(f, "type"), Keyword::TypeType => write!(f, "Type"), - Keyword::StructDefinition => write!(f, "StructDefinition"), Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), Keyword::Use => write!(f, "use"), @@ -994,12 +1000,14 @@ impl Keyword { "fn" => Keyword::Fn, "for" => Keyword::For, "fmtstr" => Keyword::FormatString, + "FunctionDefinition" => Keyword::FunctionDefinition, "global" => Keyword::Global, "if" => Keyword::If, "impl" => Keyword::Impl, "in" => Keyword::In, "let" => Keyword::Let, "mod" => Keyword::Mod, + "Module" => Keyword::Module, "mut" => Keyword::Mut, "pub" => Keyword::Pub, "Quoted" => Keyword::Quoted, @@ -1010,6 +1018,7 @@ impl Keyword { "super" => Keyword::Super, "TopLevelItem" => Keyword::TopLevelItem, "trait" => Keyword::Trait, + "TraitDefinition" => Keyword::TraitDefinition, "type" => Keyword::Type, "Type" => Keyword::TypeType, "StructDefinition" => Keyword::StructDefinition, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index fcaef0a8dd6..0ba74e22781 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -3,7 +3,11 @@ use noirc_errors::Location; use rangemap::RangeMap; use rustc_hash::FxHashMap; -use crate::{macros_api::NodeInterner, node_interner::ReferenceId}; +use crate::{ + hir::def_map::{ModuleDefId, ModuleId}, + macros_api::{NodeInterner, StructId}, + node_interner::{DefinitionId, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId}, +}; use petgraph::prelude::NodeIndex as PetGraphIndex; #[derive(Debug, Default)] @@ -13,7 +17,7 @@ pub(crate) struct LocationIndices { impl LocationIndices { pub(crate) fn add_location(&mut self, location: Location, node_index: PetGraphIndex) { - // Some location spans are empty: maybe they are from ficticious nodes? + // Some location spans are empty: maybe they are from fictitious nodes? if location.span.start() == location.span.end() { return; } @@ -31,13 +35,18 @@ impl LocationIndices { impl NodeInterner { pub fn reference_location(&self, reference: ReferenceId) -> Location { match reference { - ReferenceId::Module(id) => self.module_location(&id), + ReferenceId::Module(id) => self.module_attributes(&id).location, ReferenceId::Function(id) => self.function_modifiers(&id).name_location, ReferenceId::Struct(id) => { let struct_type = self.get_struct(id); let struct_type = struct_type.borrow(); Location::new(struct_type.name.span(), struct_type.location.file) } + ReferenceId::StructMember(id, field_index) => { + let struct_type = self.get_struct(id); + let struct_type = struct_type.borrow(); + Location::new(struct_type.field_at(field_index).0.span(), struct_type.location.file) + } ReferenceId::Trait(id) => { let trait_type = self.get_trait(id); Location::new(trait_type.name.span(), trait_type.location.file) @@ -53,11 +62,97 @@ impl NodeInterner { } } - pub(crate) fn add_reference(&mut self, referenced: ReferenceId, reference: ReferenceId) { + pub fn reference_module(&self, reference: ReferenceId) -> Option<&ModuleId> { + self.reference_modules.get(&reference) + } + + pub(crate) fn add_module_def_id_reference( + &mut self, + def_id: ModuleDefId, + location: Location, + is_self_type: bool, + ) { + match def_id { + ModuleDefId::ModuleId(module_id) => { + self.add_module_reference(module_id, location); + } + ModuleDefId::FunctionId(func_id) => { + self.add_function_reference(func_id, location); + } + ModuleDefId::TypeId(struct_id) => { + self.add_struct_reference(struct_id, location, is_self_type); + } + ModuleDefId::TraitId(trait_id) => { + self.add_trait_reference(trait_id, location, is_self_type); + } + ModuleDefId::TypeAliasId(type_alias_id) => { + self.add_alias_reference(type_alias_id, location); + } + ModuleDefId::GlobalId(global_id) => { + self.add_global_reference(global_id, location); + } + }; + } + + pub(crate) fn add_module_reference(&mut self, id: ModuleId, location: Location) { + self.add_reference(ReferenceId::Module(id), location, false); + } + + pub(crate) fn add_struct_reference( + &mut self, + id: StructId, + location: Location, + is_self_type: bool, + ) { + self.add_reference(ReferenceId::Struct(id), location, is_self_type); + } + + pub(crate) fn add_struct_member_reference( + &mut self, + id: StructId, + member_index: usize, + location: Location, + ) { + self.add_reference(ReferenceId::StructMember(id, member_index), location, false); + } + + pub(crate) fn add_trait_reference( + &mut self, + id: TraitId, + location: Location, + is_self_type: bool, + ) { + self.add_reference(ReferenceId::Trait(id), location, is_self_type); + } + + pub(crate) fn add_alias_reference(&mut self, id: TypeAliasId, location: Location) { + self.add_reference(ReferenceId::Alias(id), location, false); + } + + pub(crate) fn add_function_reference(&mut self, id: FuncId, location: Location) { + self.add_reference(ReferenceId::Function(id), location, false); + } + + pub(crate) fn add_global_reference(&mut self, id: GlobalId, location: Location) { + self.add_reference(ReferenceId::Global(id), location, false); + } + + pub(crate) fn add_local_reference(&mut self, id: DefinitionId, location: Location) { + self.add_reference(ReferenceId::Local(id), location, false); + } + + pub(crate) fn add_reference( + &mut self, + referenced: ReferenceId, + location: Location, + is_self_type: bool, + ) { if !self.track_references { return; } + let reference = ReferenceId::Reference(location, is_self_type); + let referenced_index = self.get_or_insert_reference(referenced); let reference_location = self.reference_location(reference); let reference_index = self.reference_graph.add_node(reference); @@ -66,7 +161,11 @@ impl NodeInterner { self.location_indices.add_location(reference_location, reference_index); } - pub(crate) fn add_definition_location(&mut self, referenced: ReferenceId) { + pub(crate) fn add_definition_location( + &mut self, + referenced: ReferenceId, + module_id: Option, + ) { if !self.track_references { return; } @@ -74,6 +173,9 @@ impl NodeInterner { let referenced_index = self.get_or_insert_reference(referenced); let referenced_location = self.reference_location(referenced); self.location_indices.add_location(referenced_location, referenced_index); + if let Some(module_id) = module_id { + self.reference_modules.insert(referenced, module_id); + } } #[tracing::instrument(skip(self), ret)] @@ -105,7 +207,7 @@ impl NodeInterner { // Starting at the given location, find the node referenced by it. Then, gather // all locations that reference that node, and return all of them - // (the references and optionally the referenced node if `include_referencedd` is true). + // (the references and optionally the referenced node if `include_referenced` is true). // If `include_self_type_name` is true, references where "Self" is written are returned, // otherwise they are not. // Returns `None` if the location is not known to this interner. @@ -115,14 +217,8 @@ impl NodeInterner { include_referenced: bool, include_self_type_name: bool, ) -> Option> { - let node_index = self.location_indices.get_node_from_location(location)?; - - let reference_node = self.reference_graph[node_index]; - let referenced_node_index = if let ReferenceId::Reference(_, _) = reference_node { - self.referenced_index(node_index)? - } else { - node_index - }; + let referenced_node = self.find_referenced(location)?; + let referenced_node_index = self.reference_graph_indices[&referenced_node]; let found_locations = self.find_all_references_for_index( referenced_node_index, @@ -133,6 +229,19 @@ impl NodeInterner { Some(found_locations) } + // Returns the `ReferenceId` that is referenced by the given location, if any. + pub fn find_referenced(&self, location: Location) -> Option { + let node_index = self.location_indices.get_node_from_location(location)?; + + let reference_node = self.reference_graph[node_index]; + if let ReferenceId::Reference(_, _) = reference_node { + let node_index = self.referenced_index(node_index)?; + Some(self.reference_graph[node_index]) + } else { + Some(reference_node) + } + } + // Given a referenced node index, find all references to it and return their locations, optionally together // with the reference node's location if `include_referenced` is true. // If `include_self_type_name` is true, references where "Self" is written are returned, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs index fabfc74b901..a46f32e3094 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -21,7 +21,7 @@ use crate::{ types, }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, - Type, TypeBinding, TypeBindings, TypeVariable, TypeVariableKind, + Type, TypeBinding, TypeBindings, }; use acvm::{acir::AcirField, FieldElement}; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -444,13 +444,26 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Block(block) => self.block(block.statements)?, HirExpression::Prefix(prefix) => { + let rhs = self.expr(prefix.rhs)?; let location = self.interner.expr_location(&expr); - 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), location)?, - location, - }) + + if self.interner.get_selected_impl_for_expression(expr).is_some() { + // If an impl was selected for this prefix operator, replace it + // with a method call to the appropriate trait impl method. + let (function_type, ret) = + self.interner.get_prefix_operator_type(expr, prefix.rhs); + + let method = prefix + .trait_method_id + .expect("ice: missing trait method if when impl was found"); + let func = self.resolve_trait_method_expr(expr, function_type, method)?; + self.create_prefix_operator_impl_call(func, rhs, ret, location)? + } else { + let operator = prefix.operator; + let rhs = Box::new(rhs); + let result_type = Self::convert_type(&self.interner.id_type(expr), location)?; + ast::Expression::Unary(ast::Unary { operator, rhs, result_type, location }) + } } HirExpression::Infix(infix) => { @@ -462,11 +475,12 @@ impl<'interner> Monomorphizer<'interner> { // If an impl was selected for this infix operator, replace it // with a method call to the appropriate trait impl method. let (function_type, ret) = - self.interner.get_operator_type(infix.lhs, operator, expr); + self.interner.get_infix_operator_type(infix.lhs, operator, expr); let method = infix.trait_method_id; let func = self.resolve_trait_method_expr(expr, function_type, method)?; - self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location)? + let operator = infix.operator; + self.create_infix_operator_impl_call(func, lhs, operator, rhs, ret, location)? } else { let lhs = Box::new(lhs); let rhs = Box::new(rhs); @@ -1651,12 +1665,12 @@ impl<'interner> Monomorphizer<'interner> { }) } - /// Call an operator overloading method for the given operator. + /// Call an infix operator overloading method for the given operator. /// This function handles the special cases some operators have which don't map /// 1 to 1 onto their operator function. For example: != requires a negation on /// the result of its `eq` method, and the comparison operators each require a /// conversion from the `Ordering` result to a boolean. - fn create_operator_impl_call( + fn create_infix_operator_impl_call( &self, func: ast::Expression, lhs: ast::Expression, @@ -1715,6 +1729,21 @@ impl<'interner> Monomorphizer<'interner> { Ok(result) } + + /// Call an operator overloading method for the given prefix operator. + fn create_prefix_operator_impl_call( + &self, + func: ast::Expression, + rhs: ast::Expression, + ret: Type, + location: Location, + ) -> Result { + let arguments = vec![rhs]; + let func = Box::new(func); + let return_type = Self::convert_type(&ret, location)?; + + Ok(ast::Expression::Call(ast::Call { func, arguments, return_type, location })) + } } fn unwrap_tuple_type(typ: &HirType) -> Vec { @@ -1759,24 +1788,21 @@ pub fn perform_impl_bindings( if let Some(trait_method) = trait_method { let the_trait = interner.get_trait(trait_method.trait_id); - let trait_method_type = the_trait.methods[trait_method.method_index].typ.as_monotype(); + let mut trait_method_type = + the_trait.methods[trait_method.method_index].typ.as_monotype().clone(); + + let mut impl_method_type = + interner.function_meta(&impl_method).typ.unwrap_forall().1.clone(); // Make each NamedGeneric in this type bindable by replacing it with a TypeVariable // with the same internal id and binding. - let (generics, impl_method_type) = interner.function_meta(&impl_method).typ.unwrap_forall(); - - let replace_type_variable = |var: &TypeVariable| { - (var.id(), (var.clone(), Type::TypeVariable(var.clone(), TypeVariableKind::Normal))) - }; - - // Replace each NamedGeneric with a TypeVariable containing the same internal type variable - let type_bindings = generics.iter().map(replace_type_variable).collect(); - let impl_method_type = impl_method_type.force_substitute(&type_bindings); + trait_method_type.replace_named_generics_with_type_variables(); + impl_method_type.replace_named_generics_with_type_variables(); trait_method_type.try_unify(&impl_method_type, &mut bindings).map_err(|_| { InterpreterError::ImplMethodTypeMismatch { - expected: trait_method_type.clone(), - actual: impl_method_type, + expected: trait_method_type.follow_bindings(), + actual: impl_method_type.follow_bindings(), location, } })?; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs index 588b56afa1a..e534f1bab29 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fmt; use std::hash::Hash; +use std::marker::Copy; use std::ops::Deref; use fm::FileId; @@ -18,6 +19,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::macros_api::UnaryOp; use crate::QuotedType; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; @@ -42,6 +44,13 @@ use crate::{Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeV /// This is needed to stop recursing for cases such as `impl Foo for T where T: Eq` const IMPL_SEARCH_RECURSION_LIMIT: u32 = 10; +#[derive(Debug)] +pub struct ModuleAttributes { + pub name: String, + pub location: Location, + pub parent: LocalModuleId, +} + type StructAttributes = Vec; /// The node interner is the central storage location of all nodes in Noir's Hir (the @@ -66,7 +75,7 @@ pub struct NodeInterner { function_modules: HashMap, // The location of each module - module_locations: HashMap, + module_attributes: HashMap, /// This graph tracks dependencies between different global definitions. /// This is used to ensure the absence of dependency cycles for globals and types. @@ -139,8 +148,11 @@ pub struct NodeInterner { /// the context to get the concrete type of the object and select the correct impl itself. selected_trait_implementations: HashMap, - /// Holds the trait ids of the traits used for operator overloading - operator_traits: HashMap, + /// Holds the trait ids of the traits used for infix operator overloading + infix_operator_traits: HashMap, + + /// Holds the trait ids of the traits used for prefix operator overloading + prefix_operator_traits: HashMap, /// The `Ordering` type is a semi-builtin type that is the result of the comparison traits. ordering_type: Option, @@ -195,7 +207,7 @@ pub struct NodeInterner { /// Edges are directed from reference nodes to referenced nodes. /// For example: /// - /// ``` + /// ```text /// let foo = 3; /// // referenced /// // ^ @@ -214,6 +226,10 @@ pub struct NodeInterner { /// Store the location of the references in the graph pub(crate) location_indices: LocationIndices, + + // The module where each reference is + // (ReferenceId::Reference and ReferenceId::Local aren't included here) + pub(crate) reference_modules: HashMap, } /// A dependency in the dependency graph may be a type or a definition. @@ -241,6 +257,7 @@ pub enum DependencyId { pub enum ReferenceId { Module(ModuleId), Struct(StructId), + StructMember(StructId, usize), Trait(TraitId), Global(GlobalId), Function(FuncId), @@ -547,7 +564,7 @@ impl Default for NodeInterner { function_definition_ids: HashMap::new(), function_modifiers: HashMap::new(), function_modules: HashMap::new(), - module_locations: HashMap::new(), + module_attributes: HashMap::new(), func_id_to_trait: HashMap::new(), dependency_graph: petgraph::graph::DiGraph::new(), dependency_graph_indices: HashMap::new(), @@ -563,7 +580,8 @@ impl Default for NodeInterner { next_trait_implementation_id: 0, trait_implementation_map: HashMap::new(), selected_trait_implementations: HashMap::new(), - operator_traits: HashMap::new(), + infix_operator_traits: HashMap::new(), + prefix_operator_traits: HashMap::new(), ordering_type: None, instantiation_bindings: HashMap::new(), field_indices: HashMap::new(), @@ -579,6 +597,7 @@ impl Default for NodeInterner { location_indices: LocationIndices::default(), reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::new(), + reference_modules: HashMap::new(), }; // An empty block expression is used often, we add this into the `node` on startup @@ -847,7 +866,7 @@ impl NodeInterner { self.definitions.push(DefinitionInfo { name, mutable, comptime, kind, location }); if is_local { - self.add_definition_location(ReferenceId::Local(id)); + self.add_definition_location(ReferenceId::Local(id), None); } id @@ -885,7 +904,7 @@ impl NodeInterner { // This needs to be done after pushing the definition since it will reference the // location that was stored - self.add_definition_location(ReferenceId::Function(id)); + self.add_definition_location(ReferenceId::Function(id), Some(module)); definition_id } @@ -986,12 +1005,16 @@ impl NodeInterner { &self.struct_attributes[struct_id] } - pub fn add_module_location(&mut self, module_id: ModuleId, location: Location) { - self.module_locations.insert(module_id, location); + pub fn add_module_attributes(&mut self, module_id: ModuleId, attributes: ModuleAttributes) { + self.module_attributes.insert(module_id, attributes); } - pub fn module_location(&self, module_id: &ModuleId) -> Location { - self.module_locations[module_id] + pub fn module_attributes(&self, module_id: &ModuleId) -> &ModuleAttributes { + &self.module_attributes[module_id] + } + + pub fn try_module_attributes(&self, module_id: &ModuleId) -> Option<&ModuleAttributes> { + self.module_attributes.get(module_id) } pub fn global_attributes(&self, global_id: &GlobalId) -> &[SecondaryAttribute] { @@ -1071,6 +1094,10 @@ impl NodeInterner { self.id_location(expr_id).span } + pub fn try_expr_span(&self, expr_id: &ExprId) -> Option { + self.try_id_location(expr_id).map(|location| location.span) + } + pub fn expr_location(&self, expr_id: &ExprId) -> Location { self.id_location(expr_id) } @@ -1175,8 +1202,14 @@ impl NodeInterner { } /// Returns the span of an item stored in the Interner - pub fn id_location(&self, index: impl Into) -> Location { - self.id_to_location.get(&index.into()).copied().unwrap() + pub fn id_location(&self, index: impl Into + Copy) -> Location { + self.try_id_location(index) + .unwrap_or_else(|| panic!("ID is missing a source location: {:?}", index.into())) + } + + /// Returns the span of an item stored in the Interner, if present + pub fn try_id_location(&self, index: impl Into) -> Option { + self.id_to_location.get(&index.into()).copied() } /// Replaces the HirExpression at the given ExprId with a new HirExpression @@ -1665,18 +1698,29 @@ impl NodeInterner { /// Retrieves the trait id for a given binary operator. /// All binary operators correspond to a trait - although multiple may correspond /// to the same trait (such as `==` and `!=`). - /// `self.operator_traits` is expected to be filled before name resolution, + /// `self.infix_operator_traits` is expected to be filled before name resolution, /// during definition collection. pub fn get_operator_trait_method(&self, operator: BinaryOpKind) -> TraitMethodId { - let trait_id = self.operator_traits[&operator]; + let trait_id = self.infix_operator_traits[&operator]; // Assume that the operator's method to be overloaded is the first method of the trait. TraitMethodId { trait_id, method_index: 0 } } + /// Retrieves the trait id for a given unary operator. + /// Only some unary operators correspond to a trait: `-` and `!`, but for example `*` does not. + /// `self.prefix_operator_traits` is expected to be filled before name resolution, + /// during definition collection. + pub fn get_prefix_operator_trait_method(&self, operator: &UnaryOp) -> Option { + let trait_id = self.prefix_operator_traits.get(operator)?; + + // Assume that the operator's method to be overloaded is the first method of the trait. + Some(TraitMethodId { trait_id: *trait_id, method_index: 0 }) + } + /// Add the given trait as an operator trait if its name matches one of the /// operator trait names (Add, Sub, ...). - pub fn try_add_operator_trait(&mut self, trait_id: TraitId) { + pub fn try_add_infix_operator_trait(&mut self, trait_id: TraitId) { let the_trait = self.get_trait(trait_id); let operator = match the_trait.name.0.contents.as_str() { @@ -1695,17 +1739,17 @@ impl NodeInterner { _ => return, }; - self.operator_traits.insert(operator, trait_id); + self.infix_operator_traits.insert(operator, trait_id); // Some operators also require we insert a matching entry for related operators match operator { BinaryOpKind::Equal => { - self.operator_traits.insert(BinaryOpKind::NotEqual, trait_id); + self.infix_operator_traits.insert(BinaryOpKind::NotEqual, trait_id); } BinaryOpKind::Less => { - self.operator_traits.insert(BinaryOpKind::LessEqual, trait_id); - self.operator_traits.insert(BinaryOpKind::Greater, trait_id); - self.operator_traits.insert(BinaryOpKind::GreaterEqual, trait_id); + self.infix_operator_traits.insert(BinaryOpKind::LessEqual, trait_id); + self.infix_operator_traits.insert(BinaryOpKind::Greater, trait_id); + self.infix_operator_traits.insert(BinaryOpKind::GreaterEqual, trait_id); let the_trait = self.get_trait(trait_id); self.ordering_type = match &the_trait.methods[0].typ { @@ -1720,27 +1764,43 @@ impl NodeInterner { } } + /// Add the given trait as an operator trait if its name matches one of the + /// prefix operator trait names (Not or Neg). + pub fn try_add_prefix_operator_trait(&mut self, trait_id: TraitId) { + let the_trait = self.get_trait(trait_id); + + let operator = match the_trait.name.0.contents.as_str() { + "Neg" => UnaryOp::Minus, + "Not" => UnaryOp::Not, + _ => return, + }; + + self.prefix_operator_traits.insert(operator, trait_id); + } + /// This function is needed when creating a NodeInterner for testing so that calls /// to `get_operator_trait` do not panic when the stdlib isn't present. #[cfg(test)] pub fn populate_dummy_operator_traits(&mut self) { let dummy_trait = TraitId(ModuleId::dummy_id()); - self.operator_traits.insert(BinaryOpKind::Add, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Subtract, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Multiply, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Divide, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Modulo, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Equal, dummy_trait); - self.operator_traits.insert(BinaryOpKind::NotEqual, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Less, dummy_trait); - self.operator_traits.insert(BinaryOpKind::LessEqual, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Greater, dummy_trait); - self.operator_traits.insert(BinaryOpKind::GreaterEqual, dummy_trait); - self.operator_traits.insert(BinaryOpKind::And, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Or, dummy_trait); - self.operator_traits.insert(BinaryOpKind::Xor, dummy_trait); - self.operator_traits.insert(BinaryOpKind::ShiftLeft, dummy_trait); - self.operator_traits.insert(BinaryOpKind::ShiftRight, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Add, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Subtract, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Multiply, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Divide, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Modulo, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Equal, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::NotEqual, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Less, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::LessEqual, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Greater, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::GreaterEqual, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::And, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Or, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::Xor, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::ShiftLeft, dummy_trait); + self.infix_operator_traits.insert(BinaryOpKind::ShiftRight, dummy_trait); + self.prefix_operator_traits.insert(UnaryOp::Minus, dummy_trait); + self.prefix_operator_traits.insert(UnaryOp::Not, dummy_trait); } pub(crate) fn ordering_type(&self) -> Type { @@ -1873,7 +1933,7 @@ impl NodeInterner { } /// Returns the type of an operator (which is always a function), along with its return type. - pub fn get_operator_type( + pub fn get_infix_operator_type( &self, lhs: ExprId, operator: BinaryOpKind, @@ -1894,6 +1954,15 @@ impl NodeInterner { let env = Box::new(Type::Unit); (Type::Function(args, Box::new(ret.clone()), env), ret) } + + /// Returns the type of a prefix operator (which is always a function), along with its return type. + pub fn get_prefix_operator_type(&self, operator_expr: ExprId, rhs: ExprId) -> (Type, Type) { + let rhs_type = self.id_type(rhs); + let args = vec![rhs_type]; + let ret = self.id_type(operator_expr); + let env = Box::new(Type::Unit); + (Type::Function(args, Box::new(ret.clone()), env), ret) + } } impl Methods { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs index 1aec57c8e41..e64de584da4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -1,5 +1,6 @@ use chumsky::prelude::*; +use super::attributes::{attributes, validate_secondary_attributes}; use super::function::function_return_type; use super::{block, expression, fresh_statement, function, function_declaration_parameters}; @@ -18,15 +19,24 @@ use crate::{ use super::{generic_type_args, parse_type, path, primitives::ident}; pub(super) fn trait_definition() -> impl NoirParser { - keyword(Keyword::Trait) - .ignore_then(ident()) + attributes() + .then_ignore(keyword(Keyword::Trait)) + .then(ident()) .then(function::generics()) .then(where_clause()) .then_ignore(just(Token::LeftBrace)) .then(trait_body()) .then_ignore(just(Token::RightBrace)) - .map_with_span(|(((name, generics), where_clause), items), span| { - TopLevelStatement::Trait(NoirTrait { name, generics, where_clause, span, items }) + .validate(|((((attributes, name), generics), where_clause), items), span, emit| { + let attributes = validate_secondary_attributes(attributes, span, emit); + TopLevelStatement::Trait(NoirTrait { + name, + generics, + where_clause, + span, + items, + attributes, + }) }) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs index 32929312d54..7b69b309bae 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -26,6 +26,9 @@ pub(super) fn parse_type_inner<'a>( string_type(), expr_type(), struct_definition_type(), + trait_definition_type(), + function_definition_type(), + module_type(), top_level_item_type(), type_of_quoted_types(), quoted_type(), @@ -54,15 +57,7 @@ pub(super) fn parenthesized_type( } pub(super) fn maybe_comp_time() -> impl NoirParser { - keyword(Keyword::Comptime).or_not().validate(|opt, span, emit| { - if opt.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Comptime values"), - span, - )); - } - opt.is_some() - }) + keyword(Keyword::Comptime).or_not().map(|opt| opt.is_some()) } pub(super) fn field_type() -> impl NoirParser { @@ -87,6 +82,23 @@ pub(super) fn struct_definition_type() -> impl NoirParser { }) } +pub(super) fn trait_definition_type() -> impl NoirParser { + keyword(Keyword::TraitDefinition).map_with_span(|_, span| { + UnresolvedTypeData::Quoted(QuotedType::TraitDefinition).with_span(span) + }) +} + +pub(super) fn function_definition_type() -> impl NoirParser { + keyword(Keyword::FunctionDefinition).map_with_span(|_, span| { + UnresolvedTypeData::Quoted(QuotedType::FunctionDefinition).with_span(span) + }) +} + +pub(super) fn module_type() -> impl NoirParser { + keyword(Keyword::Module) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Module).with_span(span)) +} + /// This is the type `TopLevelItem` - the type of a quoted statement in the top level. /// E.g. a type definition, trait definition, trait impl, function, etc. fn top_level_item_type() -> impl NoirParser { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index 70f8f785d68..8cedeeeff0d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -86,7 +86,8 @@ pub(crate) fn get_program( program.clone().into_sorted(), root_file_id, use_legacy, - &[], // No macro processors + None, // No debug_comptime_in_file + &[], // No macro processors )); } (program, context, errors) diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index 2a9bfb4b544..689b72435ef 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -206,6 +206,8 @@ "unoptimized", "urem", "USERPROFILE", + "vararg", + "varargs", "vecmap", "vitkov", "wasi", diff --git a/noir/noir-repo/docs/docs/explainers/explainer-oracle.md b/noir/noir-repo/docs/docs/explainers/explainer-oracle.md index b84ca5dd986..821e1f95c04 100644 --- a/noir/noir-repo/docs/docs/explainers/explainer-oracle.md +++ b/noir/noir-repo/docs/docs/explainers/explainer-oracle.md @@ -31,7 +31,7 @@ In short, anything that can be constrained in a Noir program but needs to be fet Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have an oracle call like this: ```rust #[oracle(getNoun)] @@ -52,6 +52,6 @@ If you don't constrain the return of your oracle, you could be clearly opening a On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they match the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/noir-repo/docs/docs/how_to/how-to-oracles.md b/noir/noir-repo/docs/docs/how_to/how-to-oracles.md index df41276cfe1..2f69902062c 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-oracles.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-oracles.md @@ -46,7 +46,7 @@ unconstrained fn get_sqrt(number: Field) -> Field { } ``` -In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: +In this example, we're wrapping our oracle function in an unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: ```rust fn main(input: Field) { @@ -234,7 +234,7 @@ const client = new JSONRPCClient((jsonRPCRequest) => { // declaring a function that takes the name of the foreign call (getSqrt) and the inputs const foreignCallHandler = async (name, input) => { // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + // in this case we make the RPC request with the first parameter "numbers", which would be input[0] const oracleReturn = await client.request(name, [ input[0].map((i) => i.toString("hex")), ]); diff --git a/noir/noir-repo/docs/docs/how_to/how-to-recursion.md b/noir/noir-repo/docs/docs/how_to/how-to-recursion.md index aac84e29fac..71f02fa5435 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-recursion.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-recursion.md @@ -47,7 +47,7 @@ In a standard recursive app, you're also dealing with at least two circuits. For - `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. - `recursive`: a circuit that verifies `main` -For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. +For a full example of how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. ## Step 1: Setup diff --git a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md index e6ed9abaec6..c800d91ac69 100644 --- a/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md +++ b/noir/noir-repo/docs/docs/how_to/how-to-solidity-verifier.md @@ -40,7 +40,7 @@ Generating a Solidity Verifier contract is actually a one-command process. Howev ## Step 1 - Generate a contract -This is by far the most straight-forward step. Just run: +This is by far the most straightforward step. Just run: ```sh nargo compile @@ -99,7 +99,7 @@ This time we will see a warning about an unused function parameter. This is expe ## Step 3 - Deploying -At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. +At this point we should have a compiled contract ready to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: @@ -111,7 +111,7 @@ Remix will take care of the dependencies for us so we can simply deploy the Ultr ![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking that the deployer contract is the correct one. :::note diff --git a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr index c218ecd2348..a56d6f60390 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr @@ -14,7 +14,7 @@ impl BoundedVec { /// Get an element from the vector at the given index. /// Panics if the given index points beyond the end of the vector (`self.len()`). pub fn get(self, index: u32) -> T { - assert(index < self.len); + assert(index < self.len, "Attempted to read past end of BoundedVec"); self.get_unchecked(index) } @@ -152,25 +152,16 @@ impl From<[T; Len]> for BoundedVec } mod bounded_vec_tests { - // TODO: Allow imports from "super" - use crate::collections::bounded_vec::BoundedVec; - #[test] - fn empty_equality() { - let mut bounded_vec1: BoundedVec = BoundedVec::new(); - let mut bounded_vec2: BoundedVec = BoundedVec::new(); - - assert_eq(bounded_vec1, bounded_vec2); - } + mod get { + use crate::collections::bounded_vec::BoundedVec; - #[test] - fn inequality() { - let mut bounded_vec1: BoundedVec = BoundedVec::new(); - let mut bounded_vec2: BoundedVec = BoundedVec::new(); - bounded_vec1.push(1); - bounded_vec2.push(2); + #[test(should_fail_with = "Attempted to read past end of BoundedVec")] + fn panics_when_reading_elements_past_end_of_vec() { + let vec: BoundedVec = BoundedVec::new(); - assert(bounded_vec1 != bounded_vec2); + crate::println(vec.get(0)); + } } mod set { @@ -295,4 +286,26 @@ mod bounded_vec_tests { assert_eq(bounded_vec.storage()[1], 2); } } + + mod trait_eq { + use crate::collections::bounded_vec::BoundedVec; + + #[test] + fn empty_equality() { + let mut bounded_vec1: BoundedVec = BoundedVec::new(); + let mut bounded_vec2: BoundedVec = BoundedVec::new(); + + assert_eq(bounded_vec1, bounded_vec2); + } + + #[test] + fn inequality() { + let mut bounded_vec1: BoundedVec = BoundedVec::new(); + let mut bounded_vec2: BoundedVec = BoundedVec::new(); + bounded_vec1.push(1); + bounded_vec2.push(2); + + assert(bounded_vec1 != bounded_vec2); + } + } } diff --git a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr index c791ac88404..f54072d8cbd 100644 --- a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr @@ -1,6 +1,9 @@ use crate::ops::arith::{Add, Sub, Neg}; use crate::cmp::Eq; +/// A point on the embedded elliptic curve +/// By definition, the base field of the embedded curve is the scalar field of the proof system curve, i.e the Noir Field. +/// x and y denotes the Weierstrass coordinates of the point, if is_infinite is false. struct EmbeddedCurvePoint { x: Field, y: Field, @@ -8,32 +11,35 @@ struct EmbeddedCurvePoint { } impl EmbeddedCurvePoint { - fn new(x: Field, y: Field, is_infinite: bool) -> Self { - Self { x, y, is_infinite } - } - + /// Elliptic curve point doubling operation + /// returns the doubled point of a point P, i.e P+P fn double(self) -> EmbeddedCurvePoint { embedded_curve_add(self, self) } + /// Returns the null element of the curve; 'the point at infinity' fn point_at_infinity() -> EmbeddedCurvePoint { EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true } } } impl Add for EmbeddedCurvePoint { + /// Adds two points P+Q, using the curve addition formula, and also handles point at infinity fn add(self, other: EmbeddedCurvePoint) -> EmbeddedCurvePoint { embedded_curve_add(self, other) } } impl Sub for EmbeddedCurvePoint { + /// Points subtraction operation, using addition and negation fn sub(self, other: EmbeddedCurvePoint) -> EmbeddedCurvePoint { self + other.neg() } } impl Neg for EmbeddedCurvePoint { + /// Negates a point P, i.e returns -P, by negating the y coordinate. + /// If the point is at infinity, then the result is also at infinity. fn neg(self) -> EmbeddedCurvePoint { EmbeddedCurvePoint { x: self.x, @@ -44,12 +50,15 @@ impl Neg for EmbeddedCurvePoint { } impl Eq for EmbeddedCurvePoint { + /// Checks whether two points are equal fn eq(self: Self, b: EmbeddedCurvePoint) -> bool { (self.is_infinite & b.is_infinite) | ((self.is_infinite == b.is_infinite) & (self.x == b.x) & (self.y == b.y)) } } -// Scalar represented as low and high limbs +/// Scalar for the embedded curve represented as low and high limbs +/// By definition, the scalar field of the embedded curve is base field of the proving system curve. +/// It may not fit into a Field element, so it is represented with two Field elements; its low and high limbs. struct EmbeddedCurveScalar { lo: Field, hi: Field, @@ -60,11 +69,6 @@ impl EmbeddedCurveScalar { EmbeddedCurveScalar { lo, hi } } - pub fn derive_public_key(self) -> EmbeddedCurvePoint { - let public_key = fixed_base_scalar_mul(self.lo, self.hi); - EmbeddedCurvePoint { x: public_key[0], y: public_key[1], is_infinite: false } - } - #[field(bn254)] fn from_field(scalar: Field) -> EmbeddedCurveScalar { let (a,b) = crate::field::bn254::decompose(scalar); @@ -73,8 +77,8 @@ impl EmbeddedCurveScalar { } impl Eq for EmbeddedCurveScalar { - fn eq(self, key: EmbeddedCurveScalar) -> bool { - (key.hi == self.hi) & (key.lo == self.lo) + fn eq(self, other: Self) -> bool { + (other.hi == self.hi) & (other.lo == self.lo) } } diff --git a/noir/noir-repo/noir_stdlib/src/hash/keccak.nr b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr new file mode 100644 index 00000000000..7623f5a2256 --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/hash/keccak.nr @@ -0,0 +1,122 @@ +global LIMBS_PER_BLOCK = 17; //BLOCK_SIZE / 8; +global NUM_KECCAK_LANES = 25; +global BLOCK_SIZE = 136; //(1600 - BITS * 2) / WORD_SIZE; +global WORD_SIZE = 8; + +use crate::collections::bounded_vec::BoundedVec; + +#[foreign(keccakf1600)] +fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} + +fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { + // No var keccak for now + assert(N == message_size); + + //1. format_input_lanes + let max_blocks = (N + BLOCK_SIZE) / BLOCK_SIZE; + //maximum number of bytes to hash + let max_blocks_length = (BLOCK_SIZE * (max_blocks)); + assert(max_blocks_length < 1000); + let mut block_bytes :BoundedVec = BoundedVec::from_array(input); + for i in N..N + BLOCK_SIZE { + let data = if i == N { + 1 + } else if i == max_blocks_length - 1 { + 0x80 + } else { + 0 + }; + block_bytes.push(data); + } + + // keccak lanes interpret memory as little-endian integers, + // means we need to swap our byte ordering + let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE; + for i in 0..num_limbs { + let mut temp = [0; 8]; + + for j in 0..8 { + temp[j] = block_bytes.get(8*i+j); + } + for j in 0..8 { + block_bytes.set(8 * i + j, temp[7 - j]); + } + } + let byte_size = max_blocks_length; + assert(num_limbs < 1000); + let mut sliced_buffer = [0; 1000]; + // populate a vector of 64-bit limbs from our byte array + for i in 0..num_limbs { + let mut sliced = 0; + if (i * WORD_SIZE + WORD_SIZE > byte_size) { + let slice_size = byte_size - (i * WORD_SIZE); + let byte_shift = (WORD_SIZE - slice_size) * 8; + let mut v = 1; + for k in 0..slice_size { + sliced += v * (block_bytes.get(i * WORD_SIZE+7-k) as Field); + v *= 256; + } + let w = 1 << (byte_shift as u8); + sliced *= w as Field; + } else { + let mut v = 1; + for k in 0..WORD_SIZE { + sliced += v * (block_bytes.get(i * WORD_SIZE+7-k) as Field); + v *= 256; + } + } + sliced_buffer[i] = sliced as u64; + } + + //2. sponge_absorb + let num_blocks = max_blocks; + let mut state : [u64;NUM_KECCAK_LANES]= [0; NUM_KECCAK_LANES]; + + for i in 0..num_blocks { + if (i == 0) { + for j in 0..LIMBS_PER_BLOCK { + state[j] = sliced_buffer[j]; + } + } else { + for j in 0..LIMBS_PER_BLOCK { + state[j] = state[j] ^ sliced_buffer[i * LIMBS_PER_BLOCK + j]; + } + } + state = keccakf1600(state); + } + + //3. sponge_squeeze + let mut result = [0; 32]; + for i in 0..4 { + let lane = state[i] as Field; + let lane_le = lane.to_le_bytes(8); + for j in 0..8 { + result[8*i+j] = lane_le[j]; + } + } + result +} + +mod tests { + use crate::hash::keccak::keccak256; + + #[test] + fn smoke_test() { + let input = [0xbd]; + let result = [ + 0x5a, 0x50, 0x2f, 0x9f, 0xca, 0x46, 0x7b, 0x26, 0x6d, 0x5b, 0x78, 0x33, 0x65, 0x19, 0x37, 0xe8, 0x05, 0x27, 0x0c, 0xa3, 0xf3, 0xaf, 0x1c, 0x0d, 0xd2, 0x46, 0x2d, 0xca, 0x4b, 0x3b, 0x1a, 0xbf + ]; + assert_eq(keccak256(input, input.len()), result); + } + + #[test] + fn hash_hello_world() { + // "hello world" + let input = [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33]; + let result = [ + 0xec, 0xd0, 0xe1, 0x8, 0xa9, 0x8e, 0x19, 0x2a, 0xf1, 0xd2, 0xc2, 0x50, 0x55, 0xf4, 0xe3, 0xbe, 0xd7, 0x84, 0xb5, 0xc8, 0x77, 0x20, 0x4e, 0x73, 0x21, 0x9a, 0x52, 0x3, 0x25, 0x1f, 0xea, 0xab + ]; + assert_eq(keccak256(input, input.len()), result); + } +} + diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index 65f3b9419ff..42555348949 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -1,6 +1,7 @@ mod poseidon; mod mimc; mod poseidon2; +mod keccak; use crate::default::Default; use crate::uint128::U128; @@ -61,10 +62,7 @@ pub fn pedersen_hash(input: [Field; N]) -> Field } #[field(bn254)] -fn derive_generators( - domain_separator_bytes: [u8; M], - starting_index: u32 -) -> [EmbeddedCurvePoint; N] { +fn derive_generators(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] { crate::assert_constant(domain_separator_bytes); crate::assert_constant(starting_index); __derive_generators(domain_separator_bytes, starting_index) @@ -72,7 +70,10 @@ fn derive_generators( #[builtin(derive_pedersen_generators)] #[field(bn254)] -fn __derive_generators(domain_separator_bytes: [u8; M], starting_index: u32) -> [EmbeddedCurvePoint; N] {} +fn __derive_generators( + domain_separator_bytes: [u8; M], + starting_index: u32 +) -> [EmbeddedCurvePoint; N] {} fn pedersen_hash_with_separator_noir(input: [Field; N], separator: u32) -> Field { let v1 = pedersen_commitment_with_separator(input, separator); diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 1825888130b..ad8ee4f8586 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -1 +1,8 @@ mod type_def; + +/// Calling unquote as a macro (via `unquote!(arg)`) will unquote +/// its argument. Since this is the effect `!` already does, `unquote` +/// itself does not need to do anything besides return its argument. +pub comptime fn unquote(code: Quoted) -> Quoted { + code +} diff --git a/noir/noir-repo/scripts/bump-bb.sh b/noir/noir-repo/scripts/bump-bb.sh new file mode 100755 index 00000000000..36c1f78ca05 --- /dev/null +++ b/noir/noir-repo/scripts/bump-bb.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +BB_VERSION=$1 + +sed -i.bak "s/^VERSION=.*/VERSION=\"$BB_VERSION\"/" ./scripts/install_bb.sh && rm ./scripts/install_bb.sh.bak + +tmp=$(mktemp) +BACKEND_BARRETENBERG_PACKAGE_JSON=./tooling/noir_js_backend_barretenberg/package.json +jq --arg v $BB_VERSION '.dependencies."@aztec/bb.js" = $v' $BACKEND_BARRETENBERG_PACKAGE_JSON > $tmp && mv $tmp $BACKEND_BARRETENBERG_PACKAGE_JSON + +YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn install diff --git a/noir/noir-repo/scripts/install_bb.sh b/noir/noir-repo/scripts/install_bb.sh index b0d55b6ff1d..95dcfdda880 100755 --- a/noir/noir-repo/scripts/install_bb.sh +++ b/noir/noir-repo/scripts/install_bb.sh @@ -1,6 +1,6 @@ #!/bin/bash -VERSION="0.43.0" +VERSION="0.46.1" BBUP_PATH=~/.bb/bbup diff --git a/noir/noir-repo/scripts/redo-typo-pr.sh b/noir/noir-repo/scripts/redo-typo-pr.sh index 416be65a449..4b3b93b48ed 100755 --- a/noir/noir-repo/scripts/redo-typo-pr.sh +++ b/noir/noir-repo/scripts/redo-typo-pr.sh @@ -16,16 +16,21 @@ gh pr checkout $ORIGINAL_PR_NUMBER echo "Creating new local branch $NEW_BRANCH" git checkout -b $NEW_BRANCH -# Step 3: Push the new branch to GitHub +# Step 3: Squash commits +echo "Squashing new local branch $NEW_BRANCH" +git reset --soft master +git add . +git commit -m "chore: typo fixes" + +# Step 4: Push the new branch to GitHub echo "Pushing new branch $NEW_BRANCH to GitHub" -git commit --amend --no-edit git push origin $NEW_BRANCH -# Step 4: create a new pull request +# Step 5: create a new pull request echo "Creating a new pull request for $NEW_BRANCH" gh pr create --base master --head $NEW_BRANCH --title "chore: redo typo PR by $AUTHOR" --body "Thanks $AUTHOR for https://github.com/$REPO/pull/$ORIGINAL_PR_NUMBER. Our policy is to redo typo changes to dissuade metric farming. This is an automated script." -# Step 5: Close the original PR +# Step 6: Close the original PR echo "Closing original PR #$ORIGINAL_PR_NUMBER" gh pr close $ORIGINAL_PR_NUMBER diff --git a/noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/Nargo.toml new file mode 100644 index 00000000000..ae5c9ecb9c9 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unary_not_on_field_type_variable" +type = "bin" +authors = [""] +compiler_version = "" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/src/main.nr b/noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/src/main.nr new file mode 100644 index 00000000000..1023f753c32 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/unary_not_on_field_type_variable/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + let num: Field = 0; + assert_eq(!0, num); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/attribute_args/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/attribute_args/Nargo.toml new file mode 100644 index 00000000000..8efe5d203d1 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/attribute_args/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "attribute_args" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr new file mode 100644 index 00000000000..44b9c20460f --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/attribute_args/src/main.nr @@ -0,0 +1,20 @@ +#[attr_with_args(a b, c d)] +#[varargs(one, two)] +#[varargs(one, two, three, four)] +struct Foo {} + +comptime fn attr_with_args(s: StructDefinition, a: Quoted, b: Quoted) { + // Ensure all variables are in scope. + // We can't print them since that breaks the test runner. + let _ = s; + let _ = a; + let _ = b; +} + +comptime fn varargs(s: StructDefinition, t: [Quoted]) { + let _ = s; + for _ in t {} + assert(t.len() < 5); +} + +fn main() {} diff --git a/noir/noir-repo/test_programs/compile_success_empty/function_attribute/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/function_attribute/Nargo.toml new file mode 100644 index 00000000000..94b5c5da6a8 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/function_attribute/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "function_attribute" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr new file mode 100644 index 00000000000..ec22b730d3f --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/function_attribute/src/main.nr @@ -0,0 +1,18 @@ +#[function_attr] +fn foo() {} + +struct Foo {} + +comptime fn function_attr(_f: FunctionDefinition) -> Quoted { + quote { + impl Default for Foo { + fn default() -> Foo { + Foo {} + } + } + } +} + +fn main() { + let _ = Foo::default(); +} diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/regression_5428/Nargo.toml similarity index 66% rename from noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/regression_5428/Nargo.toml index 8fce1bf44b6..7507b934d66 100644 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/regression_5428/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "verify_honk_proof" +name = "regression_5428" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/compile_success_empty/regression_5428/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/regression_5428/src/main.nr new file mode 100644 index 00000000000..f01b89cbea4 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/regression_5428/src/main.nr @@ -0,0 +1,9 @@ +fn main() { + assert_true!(); +} + +comptime fn assert_true() -> Quoted { + let first = quote { assert( }; + let second = quote { true); }; + first.append(second) +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_attribute/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/trait_attribute/Nargo.toml new file mode 100644 index 00000000000..c72fe5e3e89 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_attribute/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "trait_attribute" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_attribute/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_attribute/src/main.nr new file mode 100644 index 00000000000..87f4893e3e5 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_attribute/src/main.nr @@ -0,0 +1,19 @@ +#[trait_attr] +trait Foo { + fn foo(self) -> Self; +} + +comptime fn trait_attr(_t: TraitDefinition) -> Quoted { + quote { + impl Foo for Field { + fn foo(self) -> Self { + self + 1 + } + } + } +} + +fn main() { + assert_eq(1.foo(), 2); + assert_eq(10.foo(), 11); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/unquote/Nargo.toml new file mode 100644 index 00000000000..68b2890e37a --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "unquote" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/unquote/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/unquote/src/main.nr new file mode 100644 index 00000000000..2717286b810 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/unquote/src/main.nr @@ -0,0 +1,4 @@ +fn main() { + std::meta::unquote!(quote { assert(true); }); + assert(std::meta::unquote!(quote { true })); +} diff --git a/noir/noir-repo/test_programs/execution_success/binary_operator_overloading/Nargo.toml b/noir/noir-repo/test_programs/execution_success/binary_operator_overloading/Nargo.toml new file mode 100644 index 00000000000..a43f38bdf30 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/binary_operator_overloading/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "binary_operator_overloading" +type = "bin" +authors = [""] +compiler_version = ">=0.20.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/operator_overloading/Prover.toml b/noir/noir-repo/test_programs/execution_success/binary_operator_overloading/Prover.toml similarity index 100% rename from noir/noir-repo/test_programs/execution_success/operator_overloading/Prover.toml rename to noir/noir-repo/test_programs/execution_success/binary_operator_overloading/Prover.toml diff --git a/noir/noir-repo/test_programs/execution_success/operator_overloading/src/main.nr b/noir/noir-repo/test_programs/execution_success/binary_operator_overloading/src/main.nr similarity index 100% rename from noir/noir-repo/test_programs/execution_success/operator_overloading/src/main.nr rename to noir/noir-repo/test_programs/execution_success/binary_operator_overloading/src/main.nr diff --git a/noir/noir-repo/test_programs/execution_success/operator_overloading/Nargo.toml b/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Nargo.toml similarity index 70% rename from noir/noir-repo/test_programs/execution_success/operator_overloading/Nargo.toml rename to noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Nargo.toml index 7f9f18ff567..ebc88faaaf4 100644 --- a/noir/noir-repo/test_programs/execution_success/operator_overloading/Nargo.toml +++ b/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "operator_overloading" +name = "unary_operator_overloading" type = "bin" authors = [""] compiler_version = ">=0.20.0" diff --git a/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Prover.toml b/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Prover.toml new file mode 100644 index 00000000000..b95c23a4d31 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/Prover.toml @@ -0,0 +1 @@ +x = 3 diff --git a/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/src/main.nr b/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/src/main.nr new file mode 100644 index 00000000000..20cdac51434 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/unary_operator_overloading/src/main.nr @@ -0,0 +1,36 @@ +use std::ops::{Neg, Not}; + +// x = 3 +fn main(x: u32) { + let wx = Wrapper::new(x as i32); + let ex: i32 = 3; + + assert((-wx).inner == -ex); + assert((!wx).inner == !ex); + + // Check that it works with type variables (x's type isn't immediately known) + let x = 3; + assert(-3 == -x); +} + +struct Wrapper { + inner: i32 +} + +impl Wrapper { + fn new(inner: i32) -> Self { + Wrapper { inner } + } +} + +impl Neg for Wrapper { + fn neg(self) -> Wrapper { + Wrapper::new(-self.inner) + } +} + +impl Not for Wrapper { + fn not(self) -> Wrapper { + Wrapper::new(!self.inner) + } +} diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml deleted file mode 100644 index 921b69e100a..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml +++ /dev/null @@ -1,4 +0,0 @@ -key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" -proof = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632","0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc","0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62","0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c","0x000000000000000000000000000000b0804efd6573805f991458295f510a2004","0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e","0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47","0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15","0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd","0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383","0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4","0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x000000000000000000000000000000f968b227a358a305607f3efc933823d288","0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08","0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f","0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1","0x0000000000000000000000000000005b739ed2075f2b046062b8fc6a2d1e9863","0x00000000000000000000000000000000001285cd1030d338c0e1603b4da2c838","0x00000000000000000000000000000027447d6c281eb38b2b937af4a516d60c04","0x000000000000000000000000000000000019bc3d980465fbb4a656a74296fc58","0x000000000000000000000000000000b484788ace8f7df86dd5e325d2e9b12599","0x00000000000000000000000000000000000a2ca0d10eb7b767114ae230b728d3","0x000000000000000000000000000000c6dfc7092f16f95795e437664498b88d53","0x0000000000000000000000000000000000131067b4e4d95a4f6f8cf5c9b5450a","0x0f413f22eec51f2a02800e0cafaeec1d92d744fbbaef213c687b9edabd6985f5","0x21230f4ff26c80ffb5d037a9d1d26c3f955ca34cbeca4f54db6656b932967a0c","0x0521f877fe35535767f99597cc50effbd283dcae6812ee0a7620d796ccbfd642","0x202b01350a9cc5c20ec0f3eaada338c0a3b793811bd539418ffa3cc4302615e2","0x2d1214d9b0d41058ad4a172d9c0aecc5bdabe95e687c3465050c6b5396509be4","0x1113b344a151b0af091cb28d728b752ebb4865da6cd7ee68471b961ca5cf69b9","0x2aa66d0954bb83e17bd5c9928d3aa7a7df75d741d409f7c15ba596804ba643fb","0x2e26bc7a530771ef7a95d5360d537e41cf94d8a0942764ff09881c107f91a106","0x0f14f32b921bb63ad1df00adab7c82af58ea8aa7f353f14b281208d8c5fab504","0x13429515c0c53b6502bbcdf545defb3cb69a986c9263e070fcbb397391aae1a3","0x1f21cac5e2f262afc1006a21454cc6bcb018c44e53ad8ab61cebbac99e539176","0x2a9886a6ddc8a61b097c668cd362fc8acdee8dde74f7b1af192c3e060bb2948f","0x2d718181e408ead2e9bcd30a84ad1fccbaf8d48ab6d1820bad4933d284b503c4","0x2634c1aafc902f14508f34d3d7e9d485f42d1a4c95b5a1ef73711ed0d3c68d77","0x092ede9777e6472ce5ffd8c963d466006189e960e2c591d338dc8d4af1a057fb","0x1cba45b17fd24f1cb1b4ab7b83eee741f6c77ba70a497dc4de259eceb7d5ea26","0x246e887c7bf2e17f919b2393b6e9b00b33e8822d862544a775aac05cb7bff710","0x04c3f539fe8689971948afcb437f1ecbd444a5bddaca1c8a450348dcd8480047","0x20c6a423ae4fd58e8951aa378d02d77baf90508ceb48856db2319d70938b186e","0x1bcf8786b554b3316d8ebdbc9d006a4e5d4865aad512ffd404b7f83550d3d030","0x09ab038260518f0970564afcd6bf22e2abf6b1fa5e12a327bbf195b6ca5edd78","0x1024e32554746f89c195286ba6ccfc9765e5d14bbe8064bc6fdf22d16ec6b495","0x17706656f8dbd7e47bb257a6428f0cb7278ea02fa9e6ce431d7bcc9133fba9c7","0x25a3e8a33c15ef2a4dd16313a6049bf1d468b4cdc141f238f2d51a1e8e1c22b3","0x1198863f08006edb27aee23164fb117a4ddec1bf1ed89807aa907e5cd24bf068","0x1862b4856b5b4d4a064f873e221703e4e2cd1ebfca1337dedca56485c38ed5a0","0x062214af1ea6dd6bf8895b92d394571c43970b6f967e1c794624d96071b25ad3","0x1e5be9428ddcf1f9b0cbafc28101e792ec5cf73852b0cd0b84fbff71b4490e09","0x2d4189bea5b1e30f63c64bd26df82f18bcaf885ec8887b54634b2557869ce87f","0x0f2e5d9a908850e9d44925e17d8b12d1adb1ed029799c9b5858598504242bbc0","0x3050dc85746a57931d99f3f35e77c2ba561fba0baa018b79ff1fd544026833ae","0x2a591a32437e5e0b875a137fd868bd1b6dbc003ff1b661f26e00627cc7c5cf47","0x27946841e1670ad9c65717016d0cedf524724217236e81b9fd0a264a36ebfb0e","0x0fc396e9d19d6e68e289602e292ee345542d0d28bf6de34fa62cc577cbdfb1df","0x08e7433a07a44c0c9c4dd4b273a2685bbd1a91fd5cf2b43409458fab42a23e1b","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x12bd9bfb029c3503a5c6deea87b0a0f11bb9f7ea584af2d48f3e48d7e09247ae","0x2ccc4810748c0a82dfc0f063d0b8c7999ffe9474653080e6ef92b3cb7a428784","0x08eb574d7fecadadb508c8bd35fdad06b99110609d679763c2e3645229b1b95a","0x0f1a65e747c8021ed7c454a4be1e89b1bce66ead9ed980fa98a7a050eafe98a1","0x1c8ff9e36684ec71614dee4c17859b06c742089f6029d3694a16e00dac9b57f1","0x0303101a8ba712aeca4da85b767ab8d3ecf489ec7d746f8ee20041717cc000e9","0x0aaf64c65e7088e5596108c9601467911fea809ca6540d79af77e6e66e36cd99","0x17caf164ce74ea7edfb1390e07763d2197797ec26661b92cde18a98d61d2fddc","0x18cb055c7ad6d01437725bb457681d81f3ecadc4f35d838a3c13daf25a44456a","0x2d78602b8bbcd32b36a99a6e2d248e7fe044ef1b50813133370412f9ef5299f0","0x2b139276ea86d426a115479e4154f72a6bd83a6253bf13e9670dc6b4664378f0","0x127c7837b384902c39a104036c09546728571c46c8166b1b9b13b3a615ebb781","0x05faa4816f83cf0189a482ad943c94b9ec6474002f2b327f8698763ad0ea0985","0x2f90359cc30ee693fb3aced96523cf7aebd152c22329eee56a398d9a4ac0628e","0x0a71beaf17a59c5a238f04c1f203848d87502c5057a78c13f0cfb0f9876e7714","0x2696c1e6d089556adaeb95c8a5e3065b00a393a38c2d69e9bd6ce8cdc49d87da","0x1f3d165a7dc6564a036e451eb9cb7f1e1cb1e6d29daa75e3f135ea3e58a79ccd","0x1473a660819bdd838d56122b72b32b267211e9f1103239480ec50fa85c9e1035","0x0a8ccaeb22451f391b3fc3467c8e6e900270a7afb7b510e8acf5a4f06f1c0888","0x03b3080afc0658cc87e307758cebc171921f43eca159b9dedf7f72aa8dd926bd","0x2dd7d6663fa0e1755dfafac352c361fcd64c7f4d53627e3646870ac169cc4a07","0x1ec54b883f5f35ccad0e75695af20790d9860104095bab34c9bf01628dd40cb9","0x193dff50f83c241f7a9e087a29ce72ecf3f6d8563593f786dcd04c32bcfd4ced","0x135122c0dae26cda8ca1c09de8225064ad86d10423ab0aaa53b481aa4626e1d6","0x08d5a56cbfab5aeed56d3cdd7fb6b30fc26b0c1a5b63fccd7fa44c53ba6fd35a","0x0d12f126dfa2daad3726d00ca339284cc22e36c6d81bb7a4b95c6f9598b60e7c","0x2e8b24bbdf2fd839d3c7cae1f0eeb96bfcfaeef30b27476f2fafcb17da78cd5e","0x2364acfe0cea39b7f749c5f303b99504977357925f810f684c60f35d16315211","0x06ca062eb70b8c51cfac35345e7b6b51f33a8ec9ebe204fb9b4911200bf508b7","0x266c0aa1ccb97186815bf69084f600d06ddd934e59a38dfe602ee5d6b9487f22","0x1d817537a49c6d0e3b4b65c6665334b91d7593142e60065048be9e55ceb5e7ab","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x25b77026673a1e613e50df0e88fb510973739d5f9064bd364079a9f884209632","0x25c9bc7a3f6aae3d43ff68b5614b34b5eaceff37157b37347995d231784ac1fd","0x085f69baef22680ae15f4801ef4361ebe9c7fc24a94b5bc2527dce8fb705439e","0x0d7c6b9ce31bfc32238a205455baf5ffe99cd30eb0f7bb5b504e1d4501e01382","0x1001a8cc4bc1221c814fba0eddcf3c40619b133373640c600de5bed0a0a05b10","0x20f5894be90e52977cb70f4f4cbd5101693db0360848939750db7e91109d54b6","0x22c09cb26db43f0599408b4daed0f4f496c66424e6affa41c14387d8e0af851b","0x24e5f41357798432426a9549d71e8cc681eaebacbe87f6e3bf38e85de5aa2f3d","0x06eb90100c736fbf2b87432d7821ecdc0b365024739bc36363d48b905973f5b9","0x000000000000000000000000000000ece6d09ed58e9f5661c01140b10558a8c2","0x000000000000000000000000000000000012b6e4f37adcb34b8e88ff8b6eebce","0x000000000000000000000000000000b226a2bb93593fa1fab19a44767828a3f5","0x00000000000000000000000000000000002b5b518342030543092e1428a7e33c","0x00000000000000000000000000000022ba33857034a0574c216eb3c1ddff3025","0x00000000000000000000000000000000001918e58df857985a7cf9eae7802165","0x00000000000000000000000000000045c2d840b96fb6106cc14dcad89dd5f675","0x00000000000000000000000000000000000afdfac1e3a1febdd0208867d44f98","0x00000000000000000000000000000042ebed6c5ec45d794f119aef24c192af0f","0x00000000000000000000000000000000002d05ef250900bbcc5751bbeb210d6a","0x00000000000000000000000000000060d604bdda48eecc90ed065bd9770e1323","0x00000000000000000000000000000000001fed91c63d0041660c1cbc84c2ffbb","0x00000000000000000000000000000054196b549cde36092e8184c7f4f7d878de","0x00000000000000000000000000000000000153f26a01294329922b492485cc31","0x00000000000000000000000000000056ebea579d10dbb440f0222931df2c0059","0x00000000000000000000000000000000000d2cbc61ce5b7cdd7fce398da4637b","0x000000000000000000000000000000e2b9512360b9797d96675d8a2fd2f7aa5d","0x000000000000000000000000000000000025742905f105ff895f74e7c3daa34a","0x000000000000000000000000000000a2dd7df55db59bd41b83518d4403fbc382","0x00000000000000000000000000000000002c1d9c3cbb9371d4cc4e9f900b9a46","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000bcf12ae40c9425c3e67654b84181f90502","0x00000000000000000000000000000000000b6d3faa8a71ff6ef1aa887b7307cf","0x0000000000000000000000000000001f6f719acc23b8f84808c0275d61cfb456","0x0000000000000000000000000000000000296030933ed0c134457ae71c393dfe","0x000000000000000000000000000000ebe1a57cdd7d3d763289b40ef5ed9a7ae0","0x000000000000000000000000000000000010f30483e7df51fca2316d3367603c","0x0000000000000000000000000000000149b7b283ab18060618c8e051864c03cd","0x00000000000000000000000000000000001ef7763235a3a25e241a5f06704dc3"] -public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] -verification_key = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84","0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae","0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16","0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1","0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c","0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7","0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8","0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c","0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5","0x00000000000000000000000000000000002002681bb417184b2df070a16a3858","0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511","0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223","0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7","0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c","0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130","0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f","0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3","0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592","0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3","0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1","0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0","0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c","0x0000000000000000000000000000009f825dde88092070747180d581c342444a","0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01","0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff","0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9","0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1","0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b","0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2","0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f","0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0","0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349","0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8","0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2","0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556","0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d","0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb","0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d","0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8","0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862","0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e","0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830","0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f","0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe","0x000000000000000000000000000000231147211b3c75e1f47d150e4bbd2fb22e","0x00000000000000000000000000000000000d19ee104a10d3c701cfd87473cbbe","0x0000000000000000000000000000006705f3f382637d00f698e2c5c94ed05ae9","0x00000000000000000000000000000000000b9c792da28bb60601dd7ce4b74e68","0x000000000000000000000000000000ac5acc8cc21e4ddb225c510670f80c80b3","0x00000000000000000000000000000000002da9d3fa57343e6998aba19429b9fa","0x0000000000000000000000000000004bacbf54b7c17a560df0af18b6d0d527be","0x00000000000000000000000000000000000faea33aeca2025b22c288964b21eb","0x000000000000000000000000000000492e756298d68d6e95de096055cc0336c3","0x00000000000000000000000000000000001a12a12f004859e5a3675c7315121b","0x000000000000000000000000000000893d521d512f30e6d32afbbc0cecd8ee00","0x00000000000000000000000000000000001674b3c1ef12c6da690631e0d86c04","0x000000000000000000000000000000aa6cb02a52e7a613873d4ac9b411349945","0x00000000000000000000000000000000001ecb1fe9c493add46751f9940f73e1","0x00000000000000000000000000000045b3d362ca82cba69fb2b9c733a5b8c351","0x000000000000000000000000000000000019a683586af466e331945b732d2f8c","0x000000000000000000000000000000fc79b052dfdfe67c0ecfc06b4267ffd694","0x00000000000000000000000000000000001336a70c396393038d5e9913744ac2","0x0000000000000000000000000000005450d29af1e9438e91cd33ddeb2548226e","0x000000000000000000000000000000000000993a602891cfd0e6f6ecf7404933","0x000000000000000000000000000000498efddab90a32e9b2db729ed6e9b40192","0x00000000000000000000000000000000002425efebe9628c63ca6fc28bdb5901","0x000000000000000000000000000000d8488157f875a21ab5f93f1c2b641f3de9","0x0000000000000000000000000000000000290f95ada3936604dc4b14df7504e3","0x0000000000000000000000000000005d6902187f3ed60dcce06fca211b40329a","0x00000000000000000000000000000000002b5870a6ba0b20aaa0178e5adfbc36","0x000000000000000000000000000000e5c2519171fa0e548fc3c4966ffc1ce570","0x00000000000000000000000000000000001cb8d8f4793b7debbdc429389dbf2d","0x000000000000000000000000000000a3ee22dd60456277b86c32a18982dcb185","0x00000000000000000000000000000000002493c99a3d068b03f8f2b8d28b57ce","0x000000000000000000000000000000f6c3731486320082c20ec71bbdc92196c1","0x00000000000000000000000000000000001ded39c4c8366469843cd63f09ecac","0x000000000000000000000000000000494997477ab161763e46601d95844837ef","0x00000000000000000000000000000000002e0cddbc5712d79b59cb3b41ebbcdd","0x000000000000000000000000000000426db4c64531d350750df62dbbc41a1bd9","0x0000000000000000000000000000000000303126892f664d8d505964d14315ec","0x00000000000000000000000000000076a6b2c6040c0c62bd59acfe3e3e125672","0x000000000000000000000000000000000000874a5ad262eecc6b565e0b085074","0x000000000000000000000000000000ef082fb517183c9c6841c2b8ef2ca1df04","0x0000000000000000000000000000000000127b2a745a1b74968c3edc18982b9b","0x000000000000000000000000000000c9efd4f8c3d56e1eb23d789a8f710d5be6","0x000000000000000000000000000000000015a18748490ff4c2b1871081954e86","0x000000000000000000000000000000a0011ef987dc016ab110eacd554a1d8bbf","0x00000000000000000000000000000000002097c84955059442a95df075833071","0x000000000000000000000000000000d38e9426ad3085b68b00a93c17897c2877","0x00000000000000000000000000000000002aecd48089890ea0798eb952c66824","0x00000000000000000000000000000078d8a9ce405ce559f441f2e71477ff3ddb","0x00000000000000000000000000000000001216bdb2f0d961bb8a7a23331d2150","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb","0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56","0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc","0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4"] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr deleted file mode 100644 index ecfd18f3837..00000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr +++ /dev/null @@ -1,21 +0,0 @@ - -// This circuit aggregates a single Honk proof from `assert_statement_recursive`. -global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 409; -fn main( - verification_key: [Field; 103], - // This is the proof without public inputs attached. - // - // This means: the size of this does not change with the number of public inputs. - proof: [Field; SIZE_OF_PROOF_IF_LOGN_IS_28], - public_inputs: pub [Field; 1], - // This is currently not public. It is fine given that the vk is a part of the circuit definition. - // I believe we want to eventually make it public too though. - key_hash: Field -) { - std::verify_proof( - verification_key.as_slice(), - proof.as_slice(), - public_inputs.as_slice(), - key_hash - ); -} diff --git a/noir/noir-repo/test_programs/gates_report.sh b/noir/noir-repo/test_programs/gates_report.sh index b33e81b037c..6c901ff24bc 100755 --- a/noir/noir-repo/test_programs/gates_report.sh +++ b/noir/noir-repo/test_programs/gates_report.sh @@ -4,7 +4,7 @@ set -e BACKEND=${BACKEND:-bb} # These tests are incompatible with gas reporting -excluded_dirs=("workspace" "workspace_default_member" "double_verify_nested_proof") +excluded_dirs=("workspace" "workspace_default_member" "databus") current_dir=$(pwd) artifacts_path="$current_dir/acir_artifacts" @@ -18,6 +18,10 @@ NUM_ARTIFACTS=$(ls -1q "$artifacts_path" | wc -l) ITER="1" for pathname in $test_dirs; do ARTIFACT_NAME=$(basename "$pathname") + if [[ " ${excluded_dirs[@]} " =~ "$ARTIFACT_NAME" ]]; then + ITER=$(( $ITER + 1 )) + continue + fi GATES_INFO=$($BACKEND gates -b "$artifacts_path/$ARTIFACT_NAME/target/program.json") MAIN_FUNCTION_INFO=$(echo $GATES_INFO | jq -r '.functions[0] | .name = "main"') diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 05b28f9d95a..540d6d11bc0 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -14,7 +14,7 @@ build-data.workspace = true acvm.workspace = true fm.workspace = true nargo.workspace = true -noirc_frontend.workspace = true +noirc_frontend = { workspace = true, features = ["bn254"] } noirc_printable_type.workspace = true noirc_errors.workspace = true noirc_driver.workspace = true diff --git a/noir/noir-repo/tooling/debugger/ignored-tests.txt b/noir/noir-repo/tooling/debugger/ignored-tests.txt index a3971d437fb..745971d9b28 100644 --- a/noir/noir-repo/tooling/debugger/ignored-tests.txt +++ b/noir/noir-repo/tooling/debugger/ignored-tests.txt @@ -1,18 +1,6 @@ -bigint brillig_references -brillig_to_bytes_integration debug_logs -fold_after_inlined_calls -fold_basic -fold_basic_nested_call -fold_call_witness_condition -fold_complex_outputs -fold_distinct_return -fold_fibonacci -fold_numeric_generic_poseidon is_unconstrained macros -modulus references -regression_4709 -to_bytes_integration +regression_4709 \ No newline at end of file diff --git a/noir/noir-repo/tooling/debugger/src/context.rs b/noir/noir-repo/tooling/debugger/src/context.rs index cb36988bf0b..d18ec5f0786 100644 --- a/noir/noir-repo/tooling/debugger/src/context.rs +++ b/noir/noir-repo/tooling/debugger/src/context.rs @@ -1,10 +1,11 @@ use crate::foreign_calls::DebugForeignCallExecutor; use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; -use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ - ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, + ACVMStatus, AcirCallWaitInfo, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, + OpcodeNotSolvable, StepResult, ACVM, }; use acvm::{BlackBoxFunctionSolver, FieldElement}; @@ -15,56 +16,235 @@ use nargo::NargoError; use noirc_artifacts::debug::{DebugArtifact, StackFrame}; use noirc_driver::DebugFile; +use thiserror::Error; + use std::collections::BTreeMap; use std::collections::{hash_set::Iter, HashSet}; +/// A Noir program is composed by +/// `n` ACIR circuits +/// |_ `m` ACIR opcodes +/// |_ Acir call +/// |_ Acir Brillig function invocation +/// |_ `p` Brillig opcodes +/// +/// The purpose of this structure is to map the opcode locations in ACIR circuits into +/// a flat contiguous address space to be able to expose them to the DAP interface. +/// In this address space, the ACIR circuits are laid out one after the other, and +/// Brillig functions called from such circuits are expanded inline, replacing +/// the `BrilligCall` ACIR opcode. +/// +/// `addresses: Vec>` +/// * The outer vec is `n` sized - one element per ACIR circuit +/// * Each nested vec is `m` sized - one element per ACIR opcode in circuit +/// * Each element is the "virtual address" of such opcode +/// +/// For flattening we map each ACIR circuit and ACIR opcode with a sequential address number +/// We start by assigning 0 to the very first ACIR opcode and then start accumulating by +/// traversing by depth-first +/// +/// Even if the address space is continuous, the `addresses` tree only +/// keeps track of the ACIR opcodes, since the Brillig opcode addresses can be +/// calculated from the initial opcode address. +/// As a result the flattened indexed addresses list may have "holes". +/// +/// If between two consequent `addresses` nodes there is a "hole" (an address jump), +/// this means that the first one is actually a ACIR Brillig call +/// which has as many brillig opcodes as `second_address - first_address` +/// +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct AddressMap { + addresses: Vec>, + + // virtual address of the last opcode of the program + last_valid_address: usize, +} + +impl AddressMap { + pub(super) fn new( + circuits: &[Circuit], + unconstrained_functions: &[BrilligBytecode], + ) -> Self { + let opcode_address_size = |opcode: &Opcode| { + if let Opcode::BrilligCall { id, .. } = opcode { + unconstrained_functions[*id as usize].bytecode.len() + } else { + 1 + } + }; + + let mut addresses = Vec::with_capacity(circuits.len()); + let mut next_address = 0usize; + + for circuit in circuits { + let mut circuit_addresses = Vec::with_capacity(circuit.opcodes.len()); + for opcode in &circuit.opcodes { + circuit_addresses.push(next_address); + next_address += opcode_address_size(opcode); + } + addresses.push(circuit_addresses); + } + + Self { addresses, last_valid_address: next_address - 1 } + } + + /// Returns the absolute address of the opcode at the given location. + /// Absolute here means accounting for nested Brillig opcodes in BrilligCall + /// opcodes. + pub fn debug_location_to_address(&self, location: &DebugLocation) -> usize { + let circuit_addresses = &self.addresses[location.circuit_id as usize]; + match &location.opcode_location { + OpcodeLocation::Acir(acir_index) => circuit_addresses[*acir_index], + OpcodeLocation::Brillig { acir_index, brillig_index } => { + circuit_addresses[*acir_index] + *brillig_index + } + } + } + + pub fn address_to_debug_location(&self, address: usize) -> Option { + if address > self.last_valid_address { + return None; + } + // We binary search if the given address is the first opcode address of each circuit id + // if is not, this means that the address itself is "contained" in the previous + // circuit indicated by `Err(insert_index)` + let circuit_id = + match self.addresses.binary_search_by(|addresses| addresses[0].cmp(&address)) { + Ok(found_index) => found_index, + // This means that the address is not in `insert_index` circuit + // because is an `Err`, so it must be included in previous circuit vec of opcodes + Err(insert_index) => insert_index - 1, + }; + + // We binary search among the selected `circuit_id`` list of opcodes + // If Err(insert_index) this means that the given address + // is a Brillig addresses that's contained in previous index ACIR opcode index + let opcode_location = match self.addresses[circuit_id].binary_search(&address) { + Ok(found_index) => OpcodeLocation::Acir(found_index), + Err(insert_index) => { + let acir_index = insert_index - 1; + let base_offset = self.addresses[circuit_id][acir_index]; + let brillig_index = address - base_offset; + OpcodeLocation::Brillig { acir_index, brillig_index } + } + }; + Some(DebugLocation { circuit_id: circuit_id as u32, opcode_location }) + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct DebugLocation { + pub circuit_id: u32, + pub opcode_location: OpcodeLocation, +} + +impl std::fmt::Display for DebugLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let circuit_id = self.circuit_id; + match self.opcode_location { + OpcodeLocation::Acir(index) => write!(f, "{circuit_id}:{index}"), + OpcodeLocation::Brillig { acir_index, brillig_index } => { + write!(f, "{circuit_id}:{acir_index}.{brillig_index}") + } + } + } +} + +#[derive(Error, Debug)] +pub enum DebugLocationFromStrError { + #[error("Invalid debug location string: {0}")] + InvalidDebugLocationString(String), +} + +impl std::str::FromStr for DebugLocation { + type Err = DebugLocationFromStrError; + fn from_str(s: &str) -> Result { + let parts: Vec<_> = s.split(':').collect(); + let error = Err(DebugLocationFromStrError::InvalidDebugLocationString(s.to_string())); + + match parts.len() { + 1 => OpcodeLocation::from_str(parts[0]).map_or(error, |opcode_location| { + Ok(DebugLocation { circuit_id: 0, opcode_location }) + }), + 2 => { + let first_part = parts[0].parse().ok(); + let second_part = OpcodeLocation::from_str(parts[1]).ok(); + if let (Some(circuit_id), Some(opcode_location)) = (first_part, second_part) { + Ok(DebugLocation { circuit_id, opcode_location }) + } else { + error + } + } + _ => error, + } + } +} + #[derive(Debug)] pub(super) enum DebugCommandResult { Done, Ok, - BreakpointReached(OpcodeLocation), + BreakpointReached(DebugLocation), Error(NargoError), } +pub struct ExecutionFrame<'a, B: BlackBoxFunctionSolver> { + circuit_id: u32, + acvm: ACVM<'a, FieldElement, B>, +} + pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { acvm: ACVM<'a, FieldElement, B>, + current_circuit_id: u32, brillig_solver: Option>, + + witness_stack: WitnessStack, + acvm_stack: Vec>, + + backend: &'a B, foreign_call_executor: Box, + debug_artifact: &'a DebugArtifact, - breakpoints: HashSet, - source_to_opcodes: BTreeMap>, + breakpoints: HashSet, + source_to_locations: BTreeMap>, + + circuits: &'a [Circuit], unconstrained_functions: &'a [BrilligBytecode], - // Absolute (in terms of all the opcodes ACIR+Brillig) addresses of the ACIR - // opcodes with one additional entry for to indicate the last valid address. - acir_opcode_addresses: Vec, + acir_opcode_addresses: AddressMap, } impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { pub(super) fn new( blackbox_solver: &'a B, - circuit: &'a Circuit, + circuits: &'a [Circuit], debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, foreign_call_executor: Box, unconstrained_functions: &'a [BrilligBytecode], ) -> Self { let source_to_opcodes = build_source_to_opcode_debug_mappings(debug_artifact); - let acir_opcode_addresses = build_acir_opcode_offsets(circuit, unconstrained_functions); + let current_circuit_id: u32 = 0; + let initial_circuit = &circuits[current_circuit_id as usize]; + let acir_opcode_addresses = AddressMap::new(circuits, unconstrained_functions); Self { - // TODO: need to handle brillig pointer in the debugger acvm: ACVM::new( blackbox_solver, - &circuit.opcodes, + &initial_circuit.opcodes, initial_witness, unconstrained_functions, - &circuit.assert_messages, + &initial_circuit.assert_messages, ), + current_circuit_id, brillig_solver: None, + witness_stack: WitnessStack::default(), + acvm_stack: vec![], + backend: blackbox_solver, foreign_call_executor, debug_artifact, breakpoints: HashSet::new(), - source_to_opcodes, + source_to_locations: source_to_opcodes, + circuits, unconstrained_functions, acir_opcode_addresses, } @@ -74,6 +254,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.opcodes() } + pub(super) fn get_opcodes_of_circuit(&self, circuit_id: u32) -> &[Opcode] { + &self.circuits[circuit_id as usize].opcodes + } + pub(super) fn get_witness_map(&self) -> &WitnessMap { self.acvm.witness_map() } @@ -86,36 +270,49 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.acvm.overwrite_witness(witness, value) } - pub(super) fn get_current_opcode_location(&self) -> Option { + pub(super) fn get_current_debug_location(&self) -> Option { let ip = self.acvm.instruction_pointer(); if ip >= self.get_opcodes().len() { None - } else if let Some(ref solver) = self.brillig_solver { - Some(OpcodeLocation::Brillig { - acir_index: ip, - brillig_index: solver.program_counter(), - }) } else { - Some(OpcodeLocation::Acir(ip)) + let opcode_location = if let Some(ref solver) = self.brillig_solver { + OpcodeLocation::Brillig { acir_index: ip, brillig_index: solver.program_counter() } + } else { + OpcodeLocation::Acir(ip) + }; + Some(DebugLocation { circuit_id: self.current_circuit_id, opcode_location }) } } - pub(super) fn get_call_stack(&self) -> Vec { + pub(super) fn get_call_stack(&self) -> Vec { + // Build the frames from parent ACIR calls + let mut frames: Vec<_> = self + .acvm_stack + .iter() + .map(|ExecutionFrame { circuit_id, acvm }| DebugLocation { + circuit_id: *circuit_id, + opcode_location: OpcodeLocation::Acir(acvm.instruction_pointer()), + }) + .collect(); + + // Now add the frame(s) for the currently executing ACVM let instruction_pointer = self.acvm.instruction_pointer(); - if instruction_pointer >= self.get_opcodes().len() { - vec![] - } else if let Some(ref solver) = self.brillig_solver { - solver - .get_call_stack() - .iter() - .map(|program_counter| OpcodeLocation::Brillig { + let circuit_id = self.current_circuit_id; + if let Some(ref solver) = self.brillig_solver { + frames.extend(solver.get_call_stack().iter().map(|program_counter| DebugLocation { + circuit_id, + opcode_location: OpcodeLocation::Brillig { acir_index: instruction_pointer, brillig_index: *program_counter, - }) - .collect() - } else { - vec![OpcodeLocation::Acir(instruction_pointer)] + }, + })); + } else if instruction_pointer < self.get_opcodes().len() { + frames.push(DebugLocation { + circuit_id, + opcode_location: OpcodeLocation::Acir(instruction_pointer), + }); } + frames } pub(super) fn is_source_location_in_debug_module(&self, location: &Location) -> bool { @@ -142,10 +339,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &self, file_id: &FileId, line: i64, - ) -> Option { + ) -> Option { let line = line as usize; - let line_to_opcodes = self.source_to_opcodes.get(file_id)?; - let found_index = match line_to_opcodes.binary_search_by(|x| x.0.cmp(&line)) { + let line_to_opcodes = self.source_to_locations.get(file_id)?; + let found_location = match line_to_opcodes.binary_search_by(|x| x.0.cmp(&line)) { Ok(index) => { // move backwards to find the first opcode which matches the line let mut index = index; @@ -161,7 +358,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { line_to_opcodes[index].1 } }; - Some(found_index) + Some(found_location) } /// Returns the callstack in source code locations for the currently @@ -172,9 +369,9 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { /// This function also filters source locations that are determined to be in /// the internal debug module. pub(super) fn get_current_source_location(&self) -> Option> { - self.get_current_opcode_location() + self.get_current_debug_location() .as_ref() - .map(|opcode_location| self.get_source_location_for_opcode_location(opcode_location)) + .map(|debug_location| self.get_source_location_for_debug_location(debug_location)) .filter(|v: &Vec| !v.is_empty()) } @@ -184,15 +381,12 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { /// the given opcode location cannot be mapped back to a source location /// (eg. it may be pure debug instrumentation code or other synthetically /// produced opcode by the compiler) - pub(super) fn get_source_location_for_opcode_location( + pub(super) fn get_source_location_for_debug_location( &self, - opcode_location: &OpcodeLocation, + debug_location: &DebugLocation, ) -> Vec { - // TODO: this assumes we're debugging a program (ie. the DebugArtifact - // will contain a single DebugInfo), but this assumption doesn't hold - // for contracts - self.debug_artifact.debug_symbols[0] - .opcode_location(opcode_location) + self.debug_artifact.debug_symbols[debug_location.circuit_id as usize] + .opcode_location(&debug_location.opcode_location) .map(|source_locations| { source_locations .into_iter() @@ -208,48 +402,30 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { /// general, the matching between opcode location and source location is 1 /// to 1, but due to the compiler inlining functions a single opcode /// location may expand to multiple source locations. - pub(super) fn get_source_call_stack(&self) -> Vec<(OpcodeLocation, Location)> { + pub(super) fn get_source_call_stack(&self) -> Vec<(DebugLocation, Location)> { self.get_call_stack() .iter() - .flat_map(|opcode_location| { - self.get_source_location_for_opcode_location(opcode_location) + .flat_map(|debug_location| { + self.get_source_location_for_debug_location(debug_location) .into_iter() - .map(|source_location| (*opcode_location, source_location)) + .map(|source_location| (*debug_location, source_location)) }) .collect() } /// Returns the absolute address of the opcode at the given location. - /// Absolute here means accounting for nested Brillig opcodes in BrilligCall - /// opcodes. - pub fn opcode_location_to_address(&self, location: &OpcodeLocation) -> usize { - match location { - OpcodeLocation::Acir(acir_index) => self.acir_opcode_addresses[*acir_index], - OpcodeLocation::Brillig { acir_index, brillig_index } => { - self.acir_opcode_addresses[*acir_index] + *brillig_index - } - } + pub fn debug_location_to_address(&self, location: &DebugLocation) -> usize { + self.acir_opcode_addresses.debug_location_to_address(location) } - pub fn address_to_opcode_location(&self, address: usize) -> Option { - if address >= *self.acir_opcode_addresses.last().unwrap_or(&0) { - return None; - } - let location = match self.acir_opcode_addresses.binary_search(&address) { - Ok(found_index) => OpcodeLocation::Acir(found_index), - Err(insert_index) => { - let acir_index = insert_index - 1; - let base_offset = self.acir_opcode_addresses[acir_index]; - let brillig_index = address - base_offset; - OpcodeLocation::Brillig { acir_index, brillig_index } - } - }; - Some(location) + // Returns the DebugLocation associated to the given address + pub fn address_to_debug_location(&self, address: usize) -> Option { + self.acir_opcode_addresses.address_to_debug_location(address) } - pub(super) fn render_opcode_at_location(&self, location: &OpcodeLocation) -> String { - let opcodes = self.get_opcodes(); - match location { + pub(super) fn render_opcode_at_location(&self, location: &DebugLocation) -> String { + let opcodes = self.get_opcodes_of_circuit(location.circuit_id); + match &location.opcode_location { OpcodeLocation::Acir(acir_index) => { let opcode = &opcodes[*acir_index]; match opcode { @@ -280,7 +456,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.brillig_solver = Some(solver); if self.breakpoint_reached() { DebugCommandResult::BreakpointReached( - self.get_current_opcode_location() + self.get_current_debug_location() .expect("Breakpoint reached but we have no location"), ) } else { @@ -296,7 +472,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.handle_foreign_call(foreign_call) } Err(err) => DebugCommandResult::Error(NargoError::ExecutionError( - // TODO: debugger does not handle multiple acir calls ExecutionError::SolvingError(err, None), )), } @@ -315,24 +490,82 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } else { self.acvm.resolve_pending_foreign_call(foreign_call_result); } - // TODO: should we retry executing the opcode somehow in this case? + // TODO: should we retry executing the opcode somehow in this + // case? Otherwise, executing a foreign call takes two debugging + // steps. DebugCommandResult::Ok } Err(error) => DebugCommandResult::Error(error.into()), } } - fn handle_acvm_status(&mut self, status: ACVMStatus) -> DebugCommandResult { - if let ACVMStatus::RequiresForeignCall(foreign_call) = status { - return self.handle_foreign_call(foreign_call); + fn handle_acir_call( + &mut self, + call_info: AcirCallWaitInfo, + ) -> DebugCommandResult { + let callee_circuit = &self.circuits[call_info.id as usize]; + let callee_witness_map = call_info.initial_witness; + let callee_acvm = ACVM::new( + self.backend, + &callee_circuit.opcodes, + callee_witness_map, + self.unconstrained_functions, + &callee_circuit.assert_messages, + ); + let caller_acvm = std::mem::replace(&mut self.acvm, callee_acvm); + self.acvm_stack + .push(ExecutionFrame { circuit_id: self.current_circuit_id, acvm: caller_acvm }); + self.current_circuit_id = call_info.id; + + // Explicitly handling the new ACVM status here handles two edge cases: + // 1. there is a breakpoint set at the beginning of a circuit + // 2. the called circuit has no opcodes + self.handle_acvm_status(self.acvm.get_status().clone()) + } + + fn handle_acir_call_finished(&mut self) -> DebugCommandResult { + let caller_frame = self.acvm_stack.pop().expect("Execution stack should not be empty"); + let caller_acvm = caller_frame.acvm; + let callee_acvm = std::mem::replace(&mut self.acvm, caller_acvm); + self.current_circuit_id = caller_frame.circuit_id; + let call_solved_witness = callee_acvm.finalize(); + + let ACVMStatus::RequiresAcirCall(call_info) = self.acvm.get_status() else { + unreachable!("Resolving an ACIR call, the caller is in an invalid state"); + }; + let acir_to_call = &self.circuits[call_info.id as usize]; + + 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 DebugCommandResult::Error( + ExecutionError::SolvingError( + OpcodeNotSolvable::MissingAssignment(return_witness_index).into(), + None, // Missing assignment errors do not supply user-facing diagnostics so we do not need to attach a call stack + ) + .into(), + ); + } } + self.acvm.resolve_pending_acir_call(call_resolved_outputs); + + DebugCommandResult::Ok + } + fn handle_acvm_status(&mut self, status: ACVMStatus) -> DebugCommandResult { match status { - ACVMStatus::Solved => DebugCommandResult::Done, + ACVMStatus::Solved => { + if self.acvm_stack.is_empty() { + return DebugCommandResult::Done; + } + self.handle_acir_call_finished() + } ACVMStatus::InProgress => { if self.breakpoint_reached() { DebugCommandResult::BreakpointReached( - self.get_current_opcode_location() + self.get_current_debug_location() .expect("Breakpoint reached but we have no location"), ) } else { @@ -340,15 +573,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } ACVMStatus::Failure(error) => DebugCommandResult::Error(NargoError::ExecutionError( - // TODO: debugger does not handle multiple acir calls ExecutionError::SolvingError(error, None), )), - ACVMStatus::RequiresForeignCall(_) => { - unreachable!("Unexpected pending foreign call resolution"); - } - ACVMStatus::RequiresAcirCall(_) => { - todo!("Multiple ACIR calls are not supported"); - } + ACVMStatus::RequiresForeignCall(foreign_call) => self.handle_foreign_call(foreign_call), + ACVMStatus::RequiresAcirCall(call_info) => self.handle_acir_call(call_info), } } @@ -367,9 +595,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } fn get_current_acir_index(&self) -> Option { - self.get_current_opcode_location().map(|opcode_location| match opcode_location { - OpcodeLocation::Acir(acir_index) => acir_index, - OpcodeLocation::Brillig { acir_index, .. } => acir_index, + self.get_current_debug_location().map(|debug_location| { + match debug_location.opcode_location { + OpcodeLocation::Acir(acir_index) => acir_index, + OpcodeLocation::Brillig { acir_index, .. } => acir_index, + } }) } @@ -394,10 +624,16 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { return true; } - match self.get_current_opcode_location() { - Some(OpcodeLocation::Brillig { .. }) => true, - Some(OpcodeLocation::Acir(acir_index)) => { - matches!(self.get_opcodes()[acir_index], Opcode::BrilligCall { .. }) + match self.get_current_debug_location() { + Some(DebugLocation { opcode_location: OpcodeLocation::Brillig { .. }, .. }) => true, + Some(DebugLocation { + circuit_id, + opcode_location: OpcodeLocation::Acir(acir_index), + }) => { + matches!( + self.get_opcodes_of_circuit(circuit_id)[acir_index], + Opcode::BrilligCall { .. } + ) } _ => false, } @@ -491,16 +727,19 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } fn breakpoint_reached(&self) -> bool { - if let Some(location) = self.get_current_opcode_location() { + if let Some(location) = self.get_current_debug_location() { self.breakpoints.contains(&location) } else { false } } - pub(super) fn is_valid_opcode_location(&self, location: &OpcodeLocation) -> bool { - let opcodes = self.get_opcodes(); - match *location { + pub(super) fn is_valid_debug_location(&self, location: &DebugLocation) -> bool { + if location.circuit_id as usize >= self.circuits.len() { + return false; + } + let opcodes = self.get_opcodes_of_circuit(location.circuit_id); + match location.opcode_location { OpcodeLocation::Acir(acir_index) => acir_index < opcodes.len(), OpcodeLocation::Brillig { acir_index, brillig_index } => { if acir_index < opcodes.len() { @@ -518,19 +757,19 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } - pub(super) fn is_breakpoint_set(&self, location: &OpcodeLocation) -> bool { + pub(super) fn is_breakpoint_set(&self, location: &DebugLocation) -> bool { self.breakpoints.contains(location) } - pub(super) fn add_breakpoint(&mut self, location: OpcodeLocation) -> bool { + pub(super) fn add_breakpoint(&mut self, location: DebugLocation) -> bool { self.breakpoints.insert(location) } - pub(super) fn delete_breakpoint(&mut self, location: &OpcodeLocation) -> bool { + pub(super) fn delete_breakpoint(&mut self, location: &DebugLocation) -> bool { self.breakpoints.remove(location) } - pub(super) fn iterate_breakpoints(&self) -> Iter<'_, OpcodeLocation> { + pub(super) fn iterate_breakpoints(&self) -> Iter<'_, DebugLocation> { self.breakpoints.iter() } @@ -542,8 +781,10 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { matches!(self.acvm.get_status(), ACVMStatus::Solved) } - pub fn finalize(self) -> WitnessMap { - self.acvm.finalize() + pub fn finalize(mut self) -> WitnessStack { + let last_witness_map = self.acvm.finalize(); + self.witness_stack.push(0, last_witness_map); + self.witness_stack } } @@ -555,11 +796,10 @@ fn is_debug_file_in_debug_crate(debug_file: &DebugFile) -> bool { /// numbers and opcode locations corresponding to those line numbers fn build_source_to_opcode_debug_mappings( debug_artifact: &DebugArtifact, -) -> BTreeMap> { +) -> BTreeMap> { if debug_artifact.debug_symbols.is_empty() { return BTreeMap::new(); } - let locations = &debug_artifact.debug_symbols[0].locations; let simple_files: BTreeMap<_, _> = debug_artifact .file_map .iter() @@ -572,50 +812,34 @@ fn build_source_to_opcode_debug_mappings( }) .collect(); - let mut result: BTreeMap> = BTreeMap::new(); - locations.iter().for_each(|(opcode_location, source_locations)| { - source_locations.iter().for_each(|source_location| { - let span = source_location.span; - let file_id = source_location.file; - let Some(file) = simple_files.get(&file_id) else { - return; - }; - let Ok(line_index) = file.line_index((), span.start() as usize) else { - return; - }; - let line_number = line_index + 1; - - result.entry(file_id).or_default().push((line_number, *opcode_location)); - }); - }); + let mut result: BTreeMap> = BTreeMap::new(); + + for (circuit_id, debug_symbols) in debug_artifact.debug_symbols.iter().enumerate() { + for (opcode_location, source_locations) in &debug_symbols.locations { + source_locations.iter().for_each(|source_location| { + let span = source_location.span; + let file_id = source_location.file; + let Some(file) = simple_files.get(&file_id) else { + return; + }; + let Ok(line_index) = file.line_index((), span.start() as usize) else { + return; + }; + let line_number = line_index + 1; + + let debug_location = DebugLocation { + circuit_id: circuit_id as u32, + opcode_location: *opcode_location, + }; + result.entry(file_id).or_default().push((line_number, debug_location)); + }); + } + } result.iter_mut().for_each(|(_, file_locations)| file_locations.sort_by_key(|x| (x.0, x.1))); result } -fn build_acir_opcode_offsets( - circuit: &Circuit, - unconstrained_functions: &[BrilligBytecode], -) -> Vec { - let mut result = Vec::with_capacity(circuit.opcodes.len() + 1); - // address of the first opcode is always 0 - result.push(0); - circuit.opcodes.iter().fold(0, |acc, opcode| { - let acc = acc - + match opcode { - Opcode::BrilligCall { id, .. } => { - unconstrained_functions[*id as usize].bytecode.len() - } - _ => 1, - }; - // push the starting address of the next opcode - result.push(acc); - acc - }); - result -} - -// TODO: update all debugger tests to use unconstrained brillig pointers #[cfg(test)] mod tests { use super::*; @@ -625,7 +849,7 @@ mod tests { acir::{ circuit::{ brillig::{BrilligInputs, BrilligOutputs}, - opcodes::BlockId, + opcodes::{BlockId, BlockType}, }, native_types::Expression, AcirField, @@ -675,7 +899,8 @@ mod tests { }]; let brillig_funcs = &vec![brillig_bytecode]; let current_witness_index = 2; - let circuit = &Circuit { current_witness_index, opcodes, ..Circuit::default() }; + let circuit = Circuit { current_witness_index, opcodes, ..Circuit::default() }; + let circuits = &vec![circuit]; let debug_symbols = vec![]; let file_map = BTreeMap::new(); @@ -687,51 +912,66 @@ mod tests { Box::new(DefaultDebugForeignCallExecutor::from_artifact(true, debug_artifact)); let mut context = DebugContext::new( &StubbedBlackBoxSolver, - circuit, + circuits, debug_artifact, initial_witness, foreign_call_executor, brillig_funcs, ); - assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(0))); + assert_eq!( + context.get_current_debug_location(), + Some(DebugLocation { circuit_id: 0, opcode_location: OpcodeLocation::Acir(0) }) + ); // Execute the first Brillig opcode (calldata copy) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( - context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) + context.get_current_debug_location(), + Some(DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 } + }) ); // execute the second Brillig opcode (const) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( - context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) + context.get_current_debug_location(), + Some(DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 } + }) ); // try to execute the third Brillig opcode (and resolve the foreign call) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( - context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) + context.get_current_debug_location(), + Some(DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 } + }) ); // retry the third Brillig opcode (foreign call should be finished) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( - context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }) + context.get_current_debug_location(), + Some(DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 } + }) ); // last Brillig opcode let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Done)); - assert_eq!(context.get_current_opcode_location(), None); + assert_eq!(context.get_current_debug_location(), None); } #[test] @@ -784,7 +1024,8 @@ mod tests { }), ]; let current_witness_index = 3; - let circuit = &Circuit { current_witness_index, opcodes, ..Circuit::default() }; + let circuit = Circuit { current_witness_index, opcodes, ..Circuit::default() }; + let circuits = &vec![circuit]; let debug_symbols = vec![]; let file_map = BTreeMap::new(); @@ -797,7 +1038,7 @@ mod tests { let brillig_funcs = &vec![brillig_bytecode]; let mut context = DebugContext::new( &StubbedBlackBoxSolver, - circuit, + circuits, debug_artifact, initial_witness, foreign_call_executor, @@ -805,28 +1046,40 @@ mod tests { ); // set breakpoint - let breakpoint_location = OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }; + let breakpoint_location = DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }, + }; assert!(context.add_breakpoint(breakpoint_location)); // execute the first ACIR opcode (Brillig block) -> should reach the breakpoint instead let result = context.step_acir_opcode(); assert!(matches!(result, DebugCommandResult::BreakpointReached(_))); - assert_eq!(context.get_current_opcode_location(), Some(breakpoint_location)); + assert_eq!(context.get_current_debug_location(), Some(breakpoint_location)); // continue execution to the next ACIR opcode let result = context.step_acir_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); - assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(1))); + assert_eq!( + context.get_current_debug_location(), + Some(DebugLocation { circuit_id: 0, opcode_location: OpcodeLocation::Acir(1) }) + ); // last ACIR opcode let result = context.step_acir_opcode(); assert!(matches!(result, DebugCommandResult::Done)); - assert_eq!(context.get_current_opcode_location(), None); + assert_eq!(context.get_current_debug_location(), None); } #[test] - fn test_address_opcode_location_mapping() { - let brillig_bytecode = BrilligBytecode { + fn test_address_debug_location_mapping() { + let brillig_one = BrilligBytecode { + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], + }; + let brillig_two = BrilligBytecode { bytecode: vec![ BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, @@ -834,22 +1087,33 @@ mod tests { ], }; - let opcodes = vec![ - Opcode::BrilligCall { id: 0, inputs: vec![], outputs: vec![], predicate: None }, - Opcode::MemoryInit { - block_id: BlockId(0), - init: vec![], - block_type: acvm::acir::circuit::opcodes::BlockType::Memory, - }, - Opcode::BrilligCall { id: 0, inputs: vec![], outputs: vec![], predicate: None }, - Opcode::AssertZero(Expression::default()), - ]; - let circuit = Circuit { opcodes, ..Circuit::default() }; + let circuit_one = Circuit { + opcodes: vec![ + Opcode::MemoryInit { + block_id: BlockId(0), + init: vec![], + block_type: BlockType::Memory, + }, + Opcode::BrilligCall { id: 0, inputs: vec![], outputs: vec![], predicate: None }, + Opcode::Call { id: 1, inputs: vec![], outputs: vec![], predicate: None }, + Opcode::AssertZero(Expression::default()), + ], + ..Circuit::default() + }; + let circuit_two = Circuit { + opcodes: vec![ + Opcode::BrilligCall { id: 1, inputs: vec![], outputs: vec![], predicate: None }, + Opcode::AssertZero(Expression::default()), + ], + ..Circuit::default() + }; + let circuits = vec![circuit_one, circuit_two]; let debug_artifact = DebugArtifact { debug_symbols: vec![], file_map: BTreeMap::new() }; - let brillig_funcs = &vec![brillig_bytecode]; + let brillig_funcs = &vec![brillig_one, brillig_two]; + let context = DebugContext::new( &StubbedBlackBoxSolver, - &circuit, + &circuits, &debug_artifact, WitnessMap::new(), Box::new(DefaultDebugForeignCallExecutor::new(true)), @@ -857,46 +1121,56 @@ mod tests { ); let locations = - (0..=7).map(|address| context.address_to_opcode_location(address)).collect::>(); + (0..=8).map(|address| context.address_to_debug_location(address)).collect::>(); // mapping from addresses to opcode locations assert_eq!( locations, vec![ - Some(OpcodeLocation::Acir(0)), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }), - Some(OpcodeLocation::Acir(1)), - Some(OpcodeLocation::Acir(2)), - Some(OpcodeLocation::Brillig { acir_index: 2, brillig_index: 1 }), - Some(OpcodeLocation::Brillig { acir_index: 2, brillig_index: 2 }), - Some(OpcodeLocation::Acir(3)), + Some(DebugLocation { circuit_id: 0, opcode_location: OpcodeLocation::Acir(0) }), + Some(DebugLocation { circuit_id: 0, opcode_location: OpcodeLocation::Acir(1) }), + Some(DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 1, brillig_index: 1 } + }), + Some(DebugLocation { circuit_id: 0, opcode_location: OpcodeLocation::Acir(2) }), + Some(DebugLocation { circuit_id: 0, opcode_location: OpcodeLocation::Acir(3) }), + Some(DebugLocation { circuit_id: 1, opcode_location: OpcodeLocation::Acir(0) }), + Some(DebugLocation { + circuit_id: 1, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 } + }), + Some(DebugLocation { + circuit_id: 1, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 } + }), + Some(DebugLocation { circuit_id: 1, opcode_location: OpcodeLocation::Acir(1) }), ] ); let addresses = locations .iter() .flatten() - .map(|location| context.opcode_location_to_address(location)) + .map(|location| context.debug_location_to_address(location)) .collect::>(); // and vice-versa - assert_eq!(addresses, (0..=7).collect::>()); + assert_eq!(addresses, (0..=8).collect::>()); // check edge cases - assert_eq!(None, context.address_to_opcode_location(8)); + assert_eq!(None, context.address_to_debug_location(9)); assert_eq!( - 0, - context.opcode_location_to_address(&OpcodeLocation::Brillig { - acir_index: 0, - brillig_index: 0 + 1, + context.debug_location_to_address(&DebugLocation { + circuit_id: 0, + opcode_location: OpcodeLocation::Brillig { acir_index: 1, brillig_index: 0 } }) ); assert_eq!( - 4, - context.opcode_location_to_address(&OpcodeLocation::Brillig { - acir_index: 2, - brillig_index: 0 + 5, + context.debug_location_to_address(&DebugLocation { + circuit_id: 1, + opcode_location: OpcodeLocation::Brillig { acir_index: 0, brillig_index: 0 } }) ); } diff --git a/noir/noir-repo/tooling/debugger/src/dap.rs b/noir/noir-repo/tooling/debugger/src/dap.rs index 77abf3093cd..cfe33a61cb5 100644 --- a/noir/noir-repo/tooling/debugger/src/dap.rs +++ b/noir/noir-repo/tooling/debugger/src/dap.rs @@ -2,12 +2,12 @@ use std::collections::BTreeMap; use std::io::{Read, Write}; use acvm::acir::circuit::brillig::BrilligBytecode; -use acvm::acir::circuit::{Circuit, OpcodeLocation}; +use acvm::acir::circuit::Circuit; use acvm::acir::native_types::WitnessMap; use acvm::{BlackBoxFunctionSolver, FieldElement}; -use crate::context::DebugCommandResult; use crate::context::DebugContext; +use crate::context::{DebugCommandResult, DebugLocation}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use dap::errors::ServerError; @@ -37,8 +37,8 @@ pub struct DapSession<'a, R: Read, W: Write, B: BlackBoxFunctionSolver, - source_breakpoints: BTreeMap>, + instruction_breakpoints: Vec<(DebugLocation, BreakpointId)>, + source_breakpoints: BTreeMap>, } enum ScopeReferences { @@ -61,14 +61,14 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< pub fn new( server: Server, solver: &'a B, - circuit: &'a Circuit, + circuits: &'a [Circuit], debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], ) -> Self { let context = DebugContext::new( solver, - circuit, + circuits, debug_artifact, initial_witness, Box::new(DefaultDebugForeignCallExecutor::from_artifact(true, debug_artifact)), @@ -100,7 +100,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< } pub fn run_loop(&mut self) -> Result<(), ServerError> { - self.running = self.context.get_current_opcode_location().is_some(); + self.running = self.context.get_current_debug_location().is_some(); if self.running && self.context.get_current_source_location().is_none() { // TODO: remove this? This is to ensure that the tool has a proper @@ -194,7 +194,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< .get_source_call_stack() .iter() .enumerate() - .map(|(index, (opcode_location, source_location))| { + .map(|(index, (debug_location, source_location))| { let line_number = self.debug_artifact.location_line_number(*source_location).unwrap(); let column_number = @@ -204,7 +204,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< Some(frame) => format!("{} {}", frame.function_name, index), None => format!("frame #{index}"), }; - let address = self.context.opcode_location_to_address(opcode_location); + let address = self.context.debug_location_to_address(debug_location); StackFrame { id: index as i64, @@ -251,18 +251,18 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< let mut instructions: Vec = vec![]; while count > 0 { - let opcode_location = if address >= 0 { - self.context.address_to_opcode_location(address as usize) + let debug_location = if address >= 0 { + self.context.address_to_debug_location(address as usize) } else { None }; - if let Some(opcode_location) = opcode_location { + if let Some(debug_location) = debug_location { instructions.push(DisassembledInstruction { address: address.to_string(), // we'll use the instruction_bytes field to render the OpcodeLocation - instruction_bytes: Some(opcode_location.to_string()), - instruction: self.context.render_opcode_at_location(&opcode_location), + instruction_bytes: Some(debug_location.to_string()), + instruction: self.context.render_opcode_at_location(&debug_location), ..DisassembledInstruction::default() }); } else { @@ -320,16 +320,16 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< self.handle_execution_result(result) } - fn find_breakpoints_at_location(&self, opcode_location: &OpcodeLocation) -> Vec { + fn find_breakpoints_at_location(&self, debug_location: &DebugLocation) -> Vec { let mut result = vec![]; for (location, id) in &self.instruction_breakpoints { - if opcode_location == location { + if debug_location == location { result.push(*id); } } for breakpoints in self.source_breakpoints.values() { for (location, id) in breakpoints { - if opcode_location == location { + if debug_location == location { result.push(*id); } } @@ -404,7 +404,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< }; // compute breakpoints to set and return - let mut breakpoints_to_set: Vec<(OpcodeLocation, i64)> = vec![]; + let mut breakpoints_to_set: Vec<(DebugLocation, i64)> = vec![]; let breakpoints: Vec = args .breakpoints .iter() @@ -420,8 +420,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< }; let Some(location) = self .context - .address_to_opcode_location(address) - .filter(|location| self.context.is_valid_opcode_location(location)) + .address_to_debug_location(address) + .filter(|location| self.context.is_valid_debug_location(location)) else { return Breakpoint { verified: false, @@ -472,7 +472,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< let Some(ref breakpoints) = &args.breakpoints else { return vec![]; }; - let mut breakpoints_to_set: Vec<(OpcodeLocation, i64)> = vec![]; + let mut breakpoints_to_set: Vec<(DebugLocation, i64)> = vec![]; let breakpoints = breakpoints .iter() .map(|breakpoint| { @@ -490,14 +490,14 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession< // TODO: line will not necessarily be the one requested; we // should do the reverse mapping and retrieve the actual source // code line number - if !self.context.is_valid_opcode_location(&location) { + if !self.context.is_valid_debug_location(&location) { return Breakpoint { verified: false, message: Some(String::from("Invalid opcode location")), ..Breakpoint::default() }; } - let breakpoint_address = self.context.opcode_location_to_address(&location); + let breakpoint_address = self.context.debug_location_to_address(&location); let instruction_reference = format!("{}", breakpoint_address); let breakpoint_id = self.get_next_breakpoint_id(); breakpoints_to_set.push((location, breakpoint_id)); @@ -612,7 +612,7 @@ pub fn run_session>( let mut session = DapSession::new( server, solver, - &program.program.functions[0], + &program.program.functions, &debug_artifact, initial_witness, &program.program.unconstrained_functions, diff --git a/noir/noir-repo/tooling/debugger/src/lib.rs b/noir/noir-repo/tooling/debugger/src/lib.rs index 9d0059ee495..37ac088ca35 100644 --- a/noir/noir-repo/tooling/debugger/src/lib.rs +++ b/noir/noir-repo/tooling/debugger/src/lib.rs @@ -9,23 +9,18 @@ use std::io::{Read, Write}; use ::dap::errors::ServerError; use ::dap::server::Server; -use acvm::acir::circuit::brillig::BrilligBytecode; -use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use acvm::{BlackBoxFunctionSolver, FieldElement}; -use noirc_artifacts::debug::DebugArtifact; - use nargo::NargoError; use noirc_driver::CompiledProgram; -pub fn debug_circuit>( - blackbox_solver: &B, - circuit: &Circuit, - debug_artifact: DebugArtifact, +pub fn run_repl_session>( + solver: &B, + program: CompiledProgram, initial_witness: WitnessMap, - unconstrained_functions: &[BrilligBytecode], -) -> Result>, NargoError> { - repl::run(blackbox_solver, circuit, &debug_artifact, initial_witness, unconstrained_functions) +) -> Result>, NargoError> { + repl::run(solver, program, initial_witness) } pub fn run_dap_loop>( diff --git a/noir/noir-repo/tooling/debugger/src/repl.rs b/noir/noir-repo/tooling/debugger/src/repl.rs index 7d8c6e0947d..d3462985642 100644 --- a/noir/noir-repo/tooling/debugger/src/repl.rs +++ b/noir/noir-repo/tooling/debugger/src/repl.rs @@ -1,11 +1,12 @@ -use crate::context::{DebugCommandResult, DebugContext}; +use crate::context::{DebugCommandResult, DebugContext, DebugLocation}; use acvm::acir::circuit::brillig::BrilligBytecode; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; -use acvm::acir::native_types::{Witness, WitnessMap}; +use acvm::acir::native_types::{Witness, WitnessMap, WitnessStack}; use acvm::brillig_vm::brillig::Opcode as BrilligOpcode; use acvm::{BlackBoxFunctionSolver, FieldElement}; use nargo::NargoError; +use noirc_driver::CompiledProgram; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use noirc_artifacts::debug::DebugArtifact; @@ -19,17 +20,21 @@ use crate::source_code_printer::print_source_code_location; pub struct ReplDebugger<'a, B: BlackBoxFunctionSolver> { context: DebugContext<'a, B>, blackbox_solver: &'a B, - circuit: &'a Circuit, debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, last_result: DebugCommandResult, + + // ACIR functions to debug + circuits: &'a [Circuit], + + // Brillig functions referenced from the ACIR circuits above unconstrained_functions: &'a [BrilligBytecode], } impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { pub fn new( blackbox_solver: &'a B, - circuit: &'a Circuit, + circuits: &'a [Circuit], debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, unconstrained_functions: &'a [BrilligBytecode], @@ -38,13 +43,13 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { Box::new(DefaultDebugForeignCallExecutor::from_artifact(true, debug_artifact)); let context = DebugContext::new( blackbox_solver, - circuit, + circuits, debug_artifact, initial_witness.clone(), foreign_call_executor, unconstrained_functions, ); - let last_result = if context.get_current_opcode_location().is_none() { + let last_result = if context.get_current_debug_location().is_none() { // handle circuit with no opcodes DebugCommandResult::Done } else { @@ -53,7 +58,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { Self { context, blackbox_solver, - circuit, + circuits, debug_artifact, initial_witness, last_result, @@ -62,42 +67,43 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } pub fn show_current_vm_status(&self) { - let location = self.context.get_current_opcode_location(); - let opcodes = self.context.get_opcodes(); + let location = self.context.get_current_debug_location(); match location { None => println!("Finished execution"), Some(location) => { - match location { + let circuit_id = location.circuit_id; + let opcodes = self.context.get_opcodes_of_circuit(circuit_id); + match &location.opcode_location { OpcodeLocation::Acir(ip) => { - println!("At opcode {}: {}", ip, opcodes[ip]); + println!("At opcode {} :: {}", location, opcodes[*ip]); } OpcodeLocation::Brillig { acir_index, brillig_index } => { let brillig_bytecode = - if let Opcode::BrilligCall { id, .. } = opcodes[acir_index] { + if let Opcode::BrilligCall { id, .. } = opcodes[*acir_index] { &self.unconstrained_functions[id as usize].bytecode } else { unreachable!("Brillig location does not contain Brillig opcodes"); }; println!( - "At opcode {}.{}: {:?}", - acir_index, brillig_index, brillig_bytecode[brillig_index] + "At opcode {} :: {:?}", + location, brillig_bytecode[*brillig_index] ); } } - let locations = self.context.get_source_location_for_opcode_location(&location); + let locations = self.context.get_source_location_for_debug_location(&location); print_source_code_location(self.debug_artifact, &locations); } } } - fn show_stack_frame(&self, index: usize, location: &OpcodeLocation) { + fn show_stack_frame(&self, index: usize, debug_location: &DebugLocation) { let opcodes = self.context.get_opcodes(); - match location { + match &debug_location.opcode_location { OpcodeLocation::Acir(instruction_pointer) => { println!( - "Frame #{index}, opcode {}: {}", - instruction_pointer, opcodes[*instruction_pointer] + "Frame #{index}, opcode {} :: {}", + debug_location, opcodes[*instruction_pointer] ) } OpcodeLocation::Brillig { acir_index, brillig_index } => { @@ -108,12 +114,12 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { unreachable!("Brillig location does not contain Brillig opcodes"); }; println!( - "Frame #{index}, opcode {}.{}: {:?}", - acir_index, brillig_index, brillig_bytecode[*brillig_index] + "Frame #{index}, opcode {} :: {:?}", + debug_location, brillig_bytecode[*brillig_index] ); } } - let locations = self.context.get_source_location_for_opcode_location(location); + let locations = self.context.get_source_location_for_debug_location(debug_location); print_source_code_location(self.debug_artifact, &locations); } @@ -130,8 +136,21 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } fn display_opcodes(&self) { - let opcodes = self.context.get_opcodes(); - let current_opcode_location = self.context.get_current_opcode_location(); + for i in 0..self.circuits.len() { + self.display_opcodes_of_circuit(i as u32); + } + } + + fn display_opcodes_of_circuit(&self, circuit_id: u32) { + let current_opcode_location = + self.context.get_current_debug_location().and_then(|debug_location| { + if debug_location.circuit_id == circuit_id { + Some(debug_location.opcode_location) + } else { + None + } + }); + let opcodes = self.context.get_opcodes_of_circuit(circuit_id); let current_acir_index = match current_opcode_location { Some(OpcodeLocation::Acir(ip)) => Some(ip), Some(OpcodeLocation::Brillig { acir_index, .. }) => Some(acir_index), @@ -144,7 +163,10 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { let outer_marker = |acir_index| { if current_acir_index == Some(acir_index) { "->" - } else if self.context.is_breakpoint_set(&OpcodeLocation::Acir(acir_index)) { + } else if self.context.is_breakpoint_set(&DebugLocation { + circuit_id, + opcode_location: OpcodeLocation::Acir(acir_index), + }) { " *" } else { "" @@ -153,10 +175,10 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { let brillig_marker = |acir_index, brillig_index| { if current_acir_index == Some(acir_index) && brillig_index == current_brillig_index { "->" - } else if self - .context - .is_breakpoint_set(&OpcodeLocation::Brillig { acir_index, brillig_index }) - { + } else if self.context.is_breakpoint_set(&DebugLocation { + circuit_id, + opcode_location: OpcodeLocation::Brillig { acir_index, brillig_index }, + }) { " *" } else { "" @@ -165,7 +187,8 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { let print_brillig_bytecode = |acir_index, bytecode: &[BrilligOpcode]| { for (brillig_index, brillig_opcode) in bytecode.iter().enumerate() { println!( - "{:>3}.{:<2} |{:2} {:?}", + "{:>2}:{:>3}.{:<2} |{:2} {:?}", + circuit_id, acir_index, brillig_index, brillig_marker(acir_index, brillig_index), @@ -178,33 +201,33 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { match &opcode { Opcode::BrilligCall { id, inputs, outputs, .. } => { println!( - "{:>3} {:2} BRILLIG CALL id={} inputs={:?}", - acir_index, marker, id, inputs + "{:>2}:{:>3} {:2} BRILLIG CALL id={} inputs={:?}", + circuit_id, acir_index, marker, id, inputs ); - println!(" | outputs={:?}", outputs); + println!(" | outputs={:?}", outputs); let bytecode = &self.unconstrained_functions[*id as usize].bytecode; print_brillig_bytecode(acir_index, bytecode); } - _ => println!("{:>3} {:2} {:?}", acir_index, marker, opcode), + _ => println!("{:>2}:{:>3} {:2} {:?}", circuit_id, acir_index, marker, opcode), } } } - fn add_breakpoint_at(&mut self, location: OpcodeLocation) { - if !self.context.is_valid_opcode_location(&location) { - println!("Invalid opcode location {location}"); + fn add_breakpoint_at(&mut self, location: DebugLocation) { + if !self.context.is_valid_debug_location(&location) { + println!("Invalid location {location}"); } else if self.context.add_breakpoint(location) { - println!("Added breakpoint at opcode {location}"); + println!("Added breakpoint at {location}"); } else { - println!("Breakpoint at opcode {location} already set"); + println!("Breakpoint at {location} already set"); } } - fn delete_breakpoint_at(&mut self, location: OpcodeLocation) { + fn delete_breakpoint_at(&mut self, location: DebugLocation) { if self.context.delete_breakpoint(&location) { - println!("Breakpoint at opcode {location} deleted"); + println!("Breakpoint at {location} deleted"); } else { - println!("Breakpoint at opcode {location} not set"); + println!("Breakpoint at {location} not set"); } } @@ -281,20 +304,19 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } fn restart_session(&mut self) { - let breakpoints: Vec = - self.context.iterate_breakpoints().copied().collect(); + let breakpoints: Vec = self.context.iterate_breakpoints().copied().collect(); let foreign_call_executor = Box::new(DefaultDebugForeignCallExecutor::from_artifact(true, self.debug_artifact)); self.context = DebugContext::new( self.blackbox_solver, - self.circuit, + self.circuits, self.debug_artifact, self.initial_witness.clone(), foreign_call_executor, self.unconstrained_functions, ); - for opcode_location in breakpoints { - self.context.add_breakpoint(opcode_location); + for debug_location in breakpoints { + self.context.add_breakpoint(debug_location); } self.last_result = DebugCommandResult::Ok; println!("Restarted debugging session."); @@ -372,21 +394,23 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { self.context.is_solved() } - fn finalize(self) -> WitnessMap { + fn finalize(self) -> WitnessStack { self.context.finalize() } } pub fn run>( blackbox_solver: &B, - circuit: &Circuit, - debug_artifact: &DebugArtifact, + program: CompiledProgram, initial_witness: WitnessMap, - unconstrained_functions: &[BrilligBytecode], -) -> Result>, NargoError> { +) -> Result>, NargoError> { + let circuits = &program.program.functions; + let debug_artifact = + &DebugArtifact { debug_symbols: program.debug, file_map: program.file_map }; + let unconstrained_functions = &program.program.unconstrained_functions; let context = RefCell::new(ReplDebugger::new( blackbox_solver, - circuit, + circuits, debug_artifact, initial_witness, unconstrained_functions, @@ -480,7 +504,7 @@ pub fn run>( "break", command! { "add a breakpoint at an opcode location", - (LOCATION:OpcodeLocation) => |location| { + (LOCATION:DebugLocation) => |location| { ref_context.borrow_mut().add_breakpoint_at(location); Ok(CommandStatus::Done) } @@ -490,7 +514,7 @@ pub fn run>( "delete", command! { "delete breakpoint at an opcode location", - (LOCATION:OpcodeLocation) => |location| { + (LOCATION:DebugLocation) => |location| { ref_context.borrow_mut().delete_breakpoint_at(location); Ok(CommandStatus::Done) } @@ -576,8 +600,8 @@ pub fn run>( drop(repl); if context.borrow().is_solved() { - let solved_witness = context.into_inner().finalize(); - Ok(Some(solved_witness)) + let solved_witness_stack = context.into_inner().finalize(); + Ok(Some(solved_witness_stack)) } else { Ok(None) } diff --git a/noir/noir-repo/tooling/debugger/tests/debug.rs b/noir/noir-repo/tooling/debugger/tests/debug.rs index 313b6b30591..2dca6b95f0e 100644 --- a/noir/noir-repo/tooling/debugger/tests/debug.rs +++ b/noir/noir-repo/tooling/debugger/tests/debug.rs @@ -12,7 +12,7 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); - let timeout_seconds = 20; + let timeout_seconds = 25; let mut dbg_session = spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index e47dfff714a..28ba5425fe7 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -21,7 +21,7 @@ use async_lsp::{ use fm::{codespan_files as files, FileManager}; use fxhash::FxHashSet; use lsp_types::{ - request::{PrepareRenameRequest, References, Rename}, + request::{HoverRequest, PrepareRenameRequest, References, Rename}, CodeLens, }; use nargo::{ @@ -46,7 +46,7 @@ use notifications::{ }; use requests::{ on_code_lens_request, on_formatting, on_goto_declaration_request, on_goto_definition_request, - on_goto_type_definition_request, on_initialize, on_prepare_rename_request, + on_goto_type_definition_request, on_hover_request, on_initialize, on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, on_shutdown, on_test_run_request, on_tests_request, }; @@ -129,6 +129,7 @@ impl NargoLspService { .request::(on_references_request) .request::(on_prepare_rename_request) .request::(on_rename_request) + .request::(on_hover_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -229,43 +230,74 @@ fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( } } -pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result { - if let Some(toml_path) = find_file_manifest(file_path) { - resolve_workspace_from_toml( - &toml_path, - PackageSelection::All, - Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), - ) - .map_err(|err| LspError::WorkspaceResolutionError(err.to_string())) - } else { - let Some(parent_folder) = file_path - .parent() - .and_then(|f| f.file_name()) - .and_then(|file_name_os_str| file_name_os_str.to_str()) - else { - return Err(LspError::WorkspaceResolutionError(format!( - "Could not resolve parent folder for file: {:?}", - file_path - ))); - }; - let assumed_package = Package { - version: None, - compiler_required_version: Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), - root_dir: PathBuf::from(parent_folder), - package_type: PackageType::Binary, - entry_path: PathBuf::from(file_path), - name: CrateName::from_str(parent_folder) - .map_err(|err| LspError::WorkspaceResolutionError(err.to_string()))?, - dependencies: BTreeMap::new(), - }; - let workspace = Workspace { - root_dir: PathBuf::from(parent_folder), - members: vec![assumed_package], - selected_package_index: Some(0), - is_assumed: true, - }; - Ok(workspace) +pub(crate) fn resolve_workspace_for_source_path( + file_path: &Path, + root_path: &Option, +) -> Result { + // If there's a LSP root path, starting from file_path go up the directory tree + // searching for Nargo.toml files. The last one we find is the one we'll use + // (we'll assume Noir workspaces aren't nested) + if let Some(root_path) = root_path { + let mut current_path = file_path; + let mut current_toml_path = None; + while current_path.starts_with(root_path) { + if let Some(toml_path) = find_file_manifest(current_path) { + current_toml_path = Some(toml_path); + + if let Some(next_path) = current_path.parent() { + current_path = next_path; + } else { + break; + } + } else { + break; + } + } + + if let Some(toml_path) = current_toml_path { + return resolve_workspace_from_toml( + &toml_path, + PackageSelection::All, + Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), + ) + .map_err(|err| LspError::WorkspaceResolutionError(err.to_string())); + } } + + let Some(parent_folder) = file_path + .parent() + .and_then(|f| f.file_name()) + .and_then(|file_name_os_str| file_name_os_str.to_str()) + else { + return Err(LspError::WorkspaceResolutionError(format!( + "Could not resolve parent folder for file: {:?}", + file_path + ))); + }; + let assumed_package = Package { + version: None, + compiler_required_version: Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), + root_dir: PathBuf::from(parent_folder), + package_type: PackageType::Binary, + entry_path: PathBuf::from(file_path), + name: CrateName::from_str(parent_folder) + .map_err(|err| LspError::WorkspaceResolutionError(err.to_string()))?, + dependencies: BTreeMap::new(), + }; + let workspace = Workspace { + root_dir: PathBuf::from(parent_folder), + members: vec![assumed_package], + selected_package_index: Some(0), + is_assumed: true, + }; + Ok(workspace) +} + +pub(crate) fn workspace_package_for_file<'a>( + workspace: &'a Workspace, + file_path: &Path, +) -> Option<&'a Package> { + workspace.members.iter().find(|package| file_path.starts_with(&package.root_dir)) } pub(crate) fn prepare_package<'file_manager, 'parsed_files>( @@ -373,7 +405,8 @@ fn prepare_package_from_source_string() { let mut state = LspState::new(&client, acvm::blackbox_solver::StubbedBlackBoxSolver); let (mut context, crate_id) = crate::prepare_source(source.to_string(), &mut state); - let _check_result = noirc_driver::check_crate(&mut context, crate_id, false, false, false); + let _check_result = + noirc_driver::check_crate(&mut context, crate_id, false, false, false, None); let main_func_id = context.get_main_function(&crate_id); assert!(main_func_id.is_some()); } diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index ed0ac13fae5..a5ded236514 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -14,7 +14,7 @@ use crate::types::{ use crate::{ byte_span_to_range, get_package_tests_in_crate, parse_diff, prepare_source, - resolve_workspace_for_source_path, LspState, + resolve_workspace_for_source_path, workspace_package_for_file, LspState, }; pub(super) fn on_initialized( @@ -39,7 +39,7 @@ pub(super) fn on_did_open_text_document( let document_uri = params.text_document.uri; - match process_noir_document(document_uri, state) { + match process_workspace_for_noir_document(document_uri, state) { Ok(_) => { state.open_documents_count += 1; ControlFlow::Continue(()) @@ -55,11 +55,14 @@ pub(super) fn on_did_change_text_document( let text = params.content_changes.into_iter().next().unwrap().text; state.input_files.insert(params.text_document.uri.to_string(), text.clone()); + let file_path = params.text_document.uri.to_file_path().unwrap(); + let (mut context, crate_id) = prepare_source(text, state); - let _ = check_crate(&mut context, crate_id, false, false, false); + let _ = check_crate(&mut context, crate_id, false, false, false, None); let workspace = match resolve_workspace_for_source_path( params.text_document.uri.to_file_path().unwrap().as_path(), + &state.root_path, ) { Ok(workspace) => workspace, Err(lsp_error) => { @@ -70,7 +73,8 @@ pub(super) fn on_did_change_text_document( .into())) } }; - let package = match workspace.members.first() { + + let package = match workspace_package_for_file(&workspace, &file_path) { Some(package) => package, None => { return ControlFlow::Break(Err(ResponseError::new( @@ -110,13 +114,16 @@ pub(super) fn on_did_save_text_document( ) -> ControlFlow> { let document_uri = params.text_document.uri; - match process_noir_document(document_uri, state) { + match process_workspace_for_noir_document(document_uri, state) { Ok(_) => ControlFlow::Continue(()), Err(err) => ControlFlow::Break(Err(err)), } } -fn process_noir_document( +// Given a Noir document, find the workspace it's contained in (an assumed workspace is created if +// it's only contained in a package), then type-checks the workspace's packages, +// caching code lenses and type definitions, and notifying about compilation errors. +pub(crate) fn process_workspace_for_noir_document( document_uri: lsp_types::Url, state: &mut LspState, ) -> Result<(), async_lsp::Error> { @@ -124,9 +131,10 @@ fn process_noir_document( ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") })?; - let workspace = resolve_workspace_for_source_path(&file_path).map_err(|lsp_error| { - ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) - })?; + let workspace = + resolve_workspace_for_source_path(&file_path, &state.root_path).map_err(|lsp_error| { + ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) + })?; let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); @@ -139,10 +147,11 @@ fn process_noir_document( let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); - let file_diagnostics = match check_crate(&mut context, crate_id, false, false, false) { - Ok(((), warnings)) => warnings, - Err(errors_and_warnings) => errors_and_warnings, - }; + let file_diagnostics = + match check_crate(&mut context, crate_id, false, false, false, None) { + Ok(((), warnings)) => warnings, + Err(errors_and_warnings) => errors_and_warnings, + }; let package_root_dir: String = package.root_dir.as_os_str().to_string_lossy().into(); @@ -190,6 +199,7 @@ fn process_noir_document( let severity = match diagnostic.kind { DiagnosticKind::Error => DiagnosticSeverity::ERROR, DiagnosticKind::Warning => DiagnosticSeverity::WARNING, + DiagnosticKind::Info => DiagnosticSeverity::INFORMATION, DiagnosticKind::Bug => DiagnosticSeverity::WARNING, }; Some(Diagnostic { diff --git a/noir/noir-repo/tooling/lsp/src/requests/code_lens_request.rs b/noir/noir-repo/tooling/lsp/src/requests/code_lens_request.rs index 744bddedd9d..11ea4fa74bf 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/code_lens_request.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/code_lens_request.rs @@ -61,13 +61,17 @@ fn on_code_lens_request_inner( ResponseError::new(ErrorCode::REQUEST_FAILED, "Could not read file from disk") })?; - let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); - let package = workspace.members.first().unwrap(); + let workspace = + resolve_workspace_for_source_path(file_path.as_path(), &state.root_path).unwrap(); + + let package = crate::workspace_package_for_file(&workspace, &file_path).ok_or_else(|| { + ResponseError::new(ErrorCode::REQUEST_FAILED, "Could not find package for file") + })?; let (mut context, crate_id) = prepare_source(source_string, state); // We ignore the warnings and errors produced by compilation for producing code lenses // because we can still get the test functions even if compilation fails - let _ = check_crate(&mut context, crate_id, false, false, false); + let _ = check_crate(&mut context, crate_id, false, false, false, None); let collected_lenses = collect_lenses_for_package(&context, crate_id, &workspace, package, None); diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs index 627cd8d203e..bd0f0afb827 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_declaration.rs @@ -20,10 +20,10 @@ fn on_goto_definition_inner( state: &mut LspState, params: GotoDeclarationParams, ) -> Result { - process_request(state, params.text_document_position_params, |location, interner, files| { - interner.get_declaration_location_from(location).and_then(|found_location| { + process_request(state, params.text_document_position_params, |args| { + args.interner.get_declaration_location_from(args.location).and_then(|found_location| { let file_id = found_location.file; - let definition_position = to_lsp_location(files, file_id, found_location.span)?; + let definition_position = to_lsp_location(args.files, file_id, found_location.span)?; let response = GotoDeclarationResponse::from(definition_position).to_owned(); Some(response) }) diff --git a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs index 3713e8b646a..72fac676a72 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/goto_definition.rs @@ -29,15 +29,16 @@ fn on_goto_definition_inner( params: GotoDefinitionParams, return_type_location_instead: bool, ) -> Result { - process_request(state, params.text_document_position_params, |location, interner, files| { - interner.get_definition_location_from(location, return_type_location_instead).and_then( - |found_location| { + process_request(state, params.text_document_position_params, |args| { + args.interner + .get_definition_location_from(args.location, return_type_location_instead) + .and_then(|found_location| { let file_id = found_location.file; - let definition_position = to_lsp_location(files, file_id, found_location.span)?; + let definition_position = + to_lsp_location(args.files, file_id, found_location.span)?; let response = GotoDefinitionResponse::from(definition_position).to_owned(); Some(response) - }, - ) + }) }) } diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs new file mode 100644 index 00000000000..161fd20f555 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -0,0 +1,641 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; +use noirc_frontend::{ + ast::Visibility, + graph::CrateId, + hir::def_map::ModuleId, + hir_def::stmt::HirPattern, + macros_api::{NodeInterner, StructId}, + node_interner::{ + DefinitionId, DefinitionKind, FuncId, GlobalId, ReferenceId, TraitId, TypeAliasId, + }, + Generics, Type, +}; + +use crate::LspState; + +use super::{process_request, to_lsp_location, ProcessRequestCallbackArgs}; + +pub(crate) fn on_hover_request( + state: &mut LspState, + params: HoverParams, +) -> impl Future, ResponseError>> { + let result = process_request(state, params.text_document_position_params, |args| { + args.interner.reference_at_location(args.location).map(|reference| { + let location = args.interner.reference_location(reference); + let lsp_location = to_lsp_location(args.files, location.file, location.span); + Hover { + range: lsp_location.map(|location| location.range), + contents: HoverContents::Markup(MarkupContent { + kind: MarkupKind::Markdown, + value: format_reference(reference, &args), + }), + } + }) + }); + + future::ready(result) +} + +fn format_reference(reference: ReferenceId, args: &ProcessRequestCallbackArgs) -> String { + match reference { + ReferenceId::Module(id) => format_module(id, args), + ReferenceId::Struct(id) => format_struct(id, args), + ReferenceId::StructMember(id, field_index) => format_struct_member(id, field_index, args), + ReferenceId::Trait(id) => format_trait(id, args), + ReferenceId::Global(id) => format_global(id, args), + ReferenceId::Function(id) => format_function(id, args), + ReferenceId::Alias(id) => format_alias(id, args), + ReferenceId::Local(id) => format_local(id, args), + ReferenceId::Reference(location, _) => { + format_reference(args.interner.find_referenced(location).unwrap(), args) + } + } +} +fn format_module(id: ModuleId, args: &ProcessRequestCallbackArgs) -> String { + let module_attributes = args.interner.module_attributes(&id); + + let mut string = String::new(); + if format_parent_module_from_module_id( + &ModuleId { krate: id.krate, local_id: module_attributes.parent }, + args, + &mut string, + ) { + string.push('\n'); + } + string.push_str(" "); + string.push_str("mod "); + string.push_str(&module_attributes.name); + string +} + +fn format_struct(id: StructId, args: &ProcessRequestCallbackArgs) -> String { + let struct_type = args.interner.get_struct(id); + let struct_type = struct_type.borrow(); + + let mut string = String::new(); + if format_parent_module(ReferenceId::Struct(id), args, &mut string) { + string.push('\n'); + } + string.push_str(" "); + string.push_str("struct "); + string.push_str(&struct_type.name.0.contents); + format_generics(&struct_type.generics, &mut string); + string.push_str(" {\n"); + for (field_name, field_type) in struct_type.get_fields_as_written() { + string.push_str(" "); + string.push_str(&field_name); + string.push_str(": "); + string.push_str(&format!("{}", field_type)); + string.push_str(",\n"); + } + string.push_str(" }"); + string +} + +fn format_struct_member( + id: StructId, + field_index: usize, + args: &ProcessRequestCallbackArgs, +) -> String { + let struct_type = args.interner.get_struct(id); + let struct_type = struct_type.borrow(); + let (field_name, field_type) = struct_type.field_at(field_index); + + let mut string = String::new(); + if format_parent_module(ReferenceId::Struct(id), args, &mut string) { + string.push_str("::"); + } + string.push_str(&struct_type.name.0.contents); + string.push('\n'); + string.push_str(" "); + string.push_str(&field_name.0.contents); + string.push_str(": "); + string.push_str(&format!("{}", field_type)); + string +} + +fn format_trait(id: TraitId, args: &ProcessRequestCallbackArgs) -> String { + let a_trait = args.interner.get_trait(id); + + let mut string = String::new(); + if format_parent_module(ReferenceId::Trait(id), args, &mut string) { + string.push('\n'); + } + string.push_str(" "); + string.push_str("trait "); + string.push_str(&a_trait.name.0.contents); + format_generics(&a_trait.generics, &mut string); + string +} + +fn format_global(id: GlobalId, args: &ProcessRequestCallbackArgs) -> String { + let global_info = args.interner.get_global(id); + let definition_id = global_info.definition_id; + let typ = args.interner.definition_type(definition_id); + + let mut string = String::new(); + if format_parent_module(ReferenceId::Global(id), args, &mut string) { + string.push('\n'); + } + string.push_str(" "); + string.push_str("global "); + string.push_str(&global_info.ident.0.contents); + string.push_str(": "); + string.push_str(&format!("{}", typ)); + string +} + +fn format_function(id: FuncId, args: &ProcessRequestCallbackArgs) -> String { + let func_meta = args.interner.function_meta(&id); + let func_name_definition_id = args.interner.definition(func_meta.name.id); + + let mut string = String::new(); + let formatted_parent_module = + format_parent_module(ReferenceId::Function(id), args, &mut string); + let formatted_parent_struct = if let Some(struct_id) = func_meta.struct_id { + let struct_type = args.interner.get_struct(struct_id); + let struct_type = struct_type.borrow(); + if formatted_parent_module { + string.push_str("::"); + } + string.push_str(&struct_type.name.0.contents); + true + } else { + false + }; + if formatted_parent_module || formatted_parent_struct { + string.push('\n'); + } + string.push_str(" "); + string.push_str("fn "); + string.push_str(&func_name_definition_id.name); + format_generics(&func_meta.direct_generics, &mut string); + string.push('('); + let parameters = &func_meta.parameters; + for (index, (pattern, typ, visibility)) in parameters.iter().enumerate() { + format_pattern(pattern, args.interner, &mut string); + if !pattern_is_self(pattern, args.interner) { + string.push_str(": "); + if matches!(visibility, Visibility::Public) { + string.push_str("pub "); + } + string.push_str(&format!("{}", typ)); + } + if index != parameters.len() - 1 { + string.push_str(", "); + } + } + + string.push(')'); + + let return_type = func_meta.return_type(); + match return_type { + Type::Unit => (), + _ => { + string.push_str(" -> "); + string.push_str(&format!("{}", return_type)); + } + } + + string +} + +fn format_alias(id: TypeAliasId, args: &ProcessRequestCallbackArgs) -> String { + let type_alias = args.interner.get_type_alias(id); + let type_alias = type_alias.borrow(); + + let mut string = String::new(); + format_parent_module(ReferenceId::Alias(id), args, &mut string); + string.push('\n'); + string.push_str(" "); + string.push_str("type "); + string.push_str(&type_alias.name.0.contents); + string.push_str(" = "); + string.push_str(&format!("{}", &type_alias.typ)); + string +} + +fn format_local(id: DefinitionId, args: &ProcessRequestCallbackArgs) -> String { + let definition_info = args.interner.definition(id); + let DefinitionKind::Local(expr_id) = definition_info.kind else { + panic!("Expected a local reference to reference a local definition") + }; + let typ = args.interner.definition_type(id); + + let mut string = String::new(); + string.push_str(" "); + if definition_info.comptime { + string.push_str("comptime "); + } + if expr_id.is_some() { + string.push_str("let "); + } + if definition_info.mutable { + if expr_id.is_none() { + string.push_str("let "); + } + string.push_str("mut "); + } + string.push_str(&definition_info.name); + if !matches!(typ, Type::Error) { + string.push_str(": "); + string.push_str(&format!("{}", typ)); + } + string +} + +fn format_generics(generics: &Generics, string: &mut String) { + if generics.is_empty() { + return; + } + + string.push('<'); + for (index, generic) in generics.iter().enumerate() { + string.push_str(&generic.name); + if index != generics.len() - 1 { + string.push_str(", "); + } + } + string.push('>'); +} +fn format_pattern(pattern: &HirPattern, interner: &NodeInterner, string: &mut String) { + match pattern { + HirPattern::Identifier(ident) => { + let definition = interner.definition(ident.id); + string.push_str(&definition.name); + } + HirPattern::Mutable(pattern, _) => { + string.push_str("mut "); + format_pattern(pattern, interner, string); + } + HirPattern::Tuple(..) | HirPattern::Struct(..) => { + string.push('_'); + } + } +} + +fn pattern_is_self(pattern: &HirPattern, interner: &NodeInterner) -> bool { + match pattern { + HirPattern::Identifier(ident) => { + let definition = interner.definition(ident.id); + definition.name == "self" + } + HirPattern::Mutable(pattern, _) => pattern_is_self(pattern, interner), + HirPattern::Tuple(..) | HirPattern::Struct(..) => false, + } +} + +fn format_parent_module( + referenced: ReferenceId, + args: &ProcessRequestCallbackArgs, + string: &mut String, +) -> bool { + let Some(module) = args.interner.reference_module(referenced) else { + return false; + }; + + format_parent_module_from_module_id(module, args, string) +} + +fn format_parent_module_from_module_id( + module: &ModuleId, + args: &ProcessRequestCallbackArgs, + string: &mut String, +) -> bool { + let crate_id = module.krate; + let crate_name = match crate_id { + CrateId::Root(_) => Some(args.root_crate_name.clone()), + CrateId::Crate(_) => args + .root_crate_dependencies + .iter() + .find(|dep| dep.crate_id == crate_id) + .map(|dep| format!("{}", dep.name)), + CrateId::Stdlib(_) => Some("std".to_string()), + CrateId::Dummy => None, + }; + + let wrote_crate = if let Some(crate_name) = crate_name { + string.push_str(" "); + string.push_str(&crate_name); + true + } else { + false + }; + + let Some(module_attributes) = args.interner.try_module_attributes(module) else { + return wrote_crate; + }; + + if wrote_crate { + string.push_str("::"); + } else { + string.push_str(" "); + } + + let mut segments = Vec::new(); + let mut current_attributes = module_attributes; + while let Some(parent_attributes) = args.interner.try_module_attributes(&ModuleId { + krate: module.krate, + local_id: current_attributes.parent, + }) { + segments.push(&parent_attributes.name); + current_attributes = parent_attributes; + } + + for segment in segments.iter().rev() { + string.push_str(segment); + string.push_str("::"); + } + + string.push_str(&module_attributes.name); + + true +} + +#[cfg(test)] +mod hover_tests { + use crate::test_utils; + + use super::*; + use lsp_types::{ + Position, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkDoneProgressParams, + }; + use tokio::test; + + async fn assert_hover(directory: &str, file: &str, position: Position, expected_text: &str) { + let (mut state, noir_text_document) = test_utils::init_lsp_server(directory).await; + + // noir_text_document is always `src/main.nr` in the workspace directory, so let's go to the workspace dir + let noir_text_document = noir_text_document.to_file_path().unwrap(); + let workspace_dir = noir_text_document.parent().unwrap().parent().unwrap(); + + let file_uri = Url::from_file_path(workspace_dir.join(file)).unwrap(); + + let hover = on_hover_request( + &mut state, + HoverParams { + text_document_position_params: TextDocumentPositionParams { + text_document: TextDocumentIdentifier { uri: file_uri }, + position, + }, + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + }, + ) + .await + .expect("Could not execute hover") + .unwrap(); + + let HoverContents::Markup(markup) = hover.contents else { + panic!("Expected hover contents to be Markup"); + }; + + assert_eq!(markup.value, expected_text); + } + + #[test] + async fn hover_on_module() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 6, character: 9 }, + r#" one + mod subone"#, + ) + .await; + } + + #[test] + async fn hover_on_struct() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 9, character: 20 }, + r#" one::subone + struct SubOneStruct { + some_field: i32, + some_other_field: Field, + }"#, + ) + .await; + } + + #[test] + async fn hover_on_generic_struct() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 46, character: 17 }, + r#" one::subone + struct GenericStruct { + }"#, + ) + .await; + } + + #[test] + async fn hover_on_struct_member() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 9, character: 35 }, + r#" one::subone::SubOneStruct + some_field: i32"#, + ) + .await; + } + + #[test] + async fn hover_on_trait() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 12, character: 17 }, + r#" one::subone + trait SomeTrait"#, + ) + .await; + } + + #[test] + async fn hover_on_global() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 15, character: 25 }, + r#" one::subone + global some_global: Field"#, + ) + .await; + } + + #[test] + async fn hover_on_function() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 3, character: 4 }, + r#" one + fn function_one()"#, + ) + .await; + } + + #[test] + async fn hover_on_local_function() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 2, character: 7 }, + r#" two + fn function_two()"#, + ) + .await; + } + + #[test] + async fn hover_on_struct_method() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 20, character: 6 }, + r#" one::subone::SubOneStruct + fn foo(self, x: i32, y: i32) -> Field"#, + ) + .await; + } + + #[test] + async fn hover_on_local_var() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 25, character: 12 }, + " let regular_var: Field", + ) + .await; + } + + #[test] + async fn hover_on_local_mut_var() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 27, character: 4 }, + " let mut mutable_var: Field", + ) + .await; + } + + #[test] + async fn hover_on_parameter() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 31, character: 12 }, + " some_param: i32", + ) + .await; + } + + #[test] + async fn hover_on_alias() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 34, character: 17 }, + r#" one::subone + type SomeAlias = i32"#, + ) + .await; + } + + #[test] + async fn hover_on_trait_on_call() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 39, character: 17 }, + r#" std::default + trait Default"#, + ) + .await; + } + + #[test] + async fn hover_on_std_module_in_use() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 36, character: 9 }, + r#" std + mod default"#, + ) + .await; + } + + #[test] + async fn hover_on_crate_module_in_call() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 15, character: 17 }, + r#" one + mod subone"#, + ) + .await; + } + + #[test] + async fn hover_on_module_without_crate_or_std_prefix() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 43, character: 4 }, + r#" two + mod other"#, + ) + .await; + } + + #[test] + async fn hover_on_module_with_crate_prefix() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 44, character: 11 }, + r#" two + mod other"#, + ) + .await; + } + + #[test] + async fn hover_on_module_on_struct_constructor() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 19, character: 12 }, + r#" one + mod subone"#, + ) + .await; + } + + #[test] + async fn hover_on_type_inside_generic_arguments() { + assert_hover( + "workspace", + "two/src/lib.nr", + Position { line: 51, character: 30 }, + r#" one::subone + struct SubOneStruct { + some_field: i32, + some_other_field: Field, + }"#, + ) + .await; + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 266b5b124ac..e029750e589 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -1,4 +1,4 @@ -use std::future::Future; +use std::{collections::HashMap, future::Future}; use crate::{ parse_diff, resolve_workspace_for_source_path, @@ -14,7 +14,7 @@ use lsp_types::{ use nargo::insert_all_files_for_workspace_into_file_manager; use nargo_fmt::Config; use noirc_driver::file_manager_with_stdlib; -use noirc_frontend::macros_api::NodeInterner; +use noirc_frontend::{graph::Dependency, macros_api::NodeInterner}; use serde::{Deserialize, Serialize}; use crate::{ @@ -35,6 +35,7 @@ use crate::{ mod code_lens_request; mod goto_declaration; mod goto_definition; +mod hover; mod profile_run; mod references; mod rename; @@ -44,9 +45,10 @@ mod tests; pub(crate) use { code_lens_request::collect_lenses_for_package, code_lens_request::on_code_lens_request, goto_declaration::on_goto_declaration_request, goto_definition::on_goto_definition_request, - goto_definition::on_goto_type_definition_request, profile_run::on_profile_run_request, - references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + goto_definition::on_goto_type_definition_request, hover::on_hover_request, + profile_run::on_profile_run_request, references::on_references_request, + rename::on_prepare_rename_request, rename::on_rename_request, test_run::on_test_run_request, + tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -127,6 +129,11 @@ pub(crate) fn on_initialize( work_done_progress: None, }, })), + hover_provider: Some(lsp_types::OneOf::Right(lsp_types::HoverOptions { + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + })), }, server_info: None, }) @@ -264,21 +271,33 @@ pub(crate) fn on_shutdown( async { Ok(()) } } +pub(crate) struct ProcessRequestCallbackArgs<'a> { + location: noirc_errors::Location, + files: &'a FileMap, + interner: &'a NodeInterner, + interners: &'a HashMap, + root_crate_name: String, + root_crate_dependencies: &'a Vec, +} + pub(crate) fn process_request( state: &mut LspState, text_document_position_params: TextDocumentPositionParams, callback: F, ) -> Result where - F: FnOnce(noirc_errors::Location, &NodeInterner, &FileMap) -> T, + F: FnOnce(ProcessRequestCallbackArgs) -> T, { let file_path = text_document_position_params.text_document.uri.to_file_path().map_err(|_| { ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") })?; - let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); - let package = workspace.members.first().unwrap(); + let workspace = + resolve_workspace_for_source_path(file_path.as_path(), &state.root_path).unwrap(); + let package = crate::workspace_package_for_file(&workspace, &file_path).ok_or_else(|| { + ResponseError::new(ErrorCode::REQUEST_FAILED, "Could not find package for file") + })?; let package_root_path: String = package.root_dir.as_os_str().to_string_lossy().into(); @@ -294,7 +313,7 @@ where interner = def_interner; } else { // We ignore the warnings and errors produced by compilation while resolving the definition - let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, false); + let _ = noirc_driver::check_crate(&mut context, crate_id, false, false, false, None); interner = &context.def_interner; } @@ -306,7 +325,88 @@ where &text_document_position_params.position, )?; - Ok(callback(location, interner, files)) + Ok(callback(ProcessRequestCallbackArgs { + location, + files, + interner, + interners: &state.cached_definitions, + root_crate_name: package.name.to_string(), + root_crate_dependencies: &context.crate_graph[context.root_crate_id()].dependencies, + })) +} +pub(crate) fn find_all_references_in_workspace( + location: noirc_errors::Location, + interner: &NodeInterner, + cached_interners: &HashMap, + files: &FileMap, + include_declaration: bool, + include_self_type_name: bool, +) -> Option> { + // First find the node that's referenced by the given location, if any + let referenced = interner.find_referenced(location); + + if let Some(referenced) = referenced { + // If we found the referenced node, find its location + let referenced_location = interner.reference_location(referenced); + + // Now we find all references that point to this location, in all interners + // (there's one interner per package, and all interners in a workspace rely on the + // same FileManager so a Location/FileId in one package is the same as in another package) + let mut locations = find_all_references( + referenced_location, + interner, + files, + include_declaration, + include_self_type_name, + ); + for interner in cached_interners.values() { + locations.extend(find_all_references( + referenced_location, + interner, + files, + include_declaration, + include_self_type_name, + )); + } + + // The LSP client usually removes duplicate loctions, but we do it here just in case they don't + locations.sort_by_key(|location| { + ( + location.uri.to_string(), + location.range.start.line, + location.range.start.character, + location.range.end.line, + location.range.end.character, + ) + }); + locations.dedup(); + + if locations.is_empty() { + None + } else { + Some(locations) + } + } else { + None + } +} + +pub(crate) fn find_all_references( + referenced_location: noirc_errors::Location, + interner: &NodeInterner, + files: &FileMap, + include_declaration: bool, + include_self_type_name: bool, +) -> Vec { + interner + .find_all_references(referenced_location, include_declaration, include_self_type_name) + .map(|locations| { + locations + .iter() + .filter_map(|location| to_lsp_location(files, location.file, location.span)) + .collect() + }) + .unwrap_or_default() } #[cfg(test)] diff --git a/noir/noir-repo/tooling/lsp/src/requests/references.rs b/noir/noir-repo/tooling/lsp/src/requests/references.rs index f8c23632936..badea8921b2 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/references.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/references.rs @@ -5,32 +5,34 @@ use lsp_types::{Location, ReferenceParams}; use crate::LspState; -use super::{process_request, to_lsp_location}; +use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_references_request( state: &mut LspState, params: ReferenceParams, ) -> impl Future>, ResponseError>> { - let result = - process_request(state, params.text_document_position, |location, interner, files| { - interner.find_all_references(location, params.context.include_declaration, true).map( - |locations| { - locations - .iter() - .filter_map(|location| to_lsp_location(files, location.file, location.span)) - .collect() - }, - ) - }); + let include_declaration = params.context.include_declaration; + let result = process_request(state, params.text_document_position, |args| { + find_all_references_in_workspace( + args.location, + args.interner, + args.interners, + args.files, + include_declaration, + true, + ) + }); future::ready(result) } #[cfg(test)] mod references_tests { use super::*; + use crate::notifications; use crate::test_utils::{self, search_in_file}; use lsp_types::{ - PartialResultParams, ReferenceContext, TextDocumentPositionParams, WorkDoneProgressParams, + PartialResultParams, Position, Range, ReferenceContext, TextDocumentPositionParams, Url, + WorkDoneProgressParams, }; use tokio::test; @@ -91,4 +93,70 @@ mod references_tests { async fn test_on_references_request_without_including_declaration() { check_references_succeeds("rename_function", "another_function", 0, false).await; } + + #[test] + async fn test_on_references_request_works_accross_workspace_packages() { + let (mut state, noir_text_document) = test_utils::init_lsp_server("workspace").await; + + // noir_text_document is always `src/main.nr` in the workspace directory, so let's go to the workspace dir + let noir_text_document = noir_text_document.to_file_path().unwrap(); + let workspace_dir = noir_text_document.parent().unwrap().parent().unwrap(); + + // Let's check that we can find references to `function_one` by doing that in the package "one" + // and getting results in the package "two" too. + let one_lib = Url::from_file_path(workspace_dir.join("one/src/lib.nr")).unwrap(); + let two_lib = Url::from_file_path(workspace_dir.join("two/src/lib.nr")).unwrap(); + + // We call this to open the document, so that the entire workspace is analyzed + notifications::process_workspace_for_noir_document(one_lib.clone(), &mut state).unwrap(); + + let params = ReferenceParams { + text_document_position: TextDocumentPositionParams { + text_document: lsp_types::TextDocumentIdentifier { uri: one_lib.clone() }, + position: Position { line: 0, character: 7 }, + }, + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + partial_result_params: PartialResultParams { partial_result_token: None }, + context: ReferenceContext { include_declaration: true }, + }; + + let mut locations = on_references_request(&mut state, params) + .await + .expect("Could not execute on_references_request") + .unwrap(); + + // The definition, a use in "two", and a call in "two" + assert_eq!(locations.len(), 3); + + locations.sort_by_cached_key(|location| { + (location.uri.to_file_path().unwrap(), location.range.start.line) + }); + + assert_eq!(locations[0].uri, one_lib); + assert_eq!( + locations[0].range, + Range { + start: Position { line: 0, character: 7 }, + end: Position { line: 0, character: 19 }, + } + ); + + assert_eq!(locations[1].uri, two_lib); + assert_eq!( + locations[1].range, + Range { + start: Position { line: 0, character: 9 }, + end: Position { line: 0, character: 21 }, + } + ); + + assert_eq!(locations[2].uri, two_lib); + assert_eq!( + locations[2].range, + Range { + start: Position { line: 3, character: 4 }, + end: Position { line: 3, character: 16 }, + } + ); + } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/rename.rs b/noir/noir-repo/tooling/lsp/src/requests/rename.rs index ac6c6792e15..84956681167 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/rename.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/rename.rs @@ -11,14 +11,14 @@ use noirc_frontend::node_interner::ReferenceId; use crate::LspState; -use super::{process_request, to_lsp_location}; +use super::{find_all_references_in_workspace, process_request}; pub(crate) fn on_prepare_rename_request( state: &mut LspState, params: TextDocumentPositionParams, ) -> impl Future, ResponseError>> { - let result = process_request(state, params, |location, interner, _| { - let reference_id = interner.reference_at_location(location); + let result = process_request(state, params, |args| { + let reference_id = args.interner.reference_at_location(args.location); let rename_possible = match reference_id { // Rename shouldn't be possible when triggered on top of "Self" Some(ReferenceId::Reference(_, true /* is self type name */)) => false, @@ -34,41 +34,36 @@ pub(crate) fn on_rename_request( state: &mut LspState, params: RenameParams, ) -> impl Future, ResponseError>> { - let result = - process_request(state, params.text_document_position, |location, interner, files| { - let rename_changes = - interner.find_all_references(location, true, false).map(|locations| { - let rs = locations.iter().fold( - HashMap::new(), - |mut acc: HashMap>, location| { - let file_id = location.file; - let span = location.span; - - let Some(lsp_location) = to_lsp_location(files, file_id, span) else { - return acc; - }; - - let edit = TextEdit { - range: lsp_location.range, - new_text: params.new_name.clone(), - }; - - acc.entry(lsp_location.uri).or_default().push(edit); - - acc - }, - ); - rs - }); - - let response = WorkspaceEdit { - changes: rename_changes, - document_changes: None, - change_annotations: None, - }; - - Some(response) + let result = process_request(state, params.text_document_position, |args| { + let rename_changes = find_all_references_in_workspace( + args.location, + args.interner, + args.interners, + args.files, + true, + false, + ) + .map(|locations| { + let rs = locations.iter().fold( + HashMap::new(), + |mut acc: HashMap>, location| { + let edit = + TextEdit { range: location.range, new_text: params.new_name.clone() }; + acc.entry(location.uri.clone()).or_default().push(edit); + acc + }, + ); + rs }); + + let response = WorkspaceEdit { + changes: rename_changes, + document_changes: None, + change_annotations: None, + }; + + Some(response) + }); future::ready(result) } @@ -175,6 +170,11 @@ mod rename_tests { check_rename_succeeds("rename_function_use", "some_function").await; } + #[test] + async fn test_rename_method() { + check_rename_succeeds("rename_function", "some_method").await; + } + #[test] async fn test_rename_struct() { check_rename_succeeds("rename_struct", "Foo").await; @@ -199,4 +199,9 @@ mod rename_tests { async fn test_rename_local_variable() { check_rename_succeeds("local_variable", "some_var").await; } + + #[test] + async fn test_rename_struct_member() { + check_rename_succeeds("struct_member", "some_member").await; + } } diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index acd4f5800f3..b4b9b62d6b6 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -59,7 +59,7 @@ fn on_test_run_request_inner( Some(package) => { let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); - if check_crate(&mut context, crate_id, false, false, false).is_err() { + if check_crate(&mut context, crate_id, false, false, false, None).is_err() { let result = NargoTestRunResult { id: params.id.clone(), result: "error".to_string(), diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index a2aa3ebc0bf..fb8b845df04 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -61,7 +61,7 @@ fn on_tests_request_inner( crate::prepare_package(&workspace_file_manager, &parsed_files, package); // We ignore the warnings and errors produced by compilation for producing tests // because we can still get the test functions even if compilation fails - let _ = check_crate(&mut context, crate_id, false, false, false); + let _ = check_crate(&mut context, crate_id, false, false, false, None); // We don't add test headings for a package if it contains no `#[test]` functions get_package_tests_in_crate(&context, &crate_id, &package.name) diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 57eb2dd3618..991773ce94f 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ - DeclarationCapability, DefinitionOptions, OneOf, ReferencesOptions, RenameOptions, - TypeDefinitionProviderCapability, + DeclarationCapability, DefinitionOptions, HoverOptions, OneOf, ReferencesOptions, + RenameOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; use noirc_errors::{debug_info::OpCodesCount, Location}; @@ -144,6 +144,10 @@ pub(crate) struct ServerCapabilities { /// The server provides references support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) references_provider: Option>, + + /// The server provides hover support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) hover_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr index 7a70084276e..e77b50c0b26 100644 --- a/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr +++ b/noir/noir-repo/tooling/lsp/test_programs/rename_function/src/main.nr @@ -25,3 +25,16 @@ use foo::some_other_function as bar; fn x() { bar(); } + +struct SomeStruct { + +} + +impl SomeStruct { + fn some_method(self) {} +} + +fn y() { + let some_struct = SomeStruct {}; + some_struct.some_method(); +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/struct_member/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/struct_member/Nargo.toml new file mode 100644 index 00000000000..5272b9abb68 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/struct_member/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "struct_member" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/tooling/lsp/test_programs/struct_member/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/struct_member/src/main.nr new file mode 100644 index 00000000000..3f1bac9df66 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/struct_member/src/main.nr @@ -0,0 +1,12 @@ +struct Foo { + some_member: Field +} + +fn main() { + let mut foo = Foo { some_member: 1 }; + foo.some_member = 2; + let _ = foo.some_member; + + let Foo { some_member } = foo; + let Foo { some_member: some_var } = foo; +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/workspace/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/workspace/Nargo.toml new file mode 100644 index 00000000000..d0a0badc295 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/workspace/Nargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["one", "two"] diff --git a/noir/noir-repo/tooling/lsp/test_programs/workspace/one/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/workspace/one/Nargo.toml new file mode 100644 index 00000000000..39838d73362 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/workspace/one/Nargo.toml @@ -0,0 +1,4 @@ +[package] +name = "one" +authors = [] +type = "lib" diff --git a/noir/noir-repo/tooling/lsp/test_programs/workspace/one/src/lib.nr b/noir/noir-repo/tooling/lsp/test_programs/workspace/one/src/lib.nr new file mode 100644 index 00000000000..61f282fa2a7 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/workspace/one/src/lib.nr @@ -0,0 +1,25 @@ +pub fn function_one() {} + +mod subone { + struct SubOneStruct { + some_field: i32, + some_other_field: Field, + } + + impl SubOneStruct { + fn foo(self, x: i32, y: i32) -> Field { + 0 + } + } + + trait SomeTrait { + } + + global some_global = 2; + + type SomeAlias = i32; + + struct GenericStruct { + + } +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/workspace/two/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/workspace/two/Nargo.toml new file mode 100644 index 00000000000..26d99b65df1 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/workspace/two/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "two" +authors = [] +type = "lib" + +[dependencies] +one = { path = "../one" } \ No newline at end of file diff --git a/noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/lib.nr b/noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/lib.nr new file mode 100644 index 00000000000..3f0f0f117b7 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/lib.nr @@ -0,0 +1,53 @@ +use one::function_one; + +pub fn function_two() { + function_one() +} + +use one::subone; + +fn use_struct() { + let _ = subone::SubOneStruct { some_field: 0, some_other_field: 2 }; +} + +use one::subone::SomeTrait; + +fn use_global() { + let _ = one::subone::some_global; +} + +fn use_struct_method() { + let s = subone::SubOneStruct { some_field: 0, some_other_field: 2 }; + s.foo(0, 1); +} + +fn use_local_var() { + let regular_var = 0; + let _ = regular_var; + let mut mutable_var = 0; + mutable_var = 1; +} + +fn use_parameter(some_param: i32) { + let _ = some_param; +} + +use one::subone::SomeAlias; + +use std::default::Default; + +fn use_impl_method() { + let _: i32 = Default::default(); +} + +mod other; +use other::another_function; +use crate::other::other_function; + +use one::subone::GenericStruct; + +use std::collections::bounded_vec::BoundedVec; + +fn instantiate_generic() { + let x: BoundedVec = BoundedVec::new(); +} diff --git a/noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/other.nr b/noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/other.nr new file mode 100644 index 00000000000..4d2fffcee80 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/workspace/two/src/other.nr @@ -0,0 +1,2 @@ +fn other_function() {} +fn another_function() {} diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index 3789595aa26..a8c02a0ecd1 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -61,7 +61,7 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ /// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_NEW_FEATURE_TESTS: [&str; 8] = [ +const IGNORED_NEW_FEATURE_TESTS: [&str; 13] = [ "macros", "wildcard_type", "type_definition_annotation", @@ -69,7 +69,12 @@ const IGNORED_NEW_FEATURE_TESTS: [&str; 8] = [ "derive_impl", "comptime_traits", "comptime_slice_methods", + "unary_operator_overloading", "unquote_multiple_items_from_annotation", + "function_attribute", + "trait_attribute", + "regression_5428", + "unquote", ]; fn read_test_cases( diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs index 2db3b10429a..95726492418 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/check_cmd.rs @@ -88,6 +88,7 @@ fn check_package( compile_options.disable_macros, compile_options.silence_warnings, compile_options.use_legacy, + compile_options.debug_comptime_in_file.as_deref(), )?; if package.is_library() || package.is_contract() { @@ -161,8 +162,16 @@ pub(crate) fn check_crate_and_report_errors( disable_macros: bool, silence_warnings: bool, use_legacy: bool, + debug_comptime_in_file: Option<&str>, ) -> Result<(), CompileError> { - let result = check_crate(context, crate_id, deny_warnings, disable_macros, use_legacy); + let result = check_crate( + context, + crate_id, + deny_warnings, + disable_macros, + use_legacy, + debug_comptime_in_file, + ); report_errors(result, &context.file_manager, deny_warnings, silence_warnings) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs index 778009bf791..2014a3acaa4 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use acvm::acir::native_types::{WitnessMap, WitnessStack}; +use acvm::acir::native_types::WitnessStack; use acvm::FieldElement; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -15,7 +15,6 @@ use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; use noirc_abi::InputMap; -use noirc_artifacts::debug::DebugArtifact; use noirc_driver::{ file_manager_with_stdlib, CompileOptions, CompiledProgram, NOIR_ARTIFACT_VERSION_STRING, }; @@ -169,10 +168,10 @@ fn run_async( runtime.block_on(async { println!("[{}] Starting debugger", package.name); - let (return_value, solved_witness) = + let (return_value, witness_stack) = debug_program_and_decode(program, package, prover_name)?; - if let Some(solved_witness) = solved_witness { + if let Some(solved_witness_stack) = witness_stack { println!("[{}] Circuit witness successfully solved", package.name); if let Some(return_value) = return_value { @@ -180,11 +179,8 @@ fn run_async( } if let Some(witness_name) = witness_name { - let witness_path = save_witness_to_dir( - WitnessStack::from(solved_witness), - witness_name, - target_dir, - )?; + let witness_path = + save_witness_to_dir(solved_witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } @@ -200,38 +196,32 @@ fn debug_program_and_decode( program: CompiledProgram, package: &Package, prover_name: &str, -) -> Result<(Option, Option>), CliError> { +) -> Result<(Option, Option>), 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 = debug_program(&program, &inputs_map)?; - - match solved_witness { - Some(witness) => { - let (_, return_value) = program.abi.decode(&witness)?; - Ok((return_value, Some(witness))) + let program_abi = program.abi.clone(); + let witness_stack = debug_program(program, &inputs_map)?; + + match witness_stack { + Some(witness_stack) => { + let main_witness = &witness_stack + .peek() + .expect("Should have at least one witness on the stack") + .witness; + let (_, return_value) = program_abi.decode(main_witness)?; + Ok((return_value, Some(witness_stack))) } None => Ok((None, None)), } } pub(crate) fn debug_program( - compiled_program: &CompiledProgram, + compiled_program: CompiledProgram, inputs_map: &InputMap, -) -> Result>, CliError> { +) -> Result>, CliError> { let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - let debug_artifact = DebugArtifact { - debug_symbols: compiled_program.debug.clone(), - file_map: compiled_program.file_map.clone(), - }; - - noir_debugger::debug_circuit( - &Bn254BlackBoxSolver, - &compiled_program.program.functions[0], - debug_artifact, - initial_witness, - &compiled_program.program.unconstrained_functions, - ) - .map_err(CliError::from) + noir_debugger::run_repl_session(&Bn254BlackBoxSolver, compiled_program, initial_witness) + .map_err(CliError::from) } diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs index ee30b29b0f0..105190c653f 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/export_cmd.rs @@ -90,6 +90,7 @@ fn compile_exported_functions( compile_options.disable_macros, compile_options.silence_warnings, compile_options.use_legacy, + compile_options.debug_comptime_in_file.as_deref(), )?; let exported_functions = context.get_all_exported_functions_in_crate(&crate_id); diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs index de9e8dc5d7c..27c66c956d9 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/test_cmd.rs @@ -177,6 +177,7 @@ fn run_test + Default>( compile_options.deny_warnings, compile_options.disable_macros, compile_options.use_legacy, + compile_options.debug_comptime_in_file.as_deref(), ) .expect("Any errors should have occurred when collecting test functions"); @@ -244,6 +245,7 @@ fn get_tests_in_package( compile_options.disable_macros, compile_options.silence_warnings, compile_options.use_legacy, + compile_options.debug_comptime_in_file.as_deref(), )?; Ok(context diff --git a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs index bf6614860e2..10711b6d011 100644 --- a/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs +++ b/noir/noir-repo/tooling/nargo_cli/tests/stdlib-tests.rs @@ -32,7 +32,7 @@ fn run_stdlib_tests() { let (mut context, dummy_crate_id) = prepare_package(&file_manager, &parsed_files, &dummy_package); - let result = check_crate(&mut context, dummy_crate_id, false, false, false); + let result = check_crate(&mut context, dummy_crate_id, false, false, false, None); report_errors(result, &context.file_manager, true, false) .expect("Error encountered while compiling standard library"); diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 3ce97f89ec2..12793d70545 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -41,7 +41,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "portal:../../../../barretenberg/ts", + "@aztec/bb.js": "0.46.1", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index c07d2d8a4c1..16fb26e55db 100755 --- a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh +++ b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index 181b6b3b206..dc033a7344f 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,18 +221,19 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" +"@aztec/bb.js@npm:0.46.1": + version: 0.46.1 + resolution: "@aztec/bb.js@npm:0.46.1" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: ./dest/node/main.js + bb.js: dest/node/main.js + checksum: 9475388f994e430ab3282a2c9769cd116f334358049955ed520f467d1abec8237bfeae7fa2fed9cd292f24c09c466e32e2af2d0a5cec2d10cc0c727728d96b0d languageName: node - linkType: soft + linkType: hard "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4395,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.46.1 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 53ddb79b900ef0dee41dc14abcf7546837a8cdfb Mon Sep 17 00:00:00 2001 From: AztecBot Date: Mon, 15 Jul 2024 08:02:59 +0000 Subject: [PATCH 2/2] chore: apply sync fixes --- noir/bb-version | 2 +- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- .../verify_honk_proof/Nargo.toml | 6 ++++++ .../verify_honk_proof/Prover.toml | 4 ++++ .../verify_honk_proof/src/main.nr | 21 +++++++++++++++++++ .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 ++++++------ 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml create mode 100644 noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr diff --git a/noir/bb-version b/noir/bb-version index 8298bb08b2d..620104d8208 100644 --- a/noir/bb-version +++ b/noir/bb-version @@ -1 +1 @@ -0.43.0 +0.46.1 diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 16fb26e55db..c07d2d8a4c1 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -require_command wasm-opt +#require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml new file mode 100644 index 00000000000..8fce1bf44b6 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "verify_honk_proof" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml new file mode 100644 index 00000000000..921b69e100a --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml @@ -0,0 +1,4 @@ +key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" +proof = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000005a9bae947e1e91af9e4033d8d6aa6ed632","0x000000000000000000000000000000000025e485e013446d4ac7981c88ba6ecc","0x000000000000000000000000000000ff1e0496e30ab24a63b32b2d1120b76e62","0x00000000000000000000000000000000001afe0a8a685d7cd85d1010e55d9d7c","0x000000000000000000000000000000b0804efd6573805f991458295f510a2004","0x00000000000000000000000000000000000c81a178016e2fe18605022d5a8b0e","0x000000000000000000000000000000eba51e76eb1cfff60a53a0092a3c3dea47","0x000000000000000000000000000000000022e7466247b533282f5936ac4e6c15","0x00000000000000000000000000000071b1d76edf770edff98f00ff4deec264cd","0x00000000000000000000000000000000001e48128e68794d8861fcbb2986a383","0x000000000000000000000000000000d3a2af4915ae6d86b097adc377fafda2d4","0x000000000000000000000000000000000006359de9ca452dab3a4f1f8d9c9d98","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x0000000000000000000000000000000d9d719a8b9f020ad3642d60fe704e696f","0x00000000000000000000000000000000000ddfdbbdefc4ac1580ed38e12cfa49","0x0000000000000000000000000000008289fe9754ce48cd01b7be96a861b5e157","0x00000000000000000000000000000000000ff3e0896bdea021253b3d360fa678","0x000000000000000000000000000000f968b227a358a305607f3efc933823d288","0x00000000000000000000000000000000000eaf8adb390375a76d95e918b65e08","0x000000000000000000000000000000bb34b4b447aae56f5e24f81c3acd6d547f","0x00000000000000000000000000000000002175d012746260ebcfe339a91a81e1","0x0000000000000000000000000000005b739ed2075f2b046062b8fc6a2d1e9863","0x00000000000000000000000000000000001285cd1030d338c0e1603b4da2c838","0x00000000000000000000000000000027447d6c281eb38b2b937af4a516d60c04","0x000000000000000000000000000000000019bc3d980465fbb4a656a74296fc58","0x000000000000000000000000000000b484788ace8f7df86dd5e325d2e9b12599","0x00000000000000000000000000000000000a2ca0d10eb7b767114ae230b728d3","0x000000000000000000000000000000c6dfc7092f16f95795e437664498b88d53","0x0000000000000000000000000000000000131067b4e4d95a4f6f8cf5c9b5450a","0x0f413f22eec51f2a02800e0cafaeec1d92d744fbbaef213c687b9edabd6985f5","0x21230f4ff26c80ffb5d037a9d1d26c3f955ca34cbeca4f54db6656b932967a0c","0x0521f877fe35535767f99597cc50effbd283dcae6812ee0a7620d796ccbfd642","0x202b01350a9cc5c20ec0f3eaada338c0a3b793811bd539418ffa3cc4302615e2","0x2d1214d9b0d41058ad4a172d9c0aecc5bdabe95e687c3465050c6b5396509be4","0x1113b344a151b0af091cb28d728b752ebb4865da6cd7ee68471b961ca5cf69b9","0x2aa66d0954bb83e17bd5c9928d3aa7a7df75d741d409f7c15ba596804ba643fb","0x2e26bc7a530771ef7a95d5360d537e41cf94d8a0942764ff09881c107f91a106","0x0f14f32b921bb63ad1df00adab7c82af58ea8aa7f353f14b281208d8c5fab504","0x13429515c0c53b6502bbcdf545defb3cb69a986c9263e070fcbb397391aae1a3","0x1f21cac5e2f262afc1006a21454cc6bcb018c44e53ad8ab61cebbac99e539176","0x2a9886a6ddc8a61b097c668cd362fc8acdee8dde74f7b1af192c3e060bb2948f","0x2d718181e408ead2e9bcd30a84ad1fccbaf8d48ab6d1820bad4933d284b503c4","0x2634c1aafc902f14508f34d3d7e9d485f42d1a4c95b5a1ef73711ed0d3c68d77","0x092ede9777e6472ce5ffd8c963d466006189e960e2c591d338dc8d4af1a057fb","0x1cba45b17fd24f1cb1b4ab7b83eee741f6c77ba70a497dc4de259eceb7d5ea26","0x246e887c7bf2e17f919b2393b6e9b00b33e8822d862544a775aac05cb7bff710","0x04c3f539fe8689971948afcb437f1ecbd444a5bddaca1c8a450348dcd8480047","0x20c6a423ae4fd58e8951aa378d02d77baf90508ceb48856db2319d70938b186e","0x1bcf8786b554b3316d8ebdbc9d006a4e5d4865aad512ffd404b7f83550d3d030","0x09ab038260518f0970564afcd6bf22e2abf6b1fa5e12a327bbf195b6ca5edd78","0x1024e32554746f89c195286ba6ccfc9765e5d14bbe8064bc6fdf22d16ec6b495","0x17706656f8dbd7e47bb257a6428f0cb7278ea02fa9e6ce431d7bcc9133fba9c7","0x25a3e8a33c15ef2a4dd16313a6049bf1d468b4cdc141f238f2d51a1e8e1c22b3","0x1198863f08006edb27aee23164fb117a4ddec1bf1ed89807aa907e5cd24bf068","0x1862b4856b5b4d4a064f873e221703e4e2cd1ebfca1337dedca56485c38ed5a0","0x062214af1ea6dd6bf8895b92d394571c43970b6f967e1c794624d96071b25ad3","0x1e5be9428ddcf1f9b0cbafc28101e792ec5cf73852b0cd0b84fbff71b4490e09","0x2d4189bea5b1e30f63c64bd26df82f18bcaf885ec8887b54634b2557869ce87f","0x0f2e5d9a908850e9d44925e17d8b12d1adb1ed029799c9b5858598504242bbc0","0x3050dc85746a57931d99f3f35e77c2ba561fba0baa018b79ff1fd544026833ae","0x2a591a32437e5e0b875a137fd868bd1b6dbc003ff1b661f26e00627cc7c5cf47","0x27946841e1670ad9c65717016d0cedf524724217236e81b9fd0a264a36ebfb0e","0x0fc396e9d19d6e68e289602e292ee345542d0d28bf6de34fa62cc577cbdfb1df","0x08e7433a07a44c0c9c4dd4b273a2685bbd1a91fd5cf2b43409458fab42a23e1b","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x12bd9bfb029c3503a5c6deea87b0a0f11bb9f7ea584af2d48f3e48d7e09247ae","0x2ccc4810748c0a82dfc0f063d0b8c7999ffe9474653080e6ef92b3cb7a428784","0x08eb574d7fecadadb508c8bd35fdad06b99110609d679763c2e3645229b1b95a","0x0f1a65e747c8021ed7c454a4be1e89b1bce66ead9ed980fa98a7a050eafe98a1","0x1c8ff9e36684ec71614dee4c17859b06c742089f6029d3694a16e00dac9b57f1","0x0303101a8ba712aeca4da85b767ab8d3ecf489ec7d746f8ee20041717cc000e9","0x0aaf64c65e7088e5596108c9601467911fea809ca6540d79af77e6e66e36cd99","0x17caf164ce74ea7edfb1390e07763d2197797ec26661b92cde18a98d61d2fddc","0x18cb055c7ad6d01437725bb457681d81f3ecadc4f35d838a3c13daf25a44456a","0x2d78602b8bbcd32b36a99a6e2d248e7fe044ef1b50813133370412f9ef5299f0","0x2b139276ea86d426a115479e4154f72a6bd83a6253bf13e9670dc6b4664378f0","0x127c7837b384902c39a104036c09546728571c46c8166b1b9b13b3a615ebb781","0x05faa4816f83cf0189a482ad943c94b9ec6474002f2b327f8698763ad0ea0985","0x2f90359cc30ee693fb3aced96523cf7aebd152c22329eee56a398d9a4ac0628e","0x0a71beaf17a59c5a238f04c1f203848d87502c5057a78c13f0cfb0f9876e7714","0x2696c1e6d089556adaeb95c8a5e3065b00a393a38c2d69e9bd6ce8cdc49d87da","0x1f3d165a7dc6564a036e451eb9cb7f1e1cb1e6d29daa75e3f135ea3e58a79ccd","0x1473a660819bdd838d56122b72b32b267211e9f1103239480ec50fa85c9e1035","0x0a8ccaeb22451f391b3fc3467c8e6e900270a7afb7b510e8acf5a4f06f1c0888","0x03b3080afc0658cc87e307758cebc171921f43eca159b9dedf7f72aa8dd926bd","0x2dd7d6663fa0e1755dfafac352c361fcd64c7f4d53627e3646870ac169cc4a07","0x1ec54b883f5f35ccad0e75695af20790d9860104095bab34c9bf01628dd40cb9","0x193dff50f83c241f7a9e087a29ce72ecf3f6d8563593f786dcd04c32bcfd4ced","0x135122c0dae26cda8ca1c09de8225064ad86d10423ab0aaa53b481aa4626e1d6","0x08d5a56cbfab5aeed56d3cdd7fb6b30fc26b0c1a5b63fccd7fa44c53ba6fd35a","0x0d12f126dfa2daad3726d00ca339284cc22e36c6d81bb7a4b95c6f9598b60e7c","0x2e8b24bbdf2fd839d3c7cae1f0eeb96bfcfaeef30b27476f2fafcb17da78cd5e","0x2364acfe0cea39b7f749c5f303b99504977357925f810f684c60f35d16315211","0x06ca062eb70b8c51cfac35345e7b6b51f33a8ec9ebe204fb9b4911200bf508b7","0x266c0aa1ccb97186815bf69084f600d06ddd934e59a38dfe602ee5d6b9487f22","0x1d817537a49c6d0e3b4b65c6665334b91d7593142e60065048be9e55ceb5e7ab","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x05e9b7256a368df053c691952b59e9327a7c12ed322bbd6f72c669b9b9c26d49","0x25b77026673a1e613e50df0e88fb510973739d5f9064bd364079a9f884209632","0x25c9bc7a3f6aae3d43ff68b5614b34b5eaceff37157b37347995d231784ac1fd","0x085f69baef22680ae15f4801ef4361ebe9c7fc24a94b5bc2527dce8fb705439e","0x0d7c6b9ce31bfc32238a205455baf5ffe99cd30eb0f7bb5b504e1d4501e01382","0x1001a8cc4bc1221c814fba0eddcf3c40619b133373640c600de5bed0a0a05b10","0x20f5894be90e52977cb70f4f4cbd5101693db0360848939750db7e91109d54b6","0x22c09cb26db43f0599408b4daed0f4f496c66424e6affa41c14387d8e0af851b","0x24e5f41357798432426a9549d71e8cc681eaebacbe87f6e3bf38e85de5aa2f3d","0x06eb90100c736fbf2b87432d7821ecdc0b365024739bc36363d48b905973f5b9","0x000000000000000000000000000000ece6d09ed58e9f5661c01140b10558a8c2","0x000000000000000000000000000000000012b6e4f37adcb34b8e88ff8b6eebce","0x000000000000000000000000000000b226a2bb93593fa1fab19a44767828a3f5","0x00000000000000000000000000000000002b5b518342030543092e1428a7e33c","0x00000000000000000000000000000022ba33857034a0574c216eb3c1ddff3025","0x00000000000000000000000000000000001918e58df857985a7cf9eae7802165","0x00000000000000000000000000000045c2d840b96fb6106cc14dcad89dd5f675","0x00000000000000000000000000000000000afdfac1e3a1febdd0208867d44f98","0x00000000000000000000000000000042ebed6c5ec45d794f119aef24c192af0f","0x00000000000000000000000000000000002d05ef250900bbcc5751bbeb210d6a","0x00000000000000000000000000000060d604bdda48eecc90ed065bd9770e1323","0x00000000000000000000000000000000001fed91c63d0041660c1cbc84c2ffbb","0x00000000000000000000000000000054196b549cde36092e8184c7f4f7d878de","0x00000000000000000000000000000000000153f26a01294329922b492485cc31","0x00000000000000000000000000000056ebea579d10dbb440f0222931df2c0059","0x00000000000000000000000000000000000d2cbc61ce5b7cdd7fce398da4637b","0x000000000000000000000000000000e2b9512360b9797d96675d8a2fd2f7aa5d","0x000000000000000000000000000000000025742905f105ff895f74e7c3daa34a","0x000000000000000000000000000000a2dd7df55db59bd41b83518d4403fbc382","0x00000000000000000000000000000000002c1d9c3cbb9371d4cc4e9f900b9a46","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000bcf12ae40c9425c3e67654b84181f90502","0x00000000000000000000000000000000000b6d3faa8a71ff6ef1aa887b7307cf","0x0000000000000000000000000000001f6f719acc23b8f84808c0275d61cfb456","0x0000000000000000000000000000000000296030933ed0c134457ae71c393dfe","0x000000000000000000000000000000ebe1a57cdd7d3d763289b40ef5ed9a7ae0","0x000000000000000000000000000000000010f30483e7df51fca2316d3367603c","0x0000000000000000000000000000000149b7b283ab18060618c8e051864c03cd","0x00000000000000000000000000000000001ef7763235a3a25e241a5f06704dc3"] +public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] +verification_key = ["0x0000000000000000000000000000000000000000000000000000000000000020","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x00000000000000000000000000000060e430ad1c23bfcf3514323aae3f206e84","0x00000000000000000000000000000000001b5c3ff4c2458d8f481b1c068f27ae","0x000000000000000000000000000000bb510ab2112def34980e4fc6998ad9dd16","0x00000000000000000000000000000000000576e7c105b43e061e13cb877fefe1","0x000000000000000000000000000000ced074785d11857b065d8199e6669a601c","0x00000000000000000000000000000000000053b48a4098c1c0ae268f273952f7","0x000000000000000000000000000000d1d4b26e941db8168cee8f6de548ae0fd8","0x00000000000000000000000000000000001a9adf5a6dadc3d948bb61dfd63f4c","0x0000000000000000000000000000009ce1faac6f8de6ebb18f1db17372c82ad5","0x00000000000000000000000000000000002002681bb417184b2df070a16a3858","0x000000000000000000000000000000161baa651a8092e0e84725594de5aba511","0x00000000000000000000000000000000000be0064399c2a1efff9eb0cdcb2223","0x0000000000000000000000000000008673be6fd1bdbe980a29d8c1ded54381e7","0x000000000000000000000000000000000008a5158a7d9648cf1d234524c9fa0c","0x0000000000000000000000000000002b4fce6e4b1c72062b296d49bca2aa4130","0x00000000000000000000000000000000002e45a9eff4b6769e55fb710cded44f","0x00000000000000000000000000000072b85bf733758b76bcf97333efb85a23e3","0x000000000000000000000000000000000017da0ea508994fc82862715e4b5592","0x00000000000000000000000000000094fa74695cf058dba8ff35aec95456c6c3","0x0000000000000000000000000000000000211acddb851061c24b8f159e832bd1","0x000000000000000000000000000000303b5e5c531384b9a792e11702ad3bcab0","0x00000000000000000000000000000000000d336dff51a60b8833d5d7f6d4314c","0x0000000000000000000000000000009f825dde88092070747180d581c342444a","0x0000000000000000000000000000000000237fbd6511a03cca8cac01b555fe01","0x0000000000000000000000000000007c313205159495df6d8de292079a4844ff","0x000000000000000000000000000000000018facdfc468530dd45e8f7a1d38ce9","0x0000000000000000000000000000000d1ce33446fc3dc4ab40ca38d92dac74e1","0x00000000000000000000000000000000000852d8e3e0e8f4435af3e94222688b","0x0000000000000000000000000000006c04ee19ec1dfec87ed47d6d04aa158de2","0x000000000000000000000000000000000013240f97a584b45184c8ec31319b5f","0x000000000000000000000000000000cefb5d240b07ceb4be26ea429b6dc9d9e0","0x00000000000000000000000000000000002dad22022121d689f57fb38ca21349","0x000000000000000000000000000000c9f189f2a91aeb664ce376d8b157ba98f8","0x00000000000000000000000000000000002531a51ad54f124d58094b219818d2","0x000000000000000000000000000000ef1e6db71809307f677677e62b4163f556","0x0000000000000000000000000000000000272da4396fb2a7ee0638b9140e523d","0x0000000000000000000000000000002e54c0244a7732c87bc4712a76dd8c83fb","0x000000000000000000000000000000000007db77b3e04b7eba9643da57cbbe4d","0x000000000000000000000000000000e0dfe1ddd7f74ae0d636c910c3e85830d8","0x00000000000000000000000000000000000466fa9b57ec4664abd1505b490862","0x0000000000000000000000000000009ee55ae8a32fe5384c79907067cc27192e","0x00000000000000000000000000000000000799d0e465cec07ecb5238c854e830","0x0000000000000000000000000000001d5910ad361e76e1c241247a823733c39f","0x00000000000000000000000000000000002b03f2ccf7507564da2e6678bef8fe","0x000000000000000000000000000000231147211b3c75e1f47d150e4bbd2fb22e","0x00000000000000000000000000000000000d19ee104a10d3c701cfd87473cbbe","0x0000000000000000000000000000006705f3f382637d00f698e2c5c94ed05ae9","0x00000000000000000000000000000000000b9c792da28bb60601dd7ce4b74e68","0x000000000000000000000000000000ac5acc8cc21e4ddb225c510670f80c80b3","0x00000000000000000000000000000000002da9d3fa57343e6998aba19429b9fa","0x0000000000000000000000000000004bacbf54b7c17a560df0af18b6d0d527be","0x00000000000000000000000000000000000faea33aeca2025b22c288964b21eb","0x000000000000000000000000000000492e756298d68d6e95de096055cc0336c3","0x00000000000000000000000000000000001a12a12f004859e5a3675c7315121b","0x000000000000000000000000000000893d521d512f30e6d32afbbc0cecd8ee00","0x00000000000000000000000000000000001674b3c1ef12c6da690631e0d86c04","0x000000000000000000000000000000aa6cb02a52e7a613873d4ac9b411349945","0x00000000000000000000000000000000001ecb1fe9c493add46751f9940f73e1","0x00000000000000000000000000000045b3d362ca82cba69fb2b9c733a5b8c351","0x000000000000000000000000000000000019a683586af466e331945b732d2f8c","0x000000000000000000000000000000fc79b052dfdfe67c0ecfc06b4267ffd694","0x00000000000000000000000000000000001336a70c396393038d5e9913744ac2","0x0000000000000000000000000000005450d29af1e9438e91cd33ddeb2548226e","0x000000000000000000000000000000000000993a602891cfd0e6f6ecf7404933","0x000000000000000000000000000000498efddab90a32e9b2db729ed6e9b40192","0x00000000000000000000000000000000002425efebe9628c63ca6fc28bdb5901","0x000000000000000000000000000000d8488157f875a21ab5f93f1c2b641f3de9","0x0000000000000000000000000000000000290f95ada3936604dc4b14df7504e3","0x0000000000000000000000000000005d6902187f3ed60dcce06fca211b40329a","0x00000000000000000000000000000000002b5870a6ba0b20aaa0178e5adfbc36","0x000000000000000000000000000000e5c2519171fa0e548fc3c4966ffc1ce570","0x00000000000000000000000000000000001cb8d8f4793b7debbdc429389dbf2d","0x000000000000000000000000000000a3ee22dd60456277b86c32a18982dcb185","0x00000000000000000000000000000000002493c99a3d068b03f8f2b8d28b57ce","0x000000000000000000000000000000f6c3731486320082c20ec71bbdc92196c1","0x00000000000000000000000000000000001ded39c4c8366469843cd63f09ecac","0x000000000000000000000000000000494997477ab161763e46601d95844837ef","0x00000000000000000000000000000000002e0cddbc5712d79b59cb3b41ebbcdd","0x000000000000000000000000000000426db4c64531d350750df62dbbc41a1bd9","0x0000000000000000000000000000000000303126892f664d8d505964d14315ec","0x00000000000000000000000000000076a6b2c6040c0c62bd59acfe3e3e125672","0x000000000000000000000000000000000000874a5ad262eecc6b565e0b085074","0x000000000000000000000000000000ef082fb517183c9c6841c2b8ef2ca1df04","0x0000000000000000000000000000000000127b2a745a1b74968c3edc18982b9b","0x000000000000000000000000000000c9efd4f8c3d56e1eb23d789a8f710d5be6","0x000000000000000000000000000000000015a18748490ff4c2b1871081954e86","0x000000000000000000000000000000a0011ef987dc016ab110eacd554a1d8bbf","0x00000000000000000000000000000000002097c84955059442a95df075833071","0x000000000000000000000000000000d38e9426ad3085b68b00a93c17897c2877","0x00000000000000000000000000000000002aecd48089890ea0798eb952c66824","0x00000000000000000000000000000078d8a9ce405ce559f441f2e71477ff3ddb","0x00000000000000000000000000000000001216bdb2f0d961bb8a7a23331d2150","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000ee40d90bea71fba7a412dd61fcf34e8ceb","0x0000000000000000000000000000000000140b0936c323fd2471155617b6af56","0x0000000000000000000000000000002b90071823185c5ff8e440fd3d73b6fefc","0x00000000000000000000000000000000002b6c10790a5f6631c87d652e059df4"] diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr new file mode 100644 index 00000000000..ecfd18f3837 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/verify_honk_proof/src/main.nr @@ -0,0 +1,21 @@ + +// This circuit aggregates a single Honk proof from `assert_statement_recursive`. +global SIZE_OF_PROOF_IF_LOGN_IS_28 : u32 = 409; +fn main( + verification_key: [Field; 103], + // This is the proof without public inputs attached. + // + // This means: the size of this does not change with the number of public inputs. + proof: [Field; SIZE_OF_PROOF_IF_LOGN_IS_28], + public_inputs: pub [Field; 1], + // This is currently not public. It is fine given that the vk is a part of the circuit definition. + // I believe we want to eventually make it public too though. + key_hash: Field +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 12793d70545..3ce97f89ec2 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -41,7 +41,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.46.1", + "@aztec/bb.js": "portal:../../../../barretenberg/ts", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh index 16fb26e55db..c07d2d8a4c1 100755 --- a/noir/noir-repo/tooling/noirc_abi_wasm/build.sh +++ b/noir/noir-repo/tooling/noirc_abi_wasm/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -require_command wasm-opt +#require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index dc033a7344f..181b6b3b206 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,19 +221,18 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.46.1": - version: 0.46.1 - resolution: "@aztec/bb.js@npm:0.46.1" +"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": + version: 0.0.0-use.local + resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: dest/node/main.js - checksum: 9475388f994e430ab3282a2c9769cd116f334358049955ed520f467d1abec8237bfeae7fa2fed9cd292f24c09c466e32e2af2d0a5cec2d10cc0c727728d96b0d + bb.js: ./dest/node/main.js languageName: node - linkType: hard + linkType: soft "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4396,7 +4395,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.46.1 + "@aztec/bb.js": "portal:../../../../barretenberg/ts" "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3