From d3367e793d4184ed1f642098fffe90efb320256e Mon Sep 17 00:00:00 2001 From: AztecBot Date: Fri, 2 Aug 2024 08:02:17 +0000 Subject: [PATCH] [1 changes] feat: let LSP work will with code generated by macros (https://github.com/noir-lang/noir/pull/5665) chore(docs): Update web app page to use nargo v 0.31 (https://github.com/noir-lang/noir/pull/5652) feat: add `std::meta::type_of` and `impl Eq for Type` (https://github.com/noir-lang/noir/pull/5669) fix: speed up LSP (https://github.com/noir-lang/noir/pull/5650) feat: don't eagerly error on cast expressions (https://github.com/noir-lang/noir/pull/5635) fix: error on unbound generics in structs (https://github.com/noir-lang/noir/pull/5619) fix: allow using Self for function calls (https://github.com/noir-lang/noir/pull/5629) feat: let filenames in errors be relative to the current dir if possible (https://github.com/noir-lang/noir/pull/5642) feat: LSP inlay type hints on lambda parameters (https://github.com/noir-lang/noir/pull/5639) feat: Sync from aztec-packages (https://github.com/noir-lang/noir/pull/5598) fix!: parse block and if statements independently of expressions in statements (https://github.com/noir-lang/noir/pull/5634) chore: test blackbox binary op instructions (https://github.com/noir-lang/noir/pull/5484) chore(docs): add Writing Noir doc (https://github.com/noir-lang/noir/pull/5456) chore(github): Switch to organization-wide Issue templates (https://github.com/noir-lang/noir/pull/5622) chore: Switch `Value::TraitConstraint` to a resolved trait constraint (https://github.com/noir-lang/noir/pull/5618) fix: error on incorrect generic count for impl and type alias (https://github.com/noir-lang/noir/pull/5623) feat: allow inserting LSP inlay type hints (https://github.com/noir-lang/noir/pull/5620) feat: turbofish in struct pattern (https://github.com/noir-lang/noir/pull/5616) fix: correct span for prefix operator (https://github.com/noir-lang/noir/pull/5624) fix: `NoMatchingImplFound` in comptime code only (https://github.com/noir-lang/noir/pull/5617) feat: Remove 'comptime or separate crate' restriction on comptime code (https://github.com/noir-lang/noir/pull/5609) chore(docs): Update proving backend related docs (https://github.com/noir-lang/noir/pull/5601) feat: turbofish operator in struct constructor (https://github.com/noir-lang/noir/pull/5607) feat: add parameter to call_data attribute (https://github.com/noir-lang/noir/pull/5599) feat: turbofish operator on path segments (https://github.com/noir-lang/noir/pull/5603) fix: Filter comptime globals (https://github.com/noir-lang/noir/pull/5538) chore: Display comptime assertion errors, not Debug (https://github.com/noir-lang/noir/pull/5605) chore: add array and slice control flow tests (https://github.com/noir-lang/noir/pull/5558) fix: let trait calls work in globals (https://github.com/noir-lang/noir/pull/5602) feat: Implement format strings in the comptime interpreter (https://github.com/noir-lang/noir/pull/5596) feat: Implement `Value::Type` in comptime interpreter (https://github.com/noir-lang/noir/pull/5593) chore(docs): nasty linky (https://github.com/noir-lang/noir/pull/5600) feat(acir_gen): Width aware ACIR gen addition (https://github.com/noir-lang/noir/pull/5493) fix: lookup trait constraints methods in composite types (https://github.com/noir-lang/noir/pull/5595) fix: allow trailing comma when parsing where clauses (https://github.com/noir-lang/noir/pull/5594) fix: let std::unsafe::zeroed() work for slices (https://github.com/noir-lang/noir/pull/5592) fix: error on duplicate struct field (https://github.com/noir-lang/noir/pull/5585) --- .noir-sync-commit | 2 +- .../.github/ISSUE_TEMPLATE/bug_report.yml | 120 --- .../ISSUE_TEMPLATE/feature_request.yml | 71 -- noir/noir-repo/Cargo.lock | 1 + .../acvm-repo/acir/src/circuit/mod.rs | 3 + .../acir/src/native_types/expression/mod.rs | 54 ++ noir/noir-repo/acvm-repo/acvm/Cargo.toml | 1 + .../acvm/src/compiler/transformers/csat.rs | 65 +- .../acvm/tests/solver.proptest-regressions | 13 + noir/noir-repo/acvm-repo/acvm/tests/solver.rs | 190 +++- noir/noir-repo/acvm-repo/acvm_js/build.sh | 2 +- noir/noir-repo/aztec_macros/src/lib.rs | 22 +- ...te_note_hash_and_optionally_a_nullifier.rs | 14 +- .../src/transforms/contract_interface.rs | 5 +- .../aztec_macros/src/transforms/events.rs | 60 +- .../aztec_macros/src/transforms/functions.rs | 13 +- .../src/transforms/note_interface.rs | 90 +- .../aztec_macros/src/transforms/storage.rs | 18 +- .../aztec_macros/src/utils/ast_utils.rs | 18 +- noir/noir-repo/aztec_macros/src/utils/mod.rs | 1 + .../aztec_macros/src/utils/parse_utils.rs | 533 ++++++++++++ noir/noir-repo/compiler/fm/src/file_map.rs | 18 +- .../compiler/noirc_driver/src/abi_gen.rs | 2 +- .../compiler/noirc_driver/src/lib.rs | 17 + .../compiler/noirc_errors/src/position.rs | 4 + .../compiler/noirc_evaluator/src/ssa.rs | 33 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 83 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 102 ++- .../src/ssa/function_builder/data_bus.rs | 100 ++- .../noirc_evaluator/src/ssa/opt/die.rs | 4 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 2 +- .../noirc_frontend/src/ast/expression.rs | 41 +- .../compiler/noirc_frontend/src/ast/mod.rs | 9 +- .../noirc_frontend/src/ast/statement.rs | 109 ++- .../noirc_frontend/src/ast/structure.rs | 1 - .../compiler/noirc_frontend/src/ast/traits.rs | 3 - .../compiler/noirc_frontend/src/debug/mod.rs | 76 +- .../noirc_frontend/src/elaborator/comptime.rs | 67 +- .../src/elaborator/expressions.rs | 31 +- .../noirc_frontend/src/elaborator/mod.rs | 226 +++-- .../noirc_frontend/src/elaborator/patterns.rs | 78 +- .../noirc_frontend/src/elaborator/scope.rs | 41 +- .../noirc_frontend/src/elaborator/traits.rs | 29 +- .../noirc_frontend/src/elaborator/types.rs | 157 ++-- .../noirc_frontend/src/elaborator/unquote.rs | 2 +- .../noirc_frontend/src/hir/comptime/errors.rs | 21 +- .../src/hir/comptime/hir_to_display_ast.rs | 20 +- .../src/hir/comptime/interpreter.rs | 184 +++- .../src/hir/comptime/interpreter/builtin.rs | 214 +++-- .../src/hir/comptime/interpreter/unquote.rs | 19 +- .../noirc_frontend/src/hir/comptime/value.rs | 37 +- .../src/hir/def_collector/dc_crate.rs | 22 +- .../src/hir/def_collector/dc_mod.rs | 29 +- .../src/hir/def_collector/errors.rs | 19 + .../noirc_frontend/src/hir/def_map/mod.rs | 4 +- .../compiler/noirc_frontend/src/hir/mod.rs | 6 +- .../src/hir/resolution/import.rs | 18 +- .../src/hir/type_check/errors.rs | 6 + .../noirc_frontend/src/hir_def/function.rs | 8 + .../compiler/noirc_frontend/src/locations.rs | 4 +- .../src/monomorphization/mod.rs | 117 ++- .../noirc_frontend/src/node_interner.rs | 10 +- .../noirc_frontend/src/noir_parser.lalrpop | 18 +- .../noirc_frontend/src/parser/errors.rs | 2 + .../compiler/noirc_frontend/src/parser/mod.rs | 2 +- .../noirc_frontend/src/parser/parser.rs | 175 ++-- .../noirc_frontend/src/parser/parser.rs:28:9 | 45 + .../noirc_frontend/src/parser/parser/path.rs | 42 +- .../src/parser/parser/primitives.rs | 27 +- .../src/parser/parser/structs.rs | 13 +- .../src/parser/parser/traits.rs | 27 +- .../noirc_frontend/src/parser/parser/types.rs | 26 +- .../compiler/noirc_frontend/src/tests.rs | 352 ++++++++ noir/noir-repo/cspell.json | 2 + .../docs/docs/explainers/cspell.json | 5 + .../docs/explainers/explainer-writing-noir.md | 173 ++++ .../getting_started/backend}/_category_.json | 2 +- .../docs/getting_started/backend/index.md | 31 + .../getting_started/barretenberg/index.md | 47 - .../docs/getting_started/hello_noir/index.md | 59 +- .../hello_noir/project_breakdown.md | 3 +- .../getting_started/installation/index.md | 6 +- .../tooling => reference}/noir_codegen.md | 2 +- .../docs/docs/tutorials/noirjs_app.md | 109 ++- noir/noir-repo/docs/docusaurus.config.ts | 1 - .../docs/src/components/Notes/_blackbox.mdx | 2 +- .../getting_started/01_hello_world.md | 2 +- .../data_types/02_booleans.md | 4 +- .../language_concepts/data_types/04_arrays.md | 2 +- .../modules_packages_crates/dependencies.md | 2 +- .../version-v0.19.4/nargo/01_commands.md | 2 +- .../standard_library/black_box_fns.md | 18 +- .../getting_started/backend}/_category_.json | 2 +- .../getting_started/backend/index.md | 31 + .../getting_started/barretenberg/index.md | 47 - .../getting_started/hello_noir/index.md | 59 +- .../hello_noir/project_breakdown.md | 4 +- .../getting_started/installation/index.md | 6 +- .../version-v0.31.0/tutorials/noirjs_app.md | 109 ++- .../getting_started/backend}/_category_.json | 2 +- .../getting_started/backend/index.md | 31 + .../getting_started/barretenberg/index.md | 47 - .../getting_started/hello_noir/index.md | 59 +- .../hello_noir/project_breakdown.md | 3 +- .../getting_started/installation/index.md | 6 +- .../version-v0.32.0/tutorials/noirjs_app.md | 109 ++- noir/noir-repo/noir_stdlib/src/meta/mod.nr | 5 + noir/noir-repo/noir_stdlib/src/meta/quoted.nr | 3 + noir/noir-repo/noir_stdlib/src/meta/typ.nr | 10 + .../noir_stdlib/src/meta/type_def.nr | 4 +- noir/noir-repo/noir_stdlib/src/uint128.nr | 4 +- noir/noir-repo/scripts/install_bb.sh | 2 +- .../non_comptime_local_fn_call/Nargo.toml | 7 - .../non_comptime_local_fn_call/src/main.nr | 9 - .../Nargo.toml | 7 + .../src/main.nr | 6 + .../Nargo.toml | 7 + .../src/main.nr | 12 + .../comptime_fmt_strings/Nargo.toml | 7 + .../comptime_fmt_strings/src/main.nr | 15 + .../comptime_trait_constraint/src/main.nr | 8 +- .../comptime_traits/src/main.nr | 4 +- .../comptime_type/Nargo.toml | 7 + .../comptime_type/src/main.nr | 16 + .../derive_impl/src/main.nr | 2 +- .../quoted_as_type/Nargo.toml | 7 + .../quoted_as_type/src/main.nr | 21 + .../trait_call_in_global}/Nargo.toml | 2 +- .../trait_call_in_global/src/main.nr | 5 + .../zeroed_slice/Nargo.toml | 7 + .../zeroed_slice/src/main.nr | 3 + .../execution_success/databus/src/main.nr | 2 +- .../execution_success/derive/Nargo.toml | 7 + .../execution_success/derive/src/main.nr | 51 ++ .../regression_5615/Nargo.toml | 7 + .../regression_5615/src/main.nr | 12 + .../execution_success/slice_regex/Nargo.toml | 7 + .../execution_success/slice_regex/src/main.nr | 811 ++++++++++++++++++ .../verify_honk_proof/Prover.toml | 4 - .../verify_honk_proof/src/main.nr | 21 - noir/noir-repo/tooling/lsp/src/lib.rs | 44 +- .../tooling/lsp/src/notifications/mod.rs | 50 +- .../lsp/src/requests/code_lens_request.rs | 3 +- .../lsp/src/requests/document_symbol.rs | 2 +- .../tooling/lsp/src/requests/hover.rs | 36 +- .../tooling/lsp/src/requests/inlay_hint.rs | 128 ++- .../noir-repo/tooling/lsp/src/requests/mod.rs | 11 +- .../tooling/lsp/src/requests/references.rs | 6 +- .../lsp/test_programs/inlay_hints/src/main.nr | 12 + .../tooling/nargo_cli/src/cli/compile_cmd.rs | 8 +- .../tooling/nargo_fmt/src/rewrite/expr.rs | 29 +- noir/noir-repo/tooling/nargo_fmt/src/utils.rs | 7 +- .../tooling/nargo_fmt/src/visitor/item.rs | 5 +- .../tooling/nargo_fmt/tests/expected/expr.nr | 4 +- .../tooling/nargo_fmt/tests/input/expr.nr | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- .../noir-repo/tooling/noirc_abi_wasm/build.sh | 2 +- noir/noir-repo/yarn.lock | 13 +- 158 files changed, 4716 insertions(+), 1735 deletions(-) delete mode 100644 noir/noir-repo/.github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 noir/noir-repo/.github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 noir/noir-repo/acvm-repo/acvm/tests/solver.proptest-regressions create mode 100644 noir/noir-repo/aztec_macros/src/utils/parse_utils.rs create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs:28:9 create mode 100644 noir/noir-repo/docs/docs/explainers/cspell.json create mode 100644 noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md rename noir/noir-repo/docs/{versioned_docs/version-v0.31.0/getting_started/barretenberg => docs/getting_started/backend}/_category_.json (63%) create mode 100644 noir/noir-repo/docs/docs/getting_started/backend/index.md delete mode 100644 noir/noir-repo/docs/docs/getting_started/barretenberg/index.md rename noir/noir-repo/docs/docs/{getting_started/tooling => reference}/noir_codegen.md (97%) rename noir/noir-repo/docs/versioned_docs/{version-v0.32.0/getting_started/barretenberg => version-v0.31.0/getting_started/backend}/_category_.json (63%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/index.md delete mode 100644 noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md rename noir/noir-repo/docs/{docs/getting_started/barretenberg => versioned_docs/version-v0.32.0/getting_started/backend}/_category_.json (63%) create mode 100644 noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md delete mode 100644 noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/index.md create mode 100644 noir/noir-repo/noir_stdlib/src/meta/typ.nr delete mode 100644 noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/Nargo.toml delete mode 100644 noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_type/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/src/main.nr rename noir/noir-repo/test_programs/{execution_success/verify_honk_proof => compile_success_empty/trait_call_in_global}/Nargo.toml (64%) create mode 100644 noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/src/main.nr create mode 100644 noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/Nargo.toml create mode 100644 noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_success/derive/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/derive/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_success/regression_5615/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/regression_5615/src/main.nr create mode 100644 noir/noir-repo/test_programs/execution_success/slice_regex/Nargo.toml create mode 100644 noir/noir-repo/test_programs/execution_success/slice_regex/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 diff --git a/.noir-sync-commit b/.noir-sync-commit index b2216c735d46..faae595e1e40 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -453ed590ae3ae6ee8a8d3113419fc51b825b2538 +812262413770d2f20cba04eb0e3176320a3b704a diff --git a/noir/noir-repo/.github/ISSUE_TEMPLATE/bug_report.yml b/noir/noir-repo/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 71207793e53f..000000000000 --- a/noir/noir-repo/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Bug Report -description: Report an unexpected behavior. -labels: ["bug"] -body: - - type: markdown - attributes: - value: | - # Description - Thanks for taking the time to create the Issue and welcome to the Noir community! - - type: textarea - id: aim - attributes: - label: Aim - description: Describe what you tried to achieve. - validations: - required: true - - type: textarea - id: expected - attributes: - label: Expected Behavior - description: Describe what you expected to happen. - validations: - required: true - - type: textarea - id: bug - attributes: - label: Bug - description: Describe the bug. Supply error codes / terminal logs if applicable. - validations: - required: true - - type: textarea - id: reproduction - attributes: - label: To Reproduce - description: Describe the steps to reproduce the behavior. - value: | - 1. - 2. - 3. - 4. - - type: dropdown - id: impact - attributes: - label: Project Impact - description: How does this affect a project you or others are working on? - options: - - "Nice-to-have" - - "Blocker" - - type: textarea - id: impact_context - attributes: - label: Impact Context - description: If a nice-to-have / blocker, supplement how does this Issue affect the project. - - type: dropdown - id: workaround - attributes: - label: Workaround - description: Is there a workaround for this Issue? - options: - - "Yes" - - type: textarea - id: workaround_description - attributes: - label: Workaround Description - description: If yes, supplement how could the Issue be worked around. - - type: textarea - id: additional - attributes: - label: Additional Context - description: Supplement further information if applicable. - - type: markdown - attributes: - value: | - # Environment - Specify your version of Noir tooling used. - - type: markdown - attributes: - value: | - ## Nargo (CLI) - - type: dropdown - id: nargo-install - attributes: - label: Installation Method - description: How did you install Nargo? - options: - - Binary (`noirup` default) - - Compiled from source - - type: input - id: nargo-version - attributes: - label: Nargo Version - description: Output of running `nargo --version` - placeholder: "nargo version = 0.23.0 noirc version = 0.23.0+5be9f9d7e2f39ca228df10e5a530474af0331704 (git version hash: 5be9f9d7e2f39ca228df10e5a530474af0331704, is dirty: false)" - - type: markdown - attributes: - value: | - ## NoirJS (JavaScript) - - type: input - id: noirjs-version - attributes: - label: NoirJS Version - description: Version number of `noir_js` in `package.json` - placeholder: "0.23.0" - - type: markdown - attributes: - value: | - # Pull Request - - type: dropdown - id: pr_preference - attributes: - label: Would you like to submit a PR for this Issue? - description: Fellow contributors are happy to provide support where applicable. - options: - - "Maybe" - - "Yes" - - type: textarea - id: pr_support - attributes: - label: Support Needs - description: Support from other contributors you are looking for to create a PR for this Issue. diff --git a/noir/noir-repo/.github/ISSUE_TEMPLATE/feature_request.yml b/noir/noir-repo/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index abbfe392454b..000000000000 --- a/noir/noir-repo/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Feature Request -description: Suggest an idea for this project. -labels: ["enhancement"] -body: - - type: markdown - attributes: - value: | - ## Description - Thanks for taking the time to create the Issue and welcome to the Noir community! - - type: textarea - id: problem - attributes: - label: Problem - description: Describe what you feel lacking. Supply code / step-by-step examples if applicable. - validations: - required: true - - type: textarea - id: solution - attributes: - label: Happy Case - description: Describe how you think it should work. Supply pseudocode / step-by-step examples if applicable. - validations: - required: true - - type: dropdown - id: impact - attributes: - label: Project Impact - description: How does this affect a project you or others are working on? - options: - - "Nice-to-have" - - "Blocker" - - type: textarea - id: impact_context - attributes: - label: Impact Context - description: If a nice-to-have / blocker, supplement how does this Issue affect the project. - - type: dropdown - id: workaround - attributes: - label: Workaround - description: Is there a workaround for this Issue? - options: - - "Yes" - - type: textarea - id: workaround_description - attributes: - label: Workaround Description - description: If yes, supplement how could the Issue be worked around. - - type: textarea - id: additional - attributes: - label: Additional Context - description: Supplement further information if applicable. - - type: markdown - attributes: - value: | - ## Pull Request - - type: dropdown - id: pr-preference - attributes: - label: Would you like to submit a PR for this Issue? - description: Fellow contributors are happy to provide support where applicable. - multiple: false - options: - - "Maybe" - - "Yes" - - type: textarea - id: pr-support - attributes: - label: Support Needs - description: Support from other contributors you are looking for to create a PR for this Issue. diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index f6011b705e54..bd70c8fef2c8 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -48,6 +48,7 @@ dependencies = [ "brillig_vm", "indexmap 1.9.3", "num-bigint", + "proptest", "serde", "thiserror", "tracing", 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 5d749e709b34..00d0933a3aaa 100644 --- a/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/circuit/mod.rs @@ -377,11 +377,13 @@ mod tests { output: Witness(3), }) } + fn range_opcode() -> Opcode { Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input: FunctionInput::witness(Witness(1), 8), }) } + fn keccakf1600_opcode() -> Opcode { let inputs: Box<[FunctionInput; 25]> = Box::new(std::array::from_fn(|i| FunctionInput::witness(Witness(i as u32 + 1), 8))); @@ -389,6 +391,7 @@ mod tests { Opcode::BlackBoxFuncCall(BlackBoxFuncCall::Keccakf1600 { inputs, outputs }) } + fn schnorr_verify_opcode() -> Opcode { let public_key_x = FunctionInput::witness(Witness(1), FieldElement::max_num_bits()); let public_key_y = FunctionInput::witness(Witness(2), FieldElement::max_num_bits()); diff --git a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/mod.rs b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/mod.rs index 1feda5703c85..2bbbc39d0ca9 100644 --- a/noir/noir-repo/acvm-repo/acir/src/native_types/expression/mod.rs +++ b/noir/noir-repo/acvm-repo/acir/src/native_types/expression/mod.rs @@ -273,6 +273,60 @@ impl Expression { Expression { mul_terms, linear_combinations, q_c } } + + /// Determine the width of this expression. + /// The width meaning the number of unique witnesses needed for this expression. + pub fn width(&self) -> usize { + let mut width = 0; + + for mul_term in &self.mul_terms { + // The coefficient should be non-zero, as this method is ran after the compiler removes all zero coefficient terms + assert_ne!(mul_term.0, F::zero()); + + let mut found_x = false; + let mut found_y = false; + + for term in self.linear_combinations.iter() { + let witness = &term.1; + let x = &mul_term.1; + let y = &mul_term.2; + if witness == x { + found_x = true; + }; + if witness == y { + found_y = true; + }; + if found_x & found_y { + break; + } + } + + // If the multiplication is a squaring then we must assign the two witnesses to separate wires and so we + // can never get a zero contribution to the width. + let multiplication_is_squaring = mul_term.1 == mul_term.2; + + let mul_term_width_contribution = if !multiplication_is_squaring && (found_x & found_y) + { + // Both witnesses involved in the multiplication exist elsewhere in the expression. + // They both do not contribute to the width of the expression as this would be double-counting + // due to their appearance in the linear terms. + 0 + } else if found_x || found_y { + // One of the witnesses involved in the multiplication exists elsewhere in the expression. + // The multiplication then only contributes 1 new witness to the width. + 1 + } else { + // Worst case scenario, the multiplication is using completely unique witnesses so has a contribution of 2. + 2 + }; + + width += mul_term_width_contribution; + } + + width += self.linear_combinations.len(); + + width + } } impl From for Expression { diff --git a/noir/noir-repo/acvm-repo/acvm/Cargo.toml b/noir/noir-repo/acvm-repo/acvm/Cargo.toml index 5b6397a1011b..4cda53de241f 100644 --- a/noir/noir-repo/acvm-repo/acvm/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acvm/Cargo.toml @@ -38,3 +38,4 @@ bls12_381 = [ [dev-dependencies] ark-bls12-381 = { version = "^0.4.0", default-features = false, features = ["curve"] } +proptest.workspace = true diff --git a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/csat.rs b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/csat.rs index 19cc18ca7f38..f258e0a88187 100644 --- a/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/csat.rs +++ b/noir/noir-repo/acvm-repo/acvm/src/compiler/transformers/csat.rs @@ -415,71 +415,8 @@ fn fits_in_one_identity(expr: &Expression, width: usize) -> boo if expr.mul_terms.len() > 1 { return false; }; - // A Polynomial with more terms than fan-in cannot fit within a single opcode - if expr.linear_combinations.len() > width { - return false; - } - - // A polynomial with no mul term and a fan-in that fits inside of the width can fit into a single opcode - if expr.mul_terms.is_empty() { - return true; - } - - // A polynomial with width-2 fan-in terms and a single non-zero mul term can fit into one opcode - // Example: Axy + Dz . Notice, that the mul term places a constraint on the first two terms, but not the last term - // XXX: This would change if our arithmetic polynomial equation was changed to Axyz for example, but for now it is not. - if expr.linear_combinations.len() <= (width - 2) { - return true; - } - - // We now know that we have a single mul term. We also know that the mul term must match up with at least one of the other terms - // A polynomial whose mul terms are non zero which do not match up with two terms in the fan-in cannot fit into one opcode - // An example of this is: Axy + Bx + Cy + ... - // Notice how the bivariate monomial xy has two univariate monomials with their respective coefficients - // XXX: note that if x or y is zero, then we could apply a further optimization, but this would be done in another algorithm. - // It would be the same as when we have zero coefficients - Can only work if wire is constrained to be zero publicly - let mul_term = &expr.mul_terms[0]; - - // The coefficient should be non-zero, as this method is ran after the compiler removes all zero coefficient terms - assert_ne!(mul_term.0, F::zero()); - - let mut found_x = false; - let mut found_y = false; - - for term in expr.linear_combinations.iter() { - let witness = &term.1; - let x = &mul_term.1; - let y = &mul_term.2; - if witness == x { - found_x = true; - }; - if witness == y { - found_y = true; - }; - if found_x & found_y { - break; - } - } - - // If the multiplication is a squaring then we must assign the two witnesses to separate wires and so we - // can never get a zero contribution to the width. - let multiplication_is_squaring = mul_term.1 == mul_term.2; - - let mul_term_width_contribution = if !multiplication_is_squaring && (found_x & found_y) { - // Both witnesses involved in the multiplication exist elsewhere in the expression. - // They both do not contribute to the width of the expression as this would be double-counting - // due to their appearance in the linear terms. - 0 - } else if found_x || found_y { - // One of the witnesses involved in the multiplication exists elsewhere in the expression. - // The multiplication then only contributes 1 new witness to the width. - 1 - } else { - // Worst case scenario, the multiplication is using completely unique witnesses so has a contribution of 2. - 2 - }; - mul_term_width_contribution + expr.linear_combinations.len() <= width + expr.width() <= width } #[cfg(test)] diff --git a/noir/noir-repo/acvm-repo/acvm/tests/solver.proptest-regressions b/noir/noir-repo/acvm-repo/acvm/tests/solver.proptest-regressions new file mode 100644 index 000000000000..35627c1fbae2 --- /dev/null +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.proptest-regressions @@ -0,0 +1,13 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc e4dd0e141df173f5dfdfb186bba4154247ec284b71d8f294fa3282da953a0e92 # shrinks to x = 0, y = 1 +cc 419ed6fdf1bf1f2513889c42ec86c665c9d0500ceb075cbbd07f72444dbd78c6 # shrinks to x = 266672725 +cc 0810fc9e126b56cf0a0ddb25e0dc498fa3b2f1980951550403479fc01c209833 # shrinks to modulus = [71, 253, 124, 216, 22, 140, 32, 60, 141, 202, 113, 104, 145, 106, 129, 151, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48], zero_or_ones_constant = false, use_constant = false +cc 735ee9beb1a1dbb82ded6f30e544d7dfde149957e5d45a8c96fc65a690b6b71c # shrinks to (xs, modulus) = ([(0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (49, false)], [71, 253, 124, 216, 22, 140, 32, 60, 141, 202, 113, 104, 145, 106, 129, 151, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48]) +cc ca81bc11114a2a2b34021f44ecc1e10cb018e35021ef4d728e07a6791dad38d6 # shrinks to (xs, modulus) = ([(0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (49, false)], [71, 253, 124, 216, 22, 140, 32, 60, 141, 202, 113, 104, 145, 106, 129, 151, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48]) +cc 6c1d571a0111e6b4c244dc16da122ebab361e77b71db7770d638076ab21a717b # shrinks to (xs, modulus) = ([(0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (49, false)], [71, 253, 124, 216, 22, 140, 32, 60, 141, 202, 113, 104, 145, 106, 129, 151, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48]) +cc ccb7061ab6b85e2554d00bf03d74204977ed7a4109d7e2d5c6b5aaa2179cfaf9 # shrinks to (xs, modulus) = ([(0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (0, false), (49, false)], [71, 253, 124, 216, 22, 140, 32, 60, 141, 202, 113, 104, 145, 106, 129, 151, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48]) diff --git a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs index e55dbb73ae1e..279b0444609b 100644 --- a/noir/noir-repo/acvm-repo/acvm/tests/solver.rs +++ b/noir/noir-repo/acvm-repo/acvm/tests/solver.rs @@ -5,7 +5,7 @@ use acir::{ brillig::{BinaryFieldOp, HeapArray, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray}, circuit::{ brillig::{BrilligBytecode, BrilligInputs, BrilligOutputs}, - opcodes::{BlockId, BlockType, MemOp}, + opcodes::{BlackBoxFuncCall, BlockId, BlockType, FunctionInput, MemOp}, Opcode, OpcodeLocation, }, native_types::{Expression, Witness, WitnessMap}, @@ -16,6 +16,10 @@ use acvm::pwg::{ACVMStatus, ErrorLocation, ForeignCallWaitInfo, OpcodeResolution use acvm_blackbox_solver::StubbedBlackBoxSolver; use brillig_vm::brillig::HeapValueType; +use proptest::arbitrary::any; +use proptest::prelude::*; +use proptest::result::maybe_ok; + // Reenable these test cases once we move the brillig implementation of inversion down into the acvm stdlib. #[test] @@ -722,3 +726,187 @@ fn memory_operations() { assert_eq!(witness_map[&Witness(8)], FieldElement::from(6u128)); } + +// Solve the given BlackBoxFuncCall with witnesses: 1, 2 as x, y, resp. +#[cfg(test)] +fn solve_blackbox_func_call( + blackbox_func_call: impl Fn( + Option, + Option, + ) -> BlackBoxFuncCall, + x: (FieldElement, bool), // if false, use a Witness + y: (FieldElement, bool), // if false, use a Witness +) -> FieldElement { + let (x, x_constant) = x; + let (y, y_constant) = y; + + let initial_witness = WitnessMap::from(BTreeMap::from_iter([(Witness(1), x), (Witness(2), y)])); + + let mut lhs = None; + if x_constant { + lhs = Some(x); + } + + let mut rhs = None; + if y_constant { + rhs = Some(y); + } + + let op = Opcode::BlackBoxFuncCall(blackbox_func_call(lhs, rhs)); + let opcodes = vec![op]; + let unconstrained_functions = vec![]; + let mut acvm = + ACVM::new(&StubbedBlackBoxSolver, &opcodes, initial_witness, &unconstrained_functions, &[]); + let solver_status = acvm.solve(); + assert_eq!(solver_status, ACVMStatus::Solved); + let witness_map = acvm.finalize(); + + witness_map[&Witness(3)] +} + +fn function_input_from_option( + witness: Witness, + opt_constant: Option, +) -> FunctionInput { + opt_constant + .map(|constant| FunctionInput::constant(constant, FieldElement::max_num_bits())) + .unwrap_or(FunctionInput::witness(witness, FieldElement::max_num_bits())) +} + +fn and_op(x: Option, y: Option) -> BlackBoxFuncCall { + let lhs = function_input_from_option(Witness(1), x); + let rhs = function_input_from_option(Witness(2), y); + BlackBoxFuncCall::AND { lhs, rhs, output: Witness(3) } +} + +fn xor_op(x: Option, y: Option) -> BlackBoxFuncCall { + let lhs = function_input_from_option(Witness(1), x); + let rhs = function_input_from_option(Witness(2), y); + BlackBoxFuncCall::XOR { lhs, rhs, output: Witness(3) } +} + +fn prop_assert_commutative( + op: impl Fn(Option, Option) -> BlackBoxFuncCall, + x: (FieldElement, bool), + y: (FieldElement, bool), +) -> (FieldElement, FieldElement) { + (solve_blackbox_func_call(&op, x, y), solve_blackbox_func_call(&op, y, x)) +} + +fn prop_assert_associative( + op: impl Fn(Option, Option) -> BlackBoxFuncCall, + x: (FieldElement, bool), + y: (FieldElement, bool), + z: (FieldElement, bool), + use_constant_xy: bool, + use_constant_yz: bool, +) -> (FieldElement, FieldElement) { + let f_xy = (solve_blackbox_func_call(&op, x, y), use_constant_xy); + let f_f_xy_z = solve_blackbox_func_call(&op, f_xy, z); + + let f_yz = (solve_blackbox_func_call(&op, y, z), use_constant_yz); + let f_x_f_yz = solve_blackbox_func_call(&op, x, f_yz); + + (f_f_xy_z, f_x_f_yz) +} + +fn prop_assert_identity_l( + op: impl Fn(Option, Option) -> BlackBoxFuncCall, + op_identity: (FieldElement, bool), + x: (FieldElement, bool), +) -> (FieldElement, FieldElement) { + (solve_blackbox_func_call(op, op_identity, x), x.0) +} + +fn prop_assert_zero_l( + op: impl Fn(Option, Option) -> BlackBoxFuncCall, + op_zero: (FieldElement, bool), + x: (FieldElement, bool), +) -> (FieldElement, FieldElement) { + (solve_blackbox_func_call(op, op_zero, x), FieldElement::zero()) +} + +prop_compose! { + // Use both `u128` and hex proptest strategies + fn field_element() + (u128_or_hex in maybe_ok(any::(), "[0-9a-f]{64}"), + constant_input: bool) + -> (FieldElement, bool) + { + match u128_or_hex { + Ok(number) => (FieldElement::from(number), constant_input), + Err(hex) => (FieldElement::from_hex(&hex).expect("should accept any 32 byte hex string"), constant_input), + } + } +} + +fn field_element_ones() -> FieldElement { + let exponent: FieldElement = (253_u128).into(); + FieldElement::from(2u128).pow(&exponent) - FieldElement::one() +} + +proptest! { + + #[test] + fn and_commutative(x in field_element(), y in field_element()) { + let (lhs, rhs) = prop_assert_commutative(and_op, x, y); + prop_assert_eq!(lhs, rhs); + } + + #[test] + fn xor_commutative(x in field_element(), y in field_element()) { + let (lhs, rhs) = prop_assert_commutative(xor_op, x, y); + prop_assert_eq!(lhs, rhs); + } + + #[test] + fn and_associative(x in field_element(), y in field_element(), z in field_element(), use_constant_xy: bool, use_constant_yz: bool) { + let (lhs, rhs) = prop_assert_associative(and_op, x, y, z, use_constant_xy, use_constant_yz); + prop_assert_eq!(lhs, rhs); + } + + #[test] + // TODO(https://github.com/noir-lang/noir/issues/5638) + #[should_panic(expected = "assertion failed: `(left == right)`")] + fn xor_associative(x in field_element(), y in field_element(), z in field_element(), use_constant_xy: bool, use_constant_yz: bool) { + let (lhs, rhs) = prop_assert_associative(xor_op, x, y, z, use_constant_xy, use_constant_yz); + prop_assert_eq!(lhs, rhs); + } + + // test that AND(x, x) == x + #[test] + fn and_self_identity(x in field_element()) { + prop_assert_eq!(solve_blackbox_func_call(and_op, x, x), x.0); + } + + // test that XOR(x, x) == 0 + #[test] + fn xor_self_zero(x in field_element()) { + prop_assert_eq!(solve_blackbox_func_call(xor_op, x, x), FieldElement::zero()); + } + + #[test] + fn and_identity_l(x in field_element(), ones_constant: bool) { + let ones = (field_element_ones(), ones_constant); + let (lhs, rhs) = prop_assert_identity_l(and_op, ones, x); + if x <= ones { + prop_assert_eq!(lhs, rhs); + } else { + prop_assert!(lhs != rhs); + } + } + + #[test] + fn xor_identity_l(x in field_element(), zero_constant: bool) { + let zero = (FieldElement::zero(), zero_constant); + let (lhs, rhs) = prop_assert_identity_l(xor_op, zero, x); + prop_assert_eq!(lhs, rhs); + } + + #[test] + fn and_zero_l(x in field_element(), ones_constant: bool) { + let zero = (FieldElement::zero(), ones_constant); + let (lhs, rhs) = prop_assert_zero_l(and_op, zero, x); + prop_assert_eq!(lhs, rhs); + } +} diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index c07d2d8a4c1d..16fb26e55db1 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -25,7 +25,7 @@ function run_if_available { require_command jq require_command cargo require_command wasm-bindgen -#require_command wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") pname=$(cargo read-manifest | jq -r '.name') diff --git a/noir/noir-repo/aztec_macros/src/lib.rs b/noir/noir-repo/aztec_macros/src/lib.rs index 580a132aa5a4..ec1d395725db 100644 --- a/noir/noir-repo/aztec_macros/src/lib.rs +++ b/noir/noir-repo/aztec_macros/src/lib.rs @@ -62,18 +62,25 @@ fn transform( file_id: FileId, context: &HirContext, ) -> Result { + let empty_spans = context.def_interner.is_in_lsp_mode(); + // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&file_id, &mut submodule.contents, submodule.name.0.contents.as_str()) - .map_err(|err| (err.into(), file_id))? + if transform_module( + &file_id, + &mut submodule.contents, + submodule.name.0.contents.as_str(), + empty_spans, + ) + .map_err(|err| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; } } - generate_event_impls(&mut ast).map_err(|err| (err.into(), file_id))?; - generate_note_interface_impl(&mut ast).map_err(|err| (err.into(), file_id))?; + generate_event_impls(&mut ast, empty_spans).map_err(|err| (err.into(), file_id))?; + generate_note_interface_impl(&mut ast, empty_spans).map_err(|err| (err.into(), file_id))?; Ok(ast) } @@ -85,6 +92,7 @@ fn transform_module( file_id: &FileId, module: &mut SortedModule, module_name: &str, + empty_spans: bool, ) -> Result { let mut has_transformed_module = false; @@ -99,7 +107,7 @@ fn transform_module( if !check_for_storage_implementation(module, storage_struct_name) { generate_storage_implementation(module, storage_struct_name)?; } - generate_storage_layout(module, storage_struct_name.clone(), module_name)?; + generate_storage_layout(module, storage_struct_name.clone(), module_name, empty_spans)?; } let has_initializer = module.functions.iter().any(|func| { @@ -144,7 +152,7 @@ fn transform_module( let stub_src = stub_function(fn_type, func, is_static); stubs.push((stub_src, Location { file: *file_id, span: func.name_ident().span() })); - export_fn_abi(&mut module.types, func)?; + export_fn_abi(&mut module.types, func, empty_spans)?; transform_function( fn_type, func, @@ -200,7 +208,7 @@ fn transform_module( }); } - generate_contract_interface(module, module_name, &stubs, storage_defined)?; + generate_contract_interface(module, module_name, &stubs, storage_defined, empty_spans)?; } Ok(has_transformed_module) diff --git a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs index 40fde39a06f9..8983266dab9e 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_optionally_a_nullifier.rs @@ -3,9 +3,10 @@ use noirc_frontend::ast::{FunctionReturnType, NoirFunction, UnresolvedTypeData}; use noirc_frontend::{ graph::CrateId, macros_api::{FileId, HirContext}, - parse_program, Type, + Type, }; +use crate::utils::parse_utils::parse_program; use crate::utils::{ errors::AztecMacroError, hir_utils::{ @@ -125,8 +126,12 @@ pub fn inject_compute_note_hash_and_optionally_a_nullifier( notes_and_lengths.iter().map(|(note_type, _)| note_type.clone()).collect::>(); // We can now generate a version of compute_note_hash_and_optionally_a_nullifier tailored for the contract in this crate. - let func = - generate_compute_note_hash_and_optionally_a_nullifier(¬e_types, max_note_length); + let empty_spans = context.def_interner.is_in_lsp_mode(); + let func = generate_compute_note_hash_and_optionally_a_nullifier( + ¬e_types, + max_note_length, + empty_spans, + ); // And inject the newly created function into the contract. @@ -149,11 +154,12 @@ pub fn inject_compute_note_hash_and_optionally_a_nullifier( fn generate_compute_note_hash_and_optionally_a_nullifier( note_types: &[String], max_note_length: u128, + empty_spans: bool, ) -> NoirFunction { let function_source = generate_compute_note_hash_and_optionally_a_nullifier_source(note_types, max_note_length); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors.clone()); } diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 56107de77c56..dd3ec7f6a757 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -5,13 +5,13 @@ use noirc_frontend::ast::{Ident, NoirFunction, UnresolvedTypeData}; use noirc_frontend::{ graph::CrateId, macros_api::{FieldElement, FileId, HirContext, HirExpression, HirLiteral, HirStatement}, - parse_program, parser::SortedModule, Type, }; use tiny_keccak::{Hasher, Keccak}; +use crate::utils::parse_utils::parse_program; use crate::utils::{ errors::AztecMacroError, hir_utils::{collect_crate_structs, get_contract_module_data, signature_of_type}, @@ -203,6 +203,7 @@ pub fn generate_contract_interface( module_name: &str, stubs: &[(String, Location)], has_storage_layout: bool, + empty_spans: bool, ) -> Result<(), AztecMacroError> { let storage_layout_getter = format!( "#[contract_library_method] @@ -253,7 +254,7 @@ pub fn generate_contract_interface( if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() } ); - let (contract_interface_ast, errors) = parse_program(&contract_interface); + let (contract_interface_ast, errors) = parse_program(&contract_interface, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotGenerateContractInterface { secondary_message: Some("Failed to parse Noir macro code during contract interface generation. This is either a bug in the compiler or the Noir macro code".to_string()), }); diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index ecfca40189dc..8b71bd77ae68 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -4,16 +4,19 @@ use noirc_frontend::token::SecondaryAttribute; use noirc_frontend::{ graph::CrateId, macros_api::{FileId, HirContext}, - parse_program, parser::SortedModule, }; use crate::utils::hir_utils::collect_crate_structs; +use crate::utils::parse_utils::parse_program; use crate::utils::{ast_utils::is_custom_attribute, errors::AztecMacroError}; // Automatic implementation of most of the methods in the EventInterface trait, guiding the user with meaningful error messages in case some // methods must be implemented manually. -pub fn generate_event_impls(module: &mut SortedModule) -> Result<(), AztecMacroError> { +pub fn generate_event_impls( + module: &mut SortedModule, + empty_spans: bool, +) -> Result<(), AztecMacroError> { // Find structs annotated with #[aztec(event)] // Why doesn't this work ? Events are not tagged and do not appear, it seems only going through the submodule works // let annotated_event_structs = module @@ -56,28 +59,39 @@ pub fn generate_event_impls(module: &mut SortedModule) -> Result<(), AztecMacroE )); } - let mut event_interface_trait_impl = - generate_trait_impl_stub_event_interface(event_type.as_str(), event_byte_len)?; + let mut event_interface_trait_impl = generate_trait_impl_stub_event_interface( + event_type.as_str(), + event_byte_len, + empty_spans, + )?; event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_get_event_type_id(event_type.as_str(), event_len)?, + generate_fn_get_event_type_id(event_type.as_str(), event_len, empty_spans)?, )); event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_private_to_be_bytes(event_type.as_str(), event_byte_len)?, + generate_fn_private_to_be_bytes(event_type.as_str(), event_byte_len, empty_spans)?, )); event_interface_trait_impl.items.push(TraitImplItem::Function( - generate_fn_to_be_bytes(event_type.as_str(), event_byte_len)?, + generate_fn_to_be_bytes(event_type.as_str(), event_byte_len, empty_spans)?, )); event_interface_trait_impl .items - .push(TraitImplItem::Function(generate_fn_emit(event_type.as_str())?)); + .push(TraitImplItem::Function(generate_fn_emit(event_type.as_str(), empty_spans)?)); submodule.contents.trait_impls.push(event_interface_trait_impl); - let serialize_trait_impl = - generate_trait_impl_serialize(event_type.as_str(), event_len, &event_fields)?; + let serialize_trait_impl = generate_trait_impl_serialize( + event_type.as_str(), + event_len, + &event_fields, + empty_spans, + )?; submodule.contents.trait_impls.push(serialize_trait_impl); - let deserialize_trait_impl = - generate_trait_impl_deserialize(event_type.as_str(), event_len, &event_fields)?; + let deserialize_trait_impl = generate_trait_impl_deserialize( + event_type.as_str(), + event_len, + &event_fields, + empty_spans, + )?; submodule.contents.trait_impls.push(deserialize_trait_impl); } } @@ -88,6 +102,7 @@ pub fn generate_event_impls(module: &mut SortedModule) -> Result<(), AztecMacroE fn generate_trait_impl_stub_event_interface( event_type: &str, byte_length: u32, + empty_spans: bool, ) -> Result { let byte_length_without_randomness = byte_length - 32; let trait_impl_source = format!( @@ -98,7 +113,7 @@ impl dep::aztec::event::event_interface::EventInterface<{byte_length}, {byte_len ) .to_string(); - let (parsed_ast, errors) = parse_program(&trait_impl_source); + let (parsed_ast, errors) = parse_program(&trait_impl_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { @@ -116,6 +131,7 @@ fn generate_trait_impl_serialize( event_type: &str, event_len: u32, event_fields: &[(String, String)], + empty_spans: bool, ) -> Result { let field_names = event_fields .iter() @@ -143,7 +159,7 @@ fn generate_trait_impl_serialize( ) .to_string(); - let (parsed_ast, errors) = parse_program(&trait_impl_source); + let (parsed_ast, errors) = parse_program(&trait_impl_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { @@ -161,6 +177,7 @@ fn generate_trait_impl_deserialize( event_type: &str, event_len: u32, event_fields: &[(String, String)], + empty_spans: bool, ) -> Result { let field_names: Vec = event_fields .iter() @@ -189,7 +206,7 @@ fn generate_trait_impl_deserialize( ) .to_string(); - let (parsed_ast, errors) = parse_program(&trait_impl_source); + let (parsed_ast, errors) = parse_program(&trait_impl_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { @@ -206,6 +223,7 @@ fn generate_trait_impl_deserialize( fn generate_fn_get_event_type_id( event_type: &str, field_length: u32, + empty_spans: bool, ) -> Result { let from_signature_input = std::iter::repeat("Field").take(field_length as usize).collect::>().join(","); @@ -218,7 +236,7 @@ fn generate_fn_get_event_type_id( ) .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { @@ -235,6 +253,7 @@ fn generate_fn_get_event_type_id( fn generate_fn_private_to_be_bytes( event_type: &str, byte_length: u32, + empty_spans: bool, ) -> Result { let function_source = format!( " @@ -264,7 +283,7 @@ fn generate_fn_private_to_be_bytes( ) .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { @@ -281,6 +300,7 @@ fn generate_fn_private_to_be_bytes( fn generate_fn_to_be_bytes( event_type: &str, byte_length: u32, + empty_spans: bool, ) -> Result { let byte_length_without_randomness = byte_length - 32; let function_source = format!( @@ -308,7 +328,7 @@ fn generate_fn_to_be_bytes( ") .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { @@ -322,7 +342,7 @@ fn generate_fn_to_be_bytes( Ok(noir_fn) } -fn generate_fn_emit(event_type: &str) -> Result { +fn generate_fn_emit(event_type: &str, empty_spans: bool) -> Result { let function_source = format!( " fn emit(self: {event_type}, _emit: fn[Env](Self) -> ()) {{ @@ -332,7 +352,7 @@ fn generate_fn_emit(event_type: &str) -> Result { ) .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementEventInterface { diff --git a/noir/noir-repo/aztec_macros/src/transforms/functions.rs b/noir/noir-repo/aztec_macros/src/transforms/functions.rs index 4d8b6ef7cdfc..cd3fdd1fc62d 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/functions.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/functions.rs @@ -8,16 +8,18 @@ use noirc_frontend::ast::{ UnresolvedTypeData, Visibility, }; -use noirc_frontend::{macros_api::FieldElement, parse_program}; +use noirc_frontend::macros_api::FieldElement; use crate::utils::ast_utils::member_access; +use crate::utils::parse_utils::parse_program; use crate::{ chained_dep, chained_path, utils::{ ast_utils::{ assignment, assignment_with_type, call, cast, expression, ident, ident_path, index_array, make_eq, make_statement, make_type, method_call, mutable_assignment, - mutable_reference, path, return_type, variable, variable_ident, variable_path, + mutable_reference, path, path_segment, return_type, variable, variable_ident, + variable_path, }, errors::AztecMacroError, }, @@ -131,6 +133,7 @@ pub fn transform_function( pub fn export_fn_abi( types: &mut Vec, func: &NoirFunction, + empty_spans: bool, ) -> Result<(), AztecMacroError> { let mut parameters_struct_source: Option<&str> = None; @@ -197,7 +200,7 @@ pub fn export_fn_abi( program.push_str(&export_struct_source); - let (ast, errors) = parse_program(&program); + let (ast, errors) = parse_program(&program, empty_spans); if !errors.is_empty() { return Err(AztecMacroError::CouldNotExportFunctionAbi { span: None, @@ -722,8 +725,8 @@ fn add_struct_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement { fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { // let identifier_as_bytes = identifier.as_bytes(); let var = variable_ident(identifier.clone()); - let contents = if let ExpressionKind::Variable(p, _) = &var.kind { - p.segments.first().cloned().unwrap_or_else(|| panic!("No segments")).0.contents + let contents = if let ExpressionKind::Variable(p) = &var.kind { + p.first_name() } else { panic!("Unexpected identifier type") }; diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index 3233e12ab73a..6fccded45ef8 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -6,7 +6,6 @@ use noirc_frontend::ast::{ use noirc_frontend::{ graph::CrateId, macros_api::{FileId, HirContext, HirExpression, HirLiteral, HirStatement}, - parse_program, parser::SortedModule, Type, }; @@ -16,11 +15,13 @@ use regex::Regex; // TODO(#7165): nuke the following dependency from here and Cargo.toml use tiny_keccak::{Hasher, Keccak}; +use crate::utils::parse_utils::parse_program; use crate::{ chained_dep, utils::{ ast_utils::{ check_trait_method_implemented, ident, ident_path, is_custom_attribute, make_type, + path_segment, }, errors::AztecMacroError, hir_utils::{fetch_notes, get_contract_module_data, inject_global}, @@ -29,7 +30,10 @@ use crate::{ // Automatic implementation of most of the methods in the NoteInterface trait, guiding the user with meaningful error messages in case some // methods must be implemented manually. -pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), AztecMacroError> { +pub fn generate_note_interface_impl( + module: &mut SortedModule, + empty_spans: bool, +) -> Result<(), AztecMacroError> { // Find structs annotated with #[aztec(note)] let annotated_note_structs = module .types @@ -45,8 +49,8 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt .iter_mut() .find(|trait_impl| { if let UnresolvedTypeData::Named(struct_path, _, _) = &trait_impl.object_type.typ { - struct_path.last_segment() == note_struct.name - && trait_impl.trait_name.last_segment().0.contents == "NoteInterface" + struct_path.last_ident() == note_struct.name + && trait_impl.trait_name.last_name() == "NoteInterface" } else { false } @@ -58,10 +62,11 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt note_struct.name.0.contents )), })?; - let note_interface_impl_span: Option = trait_impl.object_type.span; + let note_interface_impl_span: Option = + if empty_spans { None } else { trait_impl.object_type.span }; // Look for the note struct implementation, generate a default one if it doesn't exist (in order to append methods to it) let existing_impl = module.impls.iter_mut().find(|r#impl| match &r#impl.object_type.typ { - UnresolvedTypeData::Named(path, _, _) => path.last_segment().eq(¬e_struct.name), + UnresolvedTypeData::Named(path, _, _) => path.last_ident().eq(¬e_struct.name), _ => false, }); let note_impl = if let Some(note_impl) = existing_impl { @@ -73,7 +78,6 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt generics: vec![], methods: vec![], where_clause: vec![], - is_comptime: false, }; module.impls.push(default_impl.clone()); module.impls.last_mut().unwrap() @@ -85,9 +89,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt .trait_generics .iter() .map(|gen| match gen.typ.clone() { - UnresolvedTypeData::Named(path, _, _) => { - Ok(path.last_segment().0.contents.to_string()) - } + UnresolvedTypeData::Named(path, _, _) => Ok(path.last_name().to_string()), UnresolvedTypeData::Expression(UnresolvedTypeExpression::Constant(val, _)) => { Ok(val.to_string()) } @@ -108,9 +110,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt // Automatically inject the header field if it's not present let (header_field_name, _) = if let Some(existing_header) = note_struct.fields.iter().find(|(_, field_type)| match &field_type.typ { - UnresolvedTypeData::Named(path, _, _) => { - path.last_segment().0.contents == "NoteHeader" - } + UnresolvedTypeData::Named(path, _, _) => path.last_name() == "NoteHeader", _ => false, }) { existing_header.clone() @@ -144,6 +144,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt ¬e_serialized_len, &header_field_name.0.contents, note_interface_impl_span, + empty_spans, )?; trait_impl.items.push(TraitImplItem::Function(note_serialize_content_fn)); @@ -153,6 +154,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt ¬e_serialized_len, &header_field_name.0.contents, note_interface_impl_span, + empty_spans, )?; trait_impl.items.push(TraitImplItem::Function(note_deserialize_content_fn)); @@ -161,6 +163,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt ¬e_fields, &header_field_name.0.contents, note_interface_impl_span, + empty_spans, )?; structs_to_inject.push(note_properties_struct); let note_properties_fn = generate_note_properties_fn( @@ -168,6 +171,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt ¬e_fields, &header_field_name.0.contents, note_interface_impl_span, + empty_spans, )?; note_impl.methods.push((note_properties_fn, note_impl.type_span)); } @@ -177,6 +181,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt ¬e_type, &header_field_name.0.contents, note_interface_impl_span, + empty_spans, )?; trait_impl.items.push(TraitImplItem::Function(get_header_fn)); } @@ -185,6 +190,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt ¬e_type, &header_field_name.0.contents, note_interface_impl_span, + empty_spans, )?; trait_impl.items.push(TraitImplItem::Function(set_header_fn)); } @@ -192,13 +198,16 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt if !check_trait_method_implemented(trait_impl, "get_note_type_id") { let note_type_id = compute_note_type_id(¬e_type); let get_note_type_id_fn = - generate_get_note_type_id(note_type_id, note_interface_impl_span)?; + generate_get_note_type_id(note_type_id, note_interface_impl_span, empty_spans)?; trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); } if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { - let compute_note_content_hash_fn = - generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; + let compute_note_content_hash_fn = generate_compute_note_content_hash( + ¬e_type, + note_interface_impl_span, + empty_spans, + )?; trait_impl.items.push(TraitImplItem::Function(compute_note_content_hash_fn)); } @@ -208,6 +217,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt note_bytes_len.as_str(), note_serialized_len.as_str(), note_interface_impl_span, + empty_spans, )?; trait_impl.items.push(TraitImplItem::Function(to_be_bytes_fn)); } @@ -222,6 +232,7 @@ fn generate_note_to_be_bytes( byte_length: &str, serialized_length: &str, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = format!( " @@ -252,7 +263,7 @@ fn generate_note_to_be_bytes( ) .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -263,7 +274,7 @@ fn generate_note_to_be_bytes( let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -272,6 +283,7 @@ fn generate_note_get_header( note_type: &String, note_header_field_name: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = format!( " @@ -283,7 +295,7 @@ fn generate_note_get_header( ) .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -294,7 +306,7 @@ fn generate_note_get_header( let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -303,6 +315,7 @@ fn generate_note_set_header( note_type: &String, note_header_field_name: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = format!( " @@ -313,7 +326,7 @@ fn generate_note_set_header( note_type, note_header_field_name ); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -324,7 +337,7 @@ fn generate_note_set_header( let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -334,6 +347,7 @@ fn generate_note_set_header( fn generate_get_note_type_id( note_type_id: u32, impl_span: Option, + empty_spans: bool, ) -> Result { // TODO(#7165): replace {} with dep::aztec::protocol_types::abis::note_selector::compute_note_selector(\"{}\") in the function source below let function_source = format!( @@ -346,7 +360,7 @@ fn generate_get_note_type_id( ) .to_string(); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -357,7 +371,7 @@ fn generate_get_note_type_id( let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -376,11 +390,12 @@ fn generate_note_properties_struct( note_fields: &[(String, String)], note_header_field_name: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let struct_source = generate_note_properties_struct_source(note_type, note_fields, note_header_field_name); - let (struct_ast, errors) = parse_program(&struct_source); + let (struct_ast, errors) = parse_program(&struct_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -409,6 +424,7 @@ fn generate_note_deserialize_content( note_serialize_len: &String, note_header_field_name: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = generate_note_deserialize_content_source( note_type, @@ -417,7 +433,7 @@ fn generate_note_deserialize_content( note_header_field_name, ); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -428,7 +444,7 @@ fn generate_note_deserialize_content( let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -446,6 +462,7 @@ fn generate_note_serialize_content( note_serialize_len: &String, note_header_field_name: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = generate_note_serialize_content_source( note_type, @@ -454,7 +471,7 @@ fn generate_note_serialize_content( note_header_field_name, ); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -465,7 +482,7 @@ fn generate_note_serialize_content( let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -476,10 +493,11 @@ fn generate_note_properties_fn( note_fields: &[(String, String)], note_header_field_name: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = generate_note_properties_fn_source(note_type, note_fields, note_header_field_name); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -489,7 +507,7 @@ fn generate_note_properties_fn( } let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -502,6 +520,7 @@ fn generate_note_properties_fn( fn generate_compute_note_content_hash( note_type: &String, impl_span: Option, + empty_spans: bool, ) -> Result { let function_source = format!( " @@ -511,7 +530,7 @@ fn generate_compute_note_content_hash( ", note_type ); - let (function_ast, errors) = parse_program(&function_source); + let (function_ast, errors) = parse_program(&function_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -521,7 +540,7 @@ fn generate_compute_note_content_hash( } let mut function_ast = function_ast.into_sorted(); let mut noir_fn = function_ast.functions.remove(0); - noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.span = impl_span.unwrap_or_default(); noir_fn.def.visibility = ItemVisibility::Public; Ok(noir_fn) } @@ -529,6 +548,7 @@ fn generate_compute_note_content_hash( fn generate_note_exports_global( note_type: &str, note_type_id: &str, + empty_spans: bool, ) -> Result { let struct_source = format!( " @@ -541,7 +561,7 @@ fn generate_note_exports_global( ) .to_string(); - let (global_ast, errors) = parse_program(&struct_source); + let (global_ast, errors) = parse_program(&struct_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { @@ -783,9 +803,11 @@ pub fn inject_note_exports( file_id, )), }?; + let empty_spans = context.def_interner.is_in_lsp_mode(); let global = generate_note_exports_global( ¬e.borrow().name.0.contents, ¬e_type_id.to_hex(), + empty_spans, ) .map_err(|err| (err, file_id))?; diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index 1c6ef6340708..dacea1a95e37 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -10,18 +10,18 @@ use noirc_frontend::{ FieldElement, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, }, node_interner::TraitId, - parse_program, parser::SortedModule, token::SecondaryAttribute, Type, }; +use crate::utils::parse_utils::parse_program; use crate::{ chained_path, utils::{ ast_utils::{ call, expression, ident, ident_path, is_custom_attribute, lambda, make_statement, - make_type, pattern, return_type, variable, variable_path, + make_type, path_segment, pattern, return_type, variable, variable_path, }, errors::AztecMacroError, hir_utils::{ @@ -59,7 +59,7 @@ fn inject_context_in_storage_field(field: &mut UnresolvedType) -> Result<(), Azt vec![], false, ))); - match path.segments.last().unwrap().0.contents.as_str() { + match path.last_name() { "Map" => inject_context_in_storage_field(&mut generics[1]), _ => Ok(()), } @@ -106,9 +106,7 @@ pub fn check_for_storage_implementation( storage_struct_name: &String, ) -> bool { module.impls.iter().any(|r#impl| match &r#impl.object_type.typ { - UnresolvedTypeData::Named(path, _, _) => { - path.segments.last().is_some_and(|segment| segment.0.contents == *storage_struct_name) - } + UnresolvedTypeData::Named(path, _, _) => path.last_name() == *storage_struct_name, _ => false, }) } @@ -123,8 +121,8 @@ pub fn generate_storage_field_constructor( match typ { UnresolvedTypeData::Named(path, generics, _) => { let mut new_path = path.clone().to_owned(); - new_path.segments.push(ident("new")); - match path.segments.last().unwrap().0.contents.as_str() { + new_path.segments.push(path_segment("new")); + match path.last_name() { "Map" => Ok(call( variable_path(new_path), vec![ @@ -248,7 +246,6 @@ pub fn generate_storage_implementation( methods: vec![(init, Span::default())], where_clause: vec![], - is_comptime: false, }; module.impls.push(storage_impl); @@ -501,6 +498,7 @@ pub fn generate_storage_layout( module: &mut SortedModule, storage_struct_name: String, module_name: &str, + empty_spans: bool, ) -> Result<(), AztecMacroError> { let definition = module .types @@ -533,7 +531,7 @@ pub fn generate_storage_layout( storable_fields_impl.join(",\n") ); - let (struct_ast, errors) = parse_program(&storage_fields_source); + let (struct_ast, errors) = parse_program(&storage_fields_source, empty_spans); if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotExportStorageLayout { diff --git a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs index 4467c4bca4b5..a74ec5b777a3 100644 --- a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs @@ -2,8 +2,8 @@ use noirc_errors::{Span, Spanned}; use noirc_frontend::ast::{ BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, LetStatement, MemberAccessExpression, - MethodCallExpression, NoirTraitImpl, Path, Pattern, PrefixExpression, Statement, StatementKind, - TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData, + MethodCallExpression, NoirTraitImpl, Path, PathSegment, Pattern, PrefixExpression, Statement, + StatementKind, TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData, }; use noirc_frontend::token::SecondaryAttribute; @@ -18,6 +18,10 @@ pub fn ident_path(name: &str) -> Path { Path::from_ident(ident(name)) } +pub fn path_segment(name: &str) -> PathSegment { + PathSegment::from(ident(name)) +} + pub fn path(ident: Ident) -> Path { Path::from_ident(ident) } @@ -27,15 +31,15 @@ pub fn expression(kind: ExpressionKind) -> Expression { } pub fn variable(name: &str) -> Expression { - expression(ExpressionKind::Variable(ident_path(name), None)) + expression(ExpressionKind::Variable(ident_path(name))) } pub fn variable_ident(identifier: Ident) -> Expression { - expression(ExpressionKind::Variable(path(identifier), None)) + expression(ExpressionKind::Variable(path(identifier))) } pub fn variable_path(path: Path) -> Expression { - expression(ExpressionKind::Variable(path, None)) + expression(ExpressionKind::Variable(path)) } pub fn method_call( @@ -149,7 +153,7 @@ macro_rules! chained_path { { let mut base_path = ident_path($base); $( - base_path.segments.push(ident($tail)); + base_path.segments.push(path_segment($tail)); )* base_path } @@ -163,7 +167,7 @@ macro_rules! chained_dep { let mut base_path = ident_path($base); base_path.kind = PathKind::Plain; $( - base_path.segments.push(ident($tail)); + base_path.segments.push(path_segment($tail)); )* base_path } diff --git a/noir/noir-repo/aztec_macros/src/utils/mod.rs b/noir/noir-repo/aztec_macros/src/utils/mod.rs index c8914f830250..6809fe9f1541 100644 --- a/noir/noir-repo/aztec_macros/src/utils/mod.rs +++ b/noir/noir-repo/aztec_macros/src/utils/mod.rs @@ -3,3 +3,4 @@ pub mod checks; pub mod constants; pub mod errors; pub mod hir_utils; +pub mod parse_utils; diff --git a/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs new file mode 100644 index 000000000000..06093a4753e6 --- /dev/null +++ b/noir/noir-repo/aztec_macros/src/utils/parse_utils.rs @@ -0,0 +1,533 @@ +use noirc_frontend::{ + ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, + ConstrainStatement, ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, + ForRange, FunctionReturnType, Ident, IfExpression, IndexExpression, InfixExpression, + LValue, Lambda, LetStatement, Literal, MemberAccessExpression, MethodCallExpression, + ModuleDeclaration, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, Path, + PathSegment, Pattern, PrefixExpression, Statement, StatementKind, TraitImplItem, TraitItem, + TypeImpl, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, + UnresolvedTypeData, UnresolvedTypeExpression, UseTree, UseTreeKind, + }, + parser::{Item, ItemKind, ParsedSubModule, ParserError}, + ParsedModule, +}; + +/// Parses a program and will clear out (set them to a default) any spans in it if `empty_spans` is true. +/// We want to do this in code generated by macros when running in LSP mode so that the generated +/// code doesn't end up overlapping real code, messing with how inlay hints, hover, etc., work. +pub fn parse_program(source_program: &str, empty_spans: bool) -> (ParsedModule, Vec) { + let (mut parsed_program, errors) = noirc_frontend::parse_program(source_program); + if empty_spans { + empty_parsed_module(&mut parsed_program); + } + (parsed_program, errors) +} + +fn empty_parsed_module(parsed_module: &mut ParsedModule) { + for item in parsed_module.items.iter_mut() { + empty_item(item); + } +} + +fn empty_item(item: &mut Item) { + item.span = Default::default(); + + match &mut item.kind { + ItemKind::Function(noir_function) => empty_noir_function(noir_function), + ItemKind::Trait(noir_trait) => { + empty_noir_trait(noir_trait); + } + ItemKind::TraitImpl(noir_trait_impl) => { + empty_noir_trait_impl(noir_trait_impl); + } + ItemKind::Impl(type_impl) => { + empty_type_impl(type_impl); + } + ItemKind::Global(let_statement) => empty_let_statement(let_statement), + ItemKind::Submodules(parsed_submodule) => { + empty_parsed_submodule(parsed_submodule); + } + ItemKind::ModuleDecl(module_declaration) => empty_module_declaration(module_declaration), + ItemKind::Import(use_tree) => empty_use_tree(use_tree), + ItemKind::Struct(noir_struct) => empty_noir_struct(noir_struct), + ItemKind::TypeAlias(noir_type_alias) => empty_noir_type_alias(noir_type_alias), + } +} + +fn empty_noir_trait(noir_trait: &mut NoirTrait) { + noir_trait.span = Default::default(); + + empty_ident(&mut noir_trait.name); + empty_unresolved_generics(&mut noir_trait.generics); + empty_unresolved_trait_constraints(&mut noir_trait.where_clause); + for item in noir_trait.items.iter_mut() { + empty_trait_item(item); + } +} + +fn empty_noir_trait_impl(noir_trait_impl: &mut NoirTraitImpl) { + empty_path(&mut noir_trait_impl.trait_name); + empty_unresolved_generics(&mut noir_trait_impl.impl_generics); + empty_unresolved_type(&mut noir_trait_impl.object_type); + empty_unresolved_trait_constraints(&mut noir_trait_impl.where_clause); + for item in noir_trait_impl.items.iter_mut() { + empty_trait_impl_item(item); + } +} + +fn empty_type_impl(type_impl: &mut TypeImpl) { + empty_unresolved_type(&mut type_impl.object_type); + type_impl.type_span = Default::default(); + empty_unresolved_generics(&mut type_impl.generics); + empty_unresolved_trait_constraints(&mut type_impl.where_clause); + for (noir_function, _) in type_impl.methods.iter_mut() { + empty_noir_function(noir_function); + } +} + +fn empty_noir_function(noir_function: &mut NoirFunction) { + let def = &mut noir_function.def; + + def.span = Default::default(); + empty_ident(&mut def.name); + empty_unresolved_generics(&mut def.generics); + + for param in def.parameters.iter_mut() { + param.span = Default::default(); + empty_unresolved_type(&mut param.typ); + empty_pattern(&mut param.pattern); + } + + empty_unresolved_trait_constraints(&mut def.where_clause); + empty_function_return_type(&mut def.return_type); + empty_block_expression(&mut def.body); +} + +fn empty_trait_item(trait_item: &mut TraitItem) { + match trait_item { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body } => { + empty_ident(name); + empty_unresolved_generics(generics); + for (name, typ) in parameters.iter_mut() { + empty_ident(name); + empty_unresolved_type(typ); + } + empty_function_return_type(return_type); + for trait_constraint in where_clause.iter_mut() { + empty_unresolved_trait_constraint(trait_constraint); + } + if let Some(body) = body { + empty_block_expression(body); + } + } + TraitItem::Constant { name, typ, default_value } => { + empty_ident(name); + empty_unresolved_type(typ); + if let Some(default_value) = default_value { + empty_expression(default_value); + } + } + TraitItem::Type { name } => { + empty_ident(name); + } + } +} + +fn empty_trait_impl_item(trait_impl_item: &mut TraitImplItem) { + match trait_impl_item { + TraitImplItem::Function(noir_function) => empty_noir_function(noir_function), + TraitImplItem::Constant(name, typ, default_value) => { + empty_ident(name); + empty_unresolved_type(typ); + empty_expression(default_value); + } + TraitImplItem::Type { name, alias } => { + empty_ident(name); + empty_unresolved_type(alias); + } + } +} + +fn empty_let_statement(let_statement: &mut LetStatement) { + empty_pattern(&mut let_statement.pattern); + empty_unresolved_type(&mut let_statement.r#type); + empty_expression(&mut let_statement.expression); +} + +fn empty_parsed_submodule(parsed_submodule: &mut ParsedSubModule) { + empty_ident(&mut parsed_submodule.name); + empty_parsed_module(&mut parsed_submodule.contents); +} + +fn empty_module_declaration(module_declaration: &mut ModuleDeclaration) { + empty_ident(&mut module_declaration.ident); +} + +fn empty_use_tree(use_tree: &mut UseTree) { + empty_path(&mut use_tree.prefix); + + match &mut use_tree.kind { + UseTreeKind::Path(name, alias) => { + empty_ident(name); + if let Some(alias) = alias { + empty_ident(alias); + } + } + UseTreeKind::List(use_trees) => { + for use_tree in use_trees.iter_mut() { + empty_use_tree(use_tree); + } + } + } +} + +fn empty_noir_struct(noir_struct: &mut NoirStruct) { + noir_struct.span = Default::default(); + empty_ident(&mut noir_struct.name); + for (name, typ) in noir_struct.fields.iter_mut() { + empty_ident(name); + empty_unresolved_type(typ); + } + empty_unresolved_generics(&mut noir_struct.generics); +} + +fn empty_noir_type_alias(noir_type_alias: &mut NoirTypeAlias) { + noir_type_alias.span = Default::default(); + empty_ident(&mut noir_type_alias.name); + empty_unresolved_type(&mut noir_type_alias.typ); +} + +fn empty_block_expression(block_expression: &mut BlockExpression) { + for statement in block_expression.statements.iter_mut() { + empty_statement(statement); + } +} + +fn empty_statement(statement: &mut Statement) { + statement.span = Default::default(); + + match &mut statement.kind { + StatementKind::Let(let_statement) => empty_let_statement(let_statement), + StatementKind::Constrain(constrain_statement) => { + empty_constrain_statement(constrain_statement) + } + StatementKind::Expression(expression) => empty_expression(expression), + StatementKind::Assign(assign_statement) => empty_assign_statement(assign_statement), + StatementKind::For(for_loop_statement) => empty_for_loop_statement(for_loop_statement), + StatementKind::Comptime(statement) => empty_statement(statement), + StatementKind::Semi(expression) => empty_expression(expression), + StatementKind::Break | StatementKind::Continue | StatementKind::Error => (), + } +} + +fn empty_constrain_statement(constrain_statement: &mut ConstrainStatement) { + empty_expression(&mut constrain_statement.0); + if let Some(expression) = &mut constrain_statement.1 { + empty_expression(expression); + } +} + +fn empty_expressions(expressions: &mut [Expression]) { + for expression in expressions.iter_mut() { + empty_expression(expression); + } +} + +fn empty_expression(expression: &mut Expression) { + expression.span = Default::default(); + + match &mut expression.kind { + ExpressionKind::Literal(literal) => empty_literal(literal), + ExpressionKind::Block(block_expression) => empty_block_expression(block_expression), + ExpressionKind::Prefix(prefix_expression) => empty_prefix_expression(prefix_expression), + ExpressionKind::Index(index_expression) => empty_index_expression(index_expression), + ExpressionKind::Call(call_expression) => empty_call_expression(call_expression), + ExpressionKind::MethodCall(method_call_expression) => { + empty_method_call_expression(method_call_expression) + } + ExpressionKind::Constructor(constructor_expression) => { + empty_constructor_expression(constructor_expression) + } + ExpressionKind::MemberAccess(member_access_expression) => { + empty_member_access_expression(member_access_expression) + } + ExpressionKind::Cast(cast_expression) => empty_cast_expression(cast_expression), + ExpressionKind::Infix(infix_expression) => empty_infix_expression(infix_expression), + ExpressionKind::If(if_expression) => empty_if_expression(if_expression), + ExpressionKind::Variable(path) => empty_path(path), + ExpressionKind::Tuple(expressions) => { + empty_expressions(expressions); + } + ExpressionKind::Lambda(lambda) => empty_lambda(lambda), + ExpressionKind::Parenthesized(expression) => empty_expression(expression), + ExpressionKind::Unquote(expression) => { + empty_expression(expression); + } + ExpressionKind::Comptime(block_expression, _span) => { + empty_block_expression(block_expression); + } + ExpressionKind::Quote(..) | ExpressionKind::Resolved(_) | ExpressionKind::Error => (), + } +} + +fn empty_assign_statement(assign_statement: &mut AssignStatement) { + empty_lvalue(&mut assign_statement.lvalue); + empty_expression(&mut assign_statement.expression); +} + +fn empty_for_loop_statement(for_loop_statement: &mut ForLoopStatement) { + for_loop_statement.span = Default::default(); + empty_ident(&mut for_loop_statement.identifier); + empty_for_range(&mut for_loop_statement.range); + empty_expression(&mut for_loop_statement.block); +} + +fn empty_unresolved_types(unresolved_types: &mut [UnresolvedType]) { + for unresolved_type in unresolved_types.iter_mut() { + empty_unresolved_type(unresolved_type); + } +} + +fn empty_unresolved_type(unresolved_type: &mut UnresolvedType) { + unresolved_type.span = Default::default(); + + match &mut unresolved_type.typ { + UnresolvedTypeData::Array(unresolved_type_expression, unresolved_type) => { + empty_unresolved_type_expression(unresolved_type_expression); + empty_unresolved_type(unresolved_type); + } + UnresolvedTypeData::Slice(unresolved_type) => empty_unresolved_type(unresolved_type), + UnresolvedTypeData::Expression(unresolved_type_expression) => { + empty_unresolved_type_expression(unresolved_type_expression) + } + UnresolvedTypeData::FormatString(unresolved_type_expression, unresolved_type) => { + empty_unresolved_type_expression(unresolved_type_expression); + empty_unresolved_type(unresolved_type); + } + UnresolvedTypeData::Parenthesized(unresolved_type) => { + empty_unresolved_type(unresolved_type) + } + UnresolvedTypeData::Named(path, unresolved_types, _) => { + empty_path(path); + empty_unresolved_types(unresolved_types); + } + UnresolvedTypeData::TraitAsType(path, unresolved_types) => { + empty_path(path); + empty_unresolved_types(unresolved_types); + } + UnresolvedTypeData::MutableReference(unresolved_type) => { + empty_unresolved_type(unresolved_type) + } + UnresolvedTypeData::Tuple(unresolved_types) => empty_unresolved_types(unresolved_types), + UnresolvedTypeData::Function(args, ret, _env) => { + empty_unresolved_types(args); + empty_unresolved_type(ret); + } + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Unit + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => (), + } +} + +fn empty_unresolved_generics(unresolved_generic: &mut UnresolvedGenerics) { + for generic in unresolved_generic.iter_mut() { + empty_unresolved_generic(generic); + } +} + +fn empty_unresolved_generic(unresolved_generic: &mut UnresolvedGeneric) { + match unresolved_generic { + UnresolvedGeneric::Variable(ident) => empty_ident(ident), + UnresolvedGeneric::Numeric { ident, typ } => { + empty_ident(ident); + empty_unresolved_type(typ); + } + } +} + +fn empty_pattern(pattern: &mut Pattern) { + match pattern { + Pattern::Identifier(ident) => empty_ident(ident), + Pattern::Mutable(pattern, _span, _) => { + empty_pattern(pattern); + } + Pattern::Tuple(patterns, _) => { + for pattern in patterns.iter_mut() { + empty_pattern(pattern); + } + } + Pattern::Struct(path, patterns, _) => { + empty_path(path); + for (name, pattern) in patterns.iter_mut() { + empty_ident(name); + empty_pattern(pattern); + } + } + } +} + +fn empty_unresolved_trait_constraints( + unresolved_trait_constriants: &mut [UnresolvedTraitConstraint], +) { + for trait_constraint in unresolved_trait_constriants.iter_mut() { + empty_unresolved_trait_constraint(trait_constraint); + } +} + +fn empty_unresolved_trait_constraint(unresolved_trait_constraint: &mut UnresolvedTraitConstraint) { + empty_unresolved_type(&mut unresolved_trait_constraint.typ); +} + +fn empty_function_return_type(function_return_type: &mut FunctionReturnType) { + match function_return_type { + FunctionReturnType::Ty(unresolved_type) => empty_unresolved_type(unresolved_type), + FunctionReturnType::Default(_) => (), + } +} + +fn empty_ident(ident: &mut Ident) { + ident.0.set_span(Default::default()); +} + +fn empty_path(path: &mut Path) { + path.span = Default::default(); + for segment in path.segments.iter_mut() { + empty_path_segment(segment); + } +} + +fn empty_path_segment(segment: &mut PathSegment) { + segment.span = Default::default(); + empty_ident(&mut segment.ident); +} + +fn empty_literal(literal: &mut Literal) { + match literal { + Literal::Array(array_literal) => empty_array_literal(array_literal), + Literal::Slice(array_literal) => empty_array_literal(array_literal), + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => (), + } +} + +fn empty_array_literal(array_literal: &mut ArrayLiteral) { + match array_literal { + ArrayLiteral::Standard(expressions) => { + empty_expressions(expressions); + } + ArrayLiteral::Repeated { repeated_element, length } => { + empty_expression(repeated_element); + empty_expression(length); + } + } +} + +fn empty_prefix_expression(prefix_expression: &mut PrefixExpression) { + empty_expression(&mut prefix_expression.rhs); +} + +fn empty_index_expression(index_expression: &mut IndexExpression) { + empty_expression(&mut index_expression.collection); + empty_expression(&mut index_expression.index); +} + +fn empty_call_expression(call_expression: &mut CallExpression) { + empty_expression(&mut call_expression.func); + empty_expressions(&mut call_expression.arguments); +} + +fn empty_method_call_expression(method_call_expression: &mut MethodCallExpression) { + empty_expression(&mut method_call_expression.object); + empty_ident(&mut method_call_expression.method_name); + if let Some(generics) = &mut method_call_expression.generics { + empty_unresolved_types(generics); + } + empty_expressions(&mut method_call_expression.arguments); +} + +fn empty_constructor_expression(constructor_expression: &mut ConstructorExpression) { + empty_path(&mut constructor_expression.type_name); + for (name, expression) in constructor_expression.fields.iter_mut() { + empty_ident(name); + empty_expression(expression); + } +} + +fn empty_member_access_expression(member_access_expression: &mut MemberAccessExpression) { + empty_expression(&mut member_access_expression.lhs); + empty_ident(&mut member_access_expression.rhs); +} + +fn empty_cast_expression(cast_expression: &mut CastExpression) { + empty_expression(&mut cast_expression.lhs); + empty_unresolved_type(&mut cast_expression.r#type); +} + +fn empty_infix_expression(infix_expression: &mut InfixExpression) { + empty_expression(&mut infix_expression.lhs); + empty_expression(&mut infix_expression.rhs); +} + +fn empty_if_expression(if_expression: &mut IfExpression) { + empty_expression(&mut if_expression.condition); + empty_expression(&mut if_expression.consequence); + if let Some(alternative) = &mut if_expression.alternative { + empty_expression(alternative); + } +} + +fn empty_lambda(lambda: &mut Lambda) { + for (name, typ) in lambda.parameters.iter_mut() { + empty_pattern(name); + empty_unresolved_type(typ); + } + empty_unresolved_type(&mut lambda.return_type); + empty_expression(&mut lambda.body); +} + +fn empty_lvalue(lvalue: &mut LValue) { + match lvalue { + LValue::Ident(ident) => empty_ident(ident), + LValue::MemberAccess { ref mut object, ref mut field_name, span: _ } => { + empty_lvalue(object); + empty_ident(field_name); + } + LValue::Index { ref mut array, ref mut index, span: _ } => { + empty_lvalue(array); + empty_expression(index); + } + LValue::Dereference(lvalue, _) => empty_lvalue(lvalue), + } +} + +fn empty_for_range(for_range: &mut ForRange) { + match for_range { + ForRange::Range(from, to) => { + empty_expression(from); + empty_expression(to); + } + ForRange::Array(expression) => empty_expression(expression), + } +} + +fn empty_unresolved_type_expression(unresolved_type_expression: &mut UnresolvedTypeExpression) { + match unresolved_type_expression { + UnresolvedTypeExpression::Variable(path) => empty_path(path), + UnresolvedTypeExpression::BinaryOperation(lhs, _, rhs, _) => { + empty_unresolved_type_expression(lhs); + empty_unresolved_type_expression(rhs); + } + UnresolvedTypeExpression::Constant(_, _) => (), + } +} diff --git a/noir/noir-repo/compiler/fm/src/file_map.rs b/noir/noir-repo/compiler/fm/src/file_map.rs index 50412d352ec4..ba552fe51561 100644 --- a/noir/noir-repo/compiler/fm/src/file_map.rs +++ b/noir/noir-repo/compiler/fm/src/file_map.rs @@ -34,6 +34,7 @@ impl From<&PathBuf> for PathString { pub struct FileMap { files: SimpleFiles, name_to_id: HashMap, + current_dir: Option, } // XXX: Note that we derive Default here due to ModuleOrigin requiring us to set a FileId @@ -82,7 +83,11 @@ impl FileMap { } impl Default for FileMap { fn default() -> Self { - FileMap { files: SimpleFiles::new(), name_to_id: HashMap::new() } + FileMap { + files: SimpleFiles::new(), + name_to_id: HashMap::new(), + current_dir: std::env::current_dir().ok(), + } } } @@ -92,7 +97,16 @@ impl<'a> Files<'a> for FileMap { type Source = &'a str; fn name(&self, file_id: Self::FileId) -> Result { - Ok(self.files.get(file_id.as_usize())?.name().clone()) + let name = self.files.get(file_id.as_usize())?.name().clone(); + + // See if we can make the file name a bit shorter/easier to read if it starts with the current directory + if let Some(current_dir) = &self.current_dir { + if let Ok(name_without_prefix) = name.0.strip_prefix(current_dir) { + return Ok(PathString::from_path(name_without_prefix.to_path_buf())); + } + } + + Ok(name) } fn source(&'a self, file_id: Self::FileId) -> Result { diff --git a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs index d0b33945f40e..6625eba5a0a3 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -116,7 +116,7 @@ fn to_abi_visibility(value: Visibility) -> AbiVisibility { match value { Visibility::Public => AbiVisibility::Public, Visibility::Private => AbiVisibility::Private, - Visibility::DataBus => AbiVisibility::DataBus, + Visibility::CallData(_) | Visibility::ReturnData => AbiVisibility::DataBus, } } diff --git a/noir/noir-repo/compiler/noirc_driver/src/lib.rs b/noir/noir-repo/compiler/noirc_driver/src/lib.rs index dd774a1eeec7..f430eb8ad197 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/lib.rs @@ -56,6 +56,12 @@ pub struct CompileOptions { #[arg(long, value_parser = parse_expression_width)] pub expression_width: Option, + /// Generate ACIR with the target backend expression width. + /// The default is to generate ACIR without a bound and split expressions after code generation. + /// Activating this flag can sometimes provide optimizations for certain programs. + #[arg(long, default_value = "false")] + pub bounded_codegen: bool, + /// Force a full recompilation. #[arg(long = "force")] pub force_compile: bool, @@ -512,6 +518,12 @@ fn compile_contract_inner( } } +/// Default expression width used for Noir compilation. +/// The ACVM native type `ExpressionWidth` has its own default which should always be unbounded, +/// while we can sometimes expect the compilation target width to change. +/// Thus, we set it separately here rather than trying to alter the default derivation of the type. +pub const DEFAULT_EXPRESSION_WIDTH: ExpressionWidth = ExpressionWidth::Bounded { width: 4 }; + /// Compile the current crate using `main_function` as the entrypoint. /// /// This function assumes [`check_crate`] is called beforehand. @@ -550,6 +562,11 @@ pub fn compile_no_check( enable_brillig_logging: options.show_brillig, force_brillig_output: options.force_brillig, print_codegen_timings: options.benchmark_codegen, + expression_width: if options.bounded_codegen { + options.expression_width.unwrap_or(DEFAULT_EXPRESSION_WIDTH) + } else { + ExpressionWidth::default() + }, }; let SsaProgramArtifact { program, debug, warnings, names, error_types, .. } = diff --git a/noir/noir-repo/compiler/noirc_errors/src/position.rs b/noir/noir-repo/compiler/noirc_errors/src/position.rs index 9f9879e1d1b0..02b242e8b4da 100644 --- a/noir/noir-repo/compiler/noirc_errors/src/position.rs +++ b/noir/noir-repo/compiler/noirc_errors/src/position.rs @@ -43,6 +43,10 @@ impl Spanned { pub fn span(&self) -> Span { self.span } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } } impl std::borrow::Borrow for Spanned { diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 81327cec0137..41dbf3b72726 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -42,6 +42,22 @@ pub mod ir; mod opt; pub mod ssa_gen; +pub struct SsaEvaluatorOptions { + /// Emit debug information for the intermediate SSA IR + pub enable_ssa_logging: bool, + + pub enable_brillig_logging: bool, + + /// Force Brillig output (for step debugging) + pub force_brillig_output: bool, + + /// Pretty print benchmark times of each code generation pass + pub print_codegen_timings: bool, + + /// Width of expressions to be used for ACIR + pub expression_width: ExpressionWidth, +} + pub(crate) struct ArtifactsAndWarnings(Artifacts, Vec); /// Optimize the given program by converting it into SSA @@ -99,7 +115,9 @@ pub(crate) fn optimize_into_acir( drop(ssa_gen_span_guard); - let artifacts = time("SSA to ACIR", options.print_codegen_timings, || ssa.into_acir(&brillig))?; + let artifacts = time("SSA to ACIR", options.print_codegen_timings, || { + ssa.into_acir(&brillig, options.expression_width) + })?; Ok(ArtifactsAndWarnings(artifacts, ssa_level_warnings)) } @@ -160,19 +178,6 @@ impl SsaProgramArtifact { } } -pub struct SsaEvaluatorOptions { - /// Emit debug information for the intermediate SSA IR - pub enable_ssa_logging: bool, - - pub enable_brillig_logging: bool, - - /// Force Brillig output (for step debugging) - pub force_brillig_output: bool, - - /// Pretty print benchmark times of each code generation pass - pub print_codegen_timings: bool, -} - /// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program]. /// /// The output ACIR is backend-agnostic and so must go through a transformation pass before usage in proof generation. diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 629cc491ba6b..fdad06a520b2 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -9,7 +9,7 @@ use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, BlockType, MemOp}; -use acvm::acir::circuit::{AssertionPayload, ExpressionOrMemory, Opcode}; +use acvm::acir::circuit::{AssertionPayload, ExpressionOrMemory, ExpressionWidth, Opcode}; use acvm::blackbox_solver; use acvm::brillig_vm::{MemoryValue, VMStatus, VM}; use acvm::{ @@ -24,6 +24,7 @@ use acvm::{ use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; use num_bigint::BigUint; +use std::cmp::Ordering; use std::{borrow::Cow, hash::Hash}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -124,9 +125,15 @@ pub(crate) struct AcirContext { /// The BigIntContext, used to generate identifiers for BigIntegers big_int_ctx: BigIntContext, + + expression_width: ExpressionWidth, } impl AcirContext { + pub(crate) fn set_expression_width(&mut self, expression_width: ExpressionWidth) { + self.expression_width = expression_width; + } + pub(crate) fn current_witness_index(&self) -> Witness { self.acir_ir.current_witness_index() } @@ -584,6 +591,7 @@ impl AcirContext { pub(crate) fn mul_var(&mut self, lhs: AcirVar, rhs: AcirVar) -> Result { let lhs_data = self.vars[&lhs].clone(); let rhs_data = self.vars[&rhs].clone(); + let result = match (lhs_data, rhs_data) { // (x * 1) == (1 * x) == x (AcirVarData::Const(constant), _) if constant.is_one() => rhs, @@ -655,6 +663,7 @@ impl AcirContext { self.mul_var(lhs, rhs)? } }; + Ok(result) } @@ -670,9 +679,62 @@ impl AcirContext { pub(crate) fn add_var(&mut self, lhs: AcirVar, rhs: AcirVar) -> Result { let lhs_expr = self.var_to_expression(lhs)?; let rhs_expr = self.var_to_expression(rhs)?; + let sum_expr = &lhs_expr + &rhs_expr; + if fits_in_one_identity(&sum_expr, self.expression_width) { + let sum_var = self.add_data(AcirVarData::from(sum_expr)); + + return Ok(sum_var); + } + + let sum_expr = match lhs_expr.width().cmp(&rhs_expr.width()) { + Ordering::Greater => { + let lhs_witness_var = self.get_or_create_witness_var(lhs)?; + let lhs_witness_expr = self.var_to_expression(lhs_witness_var)?; + + let new_sum_expr = &lhs_witness_expr + &rhs_expr; + if fits_in_one_identity(&new_sum_expr, self.expression_width) { + new_sum_expr + } else { + let rhs_witness_var = self.get_or_create_witness_var(rhs)?; + let rhs_witness_expr = self.var_to_expression(rhs_witness_var)?; + + &lhs_expr + &rhs_witness_expr + } + } + Ordering::Less => { + let rhs_witness_var = self.get_or_create_witness_var(rhs)?; + let rhs_witness_expr = self.var_to_expression(rhs_witness_var)?; + + let new_sum_expr = &lhs_expr + &rhs_witness_expr; + if fits_in_one_identity(&new_sum_expr, self.expression_width) { + new_sum_expr + } else { + let lhs_witness_var = self.get_or_create_witness_var(lhs)?; + let lhs_witness_expr = self.var_to_expression(lhs_witness_var)?; - Ok(self.add_data(AcirVarData::from(sum_expr))) + &lhs_witness_expr + &rhs_expr + } + } + Ordering::Equal => { + let lhs_witness_var = self.get_or_create_witness_var(lhs)?; + let lhs_witness_expr = self.var_to_expression(lhs_witness_var)?; + + let new_sum_expr = &lhs_witness_expr + &rhs_expr; + if fits_in_one_identity(&new_sum_expr, self.expression_width) { + new_sum_expr + } else { + let rhs_witness_var = self.get_or_create_witness_var(rhs)?; + let rhs_witness_expr = self.var_to_expression(rhs_witness_var)?; + + &lhs_witness_expr + &rhs_witness_expr + } + } + }; + + let sum_var = self.add_data(AcirVarData::from(sum_expr)); + + Ok(sum_var) } /// Adds a new Variable to context whose value will @@ -1990,6 +2052,23 @@ impl From> for AcirVarData { } } +/// Checks if this expression can fit into one arithmetic identity +fn fits_in_one_identity(expr: &Expression, width: ExpressionWidth) -> bool { + let width = match &width { + ExpressionWidth::Unbounded => { + return true; + } + ExpressionWidth::Bounded { width } => *width, + }; + + // A Polynomial with more than one mul term cannot fit into one opcode + if expr.mul_terms.len() > 1 { + return false; + }; + + expr.width() <= width +} + /// A Reference to an `AcirVarData` #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct AcirVar(usize); 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 a75aabe6a03c..0e4bbbf759cb 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 @@ -33,7 +33,7 @@ use acvm::acir::circuit::opcodes::BlockType; use noirc_frontend::monomorphization::ast::InlineType; use acvm::acir::circuit::brillig::BrilligBytecode; -use acvm::acir::circuit::{AssertionPayload, ErrorSelector, OpcodeLocation}; +use acvm::acir::circuit::{AssertionPayload, ErrorSelector, ExpressionWidth, OpcodeLocation}; use acvm::acir::native_types::Witness; use acvm::acir::BlackBoxFunc; use acvm::{acir::circuit::opcodes::BlockId, acir::AcirField, FieldElement}; @@ -282,12 +282,16 @@ pub(crate) type Artifacts = ( impl Ssa { #[tracing::instrument(level = "trace", skip_all)] - pub(crate) fn into_acir(self, brillig: &Brillig) -> Result { + pub(crate) fn into_acir( + self, + brillig: &Brillig, + expression_width: ExpressionWidth, + ) -> Result { let mut acirs = Vec::new(); - // TODO: can we parallelise this? + // TODO: can we parallelize this? let mut shared_context = SharedContext::default(); for function in self.functions.values() { - let context = Context::new(&mut shared_context); + let context = Context::new(&mut shared_context, expression_width); if let Some(mut generated_acir) = context.convert_ssa_function(&self, function, brillig)? { @@ -334,8 +338,12 @@ impl Ssa { } impl<'a> Context<'a> { - fn new(shared_context: &'a mut SharedContext) -> Context<'a> { + fn new( + shared_context: &'a mut SharedContext, + expression_width: ExpressionWidth, + ) -> Context<'a> { let mut acir_context = AcirContext::default(); + acir_context.set_expression_width(expression_width); let current_side_effects_enabled_var = acir_context.add_constant(FieldElement::one()); Context { @@ -422,6 +430,12 @@ impl<'a> Context<'a> { let (return_vars, return_warnings) = self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?; + let call_data_arrays: Vec = + self.data_bus.call_data.iter().map(|cd| cd.array_id).collect(); + for call_data_array in call_data_arrays { + self.ensure_array_is_initialized(call_data_array, dfg)?; + } + // TODO: This is a naive method of assigning the return values to their witnesses as // we're likely to get a number of constraints which are asserting one witness to be equal to another. // @@ -1255,20 +1269,23 @@ impl<'a> Context<'a> { 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) { - // 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] 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); - } + if let Some(call_data) = + self.data_bus.call_data.iter().find(|cd| cd.index_map.contains_key(&array)) + { + 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(call_data.index_map[&array] as i128)); + let new_index = self.acir_context.add_var(offset, bus_index)?; + return self.array_get( + instruction, + call_data.array_id, + new_index, + dfg, + index_side_effect, + ); } // Compiler sanity check @@ -1288,6 +1305,7 @@ impl<'a> Context<'a> { index_side_effect = false; } } + // Fallback to multiplication if the index side_effects have not already been handled if index_side_effect { // Set the value to 0 if current_side_effects is 0, to ensure it fits in any value type @@ -1698,17 +1716,20 @@ impl<'a> Context<'a> { len: usize, value: Option, ) -> Result<(), InternalError> { - let databus = if self.data_bus.call_data.is_some() - && self.block_id(&self.data_bus.call_data.unwrap()) == array - { - BlockType::CallData - } else if self.data_bus.return_data.is_some() + let mut databus = BlockType::Memory; + if self.data_bus.return_data.is_some() && self.block_id(&self.data_bus.return_data.unwrap()) == array { - BlockType::ReturnData - } else { - BlockType::Memory - }; + databus = BlockType::ReturnData; + } + for array_id in self.data_bus.call_data_array() { + if self.block_id(&array_id) == array { + assert!(databus == BlockType::Memory); + databus = BlockType::CallData; + break; + } + } + self.acir_context.initialize_array(array, len, value, databus)?; self.initialized_arrays.insert(array); Ok(()) @@ -2820,7 +2841,7 @@ mod test { use acvm::{ acir::{ - circuit::{Opcode, OpcodeLocation}, + circuit::{ExpressionWidth, Opcode, OpcodeLocation}, native_types::Witness, }, FieldElement, @@ -2917,7 +2938,7 @@ mod test { let ssa = builder.finish(); let (acir_functions, _, _) = ssa - .into_acir(&Brillig::default()) + .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // Expected result: // main f0 @@ -3012,7 +3033,7 @@ mod test { let ssa = builder.finish(); let (acir_functions, _, _) = ssa - .into_acir(&Brillig::default()) + .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the above test expect that the input witnesses of the `Call` // opcodes will be different. The changes can discerned from the checks below. @@ -3102,7 +3123,7 @@ mod test { let ssa = builder.finish(); let (acir_functions, _, _) = ssa - .into_acir(&Brillig::default()) + .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 3, "Should have three ACIR functions"); @@ -3215,8 +3236,9 @@ mod test { let ssa = builder.finish(); let brillig = ssa.to_brillig(false); - let (acir_functions, brillig_functions, _) = - ssa.into_acir(&brillig).expect("Should compile manually written SSA into ACIR"); + let (acir_functions, brillig_functions, _) = ssa + .into_acir(&brillig, ExpressionWidth::default()) + .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 1, "Should only have a `main` ACIR function"); assert_eq!(brillig_functions.len(), 2, "Should only have generated two Brillig functions"); @@ -3272,7 +3294,7 @@ mod test { // The Brillig bytecode we insert for the stdlib is hardcoded so we do not need to provide any // Brillig artifacts to the ACIR gen pass. let (acir_functions, brillig_functions, _) = ssa - .into_acir(&Brillig::default()) + .into_acir(&Brillig::default(), ExpressionWidth::default()) .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 1, "Should only have a `main` ACIR function"); @@ -3343,8 +3365,9 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = - ssa.into_acir(&brillig).expect("Should compile manually written SSA into ACIR"); + let (acir_functions, brillig_functions, _) = ssa + .into_acir(&brillig, ExpressionWidth::default()) + .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 1, "Should only have a `main` ACIR function"); // We expect 3 brillig functions: @@ -3431,8 +3454,9 @@ mod test { let brillig = ssa.to_brillig(false); println!("{}", ssa); - let (acir_functions, brillig_functions, _) = - ssa.into_acir(&brillig).expect("Should compile manually written SSA into ACIR"); + let (acir_functions, brillig_functions, _) = ssa + .into_acir(&brillig, ExpressionWidth::default()) + .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 2, "Should only have two ACIR functions"); // We expect 3 brillig functions: diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index 5f0660f5a79d..50964e9161b1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::rc::Rc; use crate::ssa::ir::{types::Type, value::ValueId}; @@ -8,6 +9,12 @@ use noirc_frontend::hir_def::function::FunctionSignature; use super::FunctionBuilder; +#[derive(Clone)] +pub(crate) enum DatabusVisibility { + None, + CallData(u32), + ReturnData, +} /// Used to create a data bus, which is an array of private inputs /// replacing public inputs pub(crate) struct DataBusBuilder { @@ -27,15 +34,16 @@ impl DataBusBuilder { } } - /// Generates a boolean vector telling which (ssa) parameter from the given function signature + /// Generates a vector telling which (ssa) parameters from the given function signature /// are tagged with databus visibility - pub(crate) fn is_databus(main_signature: &FunctionSignature) -> Vec { + pub(crate) fn is_databus(main_signature: &FunctionSignature) -> Vec { let mut params_is_databus = Vec::new(); for param in &main_signature.0 { let is_databus = match param.2 { - ast::Visibility::Public | ast::Visibility::Private => false, - ast::Visibility::DataBus => true, + ast::Visibility::Public | ast::Visibility::Private => DatabusVisibility::None, + ast::Visibility::CallData(id) => DatabusVisibility::CallData(id), + ast::Visibility::ReturnData => DatabusVisibility::ReturnData, }; let len = param.1.field_count() as usize; params_is_databus.extend(vec![is_databus; len]); @@ -44,34 +52,51 @@ impl DataBusBuilder { } } +#[derive(Clone, Debug)] +pub(crate) struct CallData { + pub(crate) array_id: ValueId, + pub(crate) index_map: HashMap, +} + #[derive(Clone, Default, Debug)] pub(crate) struct DataBus { - pub(crate) call_data: Option, - pub(crate) call_data_map: HashMap, + pub(crate) call_data: Vec, pub(crate) return_data: Option, } impl DataBus { /// Updates the databus values with the provided function pub(crate) fn map_values(&self, mut f: impl FnMut(ValueId) -> ValueId) -> DataBus { - let mut call_data_map = HashMap::default(); - for (k, v) in self.call_data_map.iter() { - call_data_map.insert(f(*k), *v); - } - DataBus { - call_data: self.call_data.map(&mut f), - call_data_map, - return_data: self.return_data.map(&mut f), - } + let call_data = self + .call_data + .iter() + .map(|cd| { + let mut call_data_map = HashMap::default(); + for (k, v) in cd.index_map.iter() { + call_data_map.insert(f(*k), *v); + } + CallData { array_id: f(cd.array_id), index_map: call_data_map } + }) + .collect(); + DataBus { call_data, return_data: self.return_data.map(&mut f) } } + pub(crate) fn call_data_array(&self) -> Vec { + self.call_data.iter().map(|cd| cd.array_id).collect() + } /// Construct a databus from call_data and return_data data bus builders - pub(crate) fn get_data_bus(call_data: DataBusBuilder, return_data: DataBusBuilder) -> DataBus { - DataBus { - call_data: call_data.databus, - call_data_map: call_data.map, - return_data: return_data.databus, + pub(crate) fn get_data_bus( + call_data: Vec, + return_data: DataBusBuilder, + ) -> DataBus { + let mut call_data_args = Vec::new(); + for call_data_item in call_data { + if let Some(array_id) = call_data_item.databus { + call_data_args.push(CallData { array_id, index_map: call_data_item.map }); + } } + + DataBus { call_data: call_data_args, return_data: return_data.databus } } } @@ -129,19 +154,36 @@ impl FunctionBuilder { } /// Generate the data bus for call-data, based on the parameters of the entry block - /// and a boolean vector telling which ones are call-data - pub(crate) fn call_data_bus(&mut self, is_params_databus: Vec) -> DataBusBuilder { + /// and a vector telling which ones are call-data + pub(crate) fn call_data_bus( + &mut self, + is_params_databus: Vec, + ) -> Vec { //filter parameters of the first block that have call-data visibility let first_block = self.current_function.entry_block(); let params = self.current_function.dfg[first_block].parameters(); - let mut databus_param = Vec::new(); - for (param, is_databus) in params.iter().zip(is_params_databus) { - if is_databus { - databus_param.push(param.to_owned()); + let mut databus_param: BTreeMap> = BTreeMap::new(); + for (param, databus_attribute) in params.iter().zip(is_params_databus) { + match databus_attribute { + DatabusVisibility::None | DatabusVisibility::ReturnData => continue, + DatabusVisibility::CallData(call_data_id) => { + if let std::collections::btree_map::Entry::Vacant(e) = + databus_param.entry(call_data_id) + { + e.insert(vec![param.to_owned()]); + } else { + databus_param.get_mut(&call_data_id).unwrap().push(param.to_owned()); + } + } } } - // create the call-data-bus from the filtered list - let call_data = DataBusBuilder::new(); - self.initialize_data_bus(&databus_param, call_data) + // create the call-data-bus from the filtered lists + let mut result = Vec::new(); + for id in databus_param.keys() { + let builder = DataBusBuilder::new(); + let call_databus = self.initialize_data_bus(&databus_param[id], builder); + result.push(call_databus); + } + result } } diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs index 42383680f445..24519d530ee8 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -34,8 +34,8 @@ impl Ssa { /// of its instructions are needed elsewhere. fn dead_instruction_elimination(function: &mut Function) { let mut context = Context::default(); - if let Some(call_data) = function.dfg.data_bus.call_data { - context.mark_used_instruction_results(&function.dfg, call_data); + for call_data in &function.dfg.data_bus.call_data { + context.mark_used_instruction_results(&function.dfg, call_data.array_id); } let blocks = PostOrder::with_function(function); 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 abd251b008fb..468a85733071 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 @@ -44,7 +44,7 @@ pub(crate) fn generate_ssa( // see which parameter has call_data/return_data attribute let is_databus = DataBusBuilder::is_databus(&program.main_function_signature); - let is_return_data = matches!(program.return_visibility, Visibility::DataBus); + let is_return_data = matches!(program.return_visibility, Visibility::ReturnData); let return_location = program.return_location; let context = SharedContext::new(program); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs index 057daa2bdde4..e176c7fc8b47 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -29,9 +29,7 @@ pub enum ExpressionKind { Cast(Box), Infix(Box), If(Box), - // The optional vec here is the optional list of generics - // provided by the turbofish operator, if used - Variable(Path, Option>), + Variable(Path), Tuple(Vec), Lambda(Box), Parenthesized(Box), @@ -118,7 +116,7 @@ impl From for UnresolvedGeneric { impl ExpressionKind { pub fn into_path(self) -> Option { match self { - ExpressionKind::Variable(path, _) => Some(path), + ExpressionKind::Variable(path) => Some(path), _ => None, } } @@ -265,29 +263,9 @@ impl Expression { arguments: Vec, span: Span, ) -> Expression { - // Need to check if lhs is an if expression since users can sequence if expressions - // with tuples without calling them. E.g. `if c { t } else { e }(a, b)` is interpreted - // as a sequence of { if, tuple } rather than a function call. This behavior matches rust. - let kind = if matches!(&lhs.kind, ExpressionKind::If(..)) { - ExpressionKind::Block(BlockExpression { - statements: vec![ - Statement { kind: StatementKind::Expression(lhs), span }, - Statement { - kind: StatementKind::Expression(Expression::new( - ExpressionKind::Tuple(arguments), - span, - )), - span, - }, - ], - }) - } else { - ExpressionKind::Call(Box::new(CallExpression { - func: Box::new(lhs), - is_macro_call, - arguments, - })) - }; + let func = Box::new(lhs); + let kind = + ExpressionKind::Call(Box::new(CallExpression { func, is_macro_call, arguments })); Expression::new(kind, span) } } @@ -583,14 +561,7 @@ impl Display for ExpressionKind { Cast(cast) => cast.fmt(f), Infix(infix) => infix.fmt(f), If(if_expr) => if_expr.fmt(f), - Variable(path, generics) => { - if let Some(generics) = generics { - let generics = vecmap(generics, ToString::to_string); - write!(f, "{path}::<{}>", generics.join(", ")) - } else { - path.fmt(f) - } - } + Variable(path) => path.fmt(f), Constructor(constructor) => constructor.fmt(f), MemberAccess(access) => access.fmt(f), Tuple(elements) => { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs index 038a13529d72..ef4932fa9f06 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -324,7 +324,7 @@ impl UnresolvedTypeExpression { Some(int) => Ok(UnresolvedTypeExpression::Constant(int, expr.span)), None => Err(expr), }, - ExpressionKind::Variable(path, _) => Ok(UnresolvedTypeExpression::Variable(path)), + ExpressionKind::Variable(path) => Ok(UnresolvedTypeExpression::Variable(path)), ExpressionKind::Prefix(prefix) if prefix.operator == UnaryOp::Minus => { let lhs = Box::new(UnresolvedTypeExpression::Constant(0, expr.span)); let rhs = Box::new(UnresolvedTypeExpression::from_expr_helper(prefix.rhs)?); @@ -390,7 +390,9 @@ pub enum Visibility { Private, /// DataBus is public input handled as private input. We use the fact that return values are properly computed by the program to avoid having them as public inputs /// it is useful for recursion and is handled by the proving system. - DataBus, + /// The u32 value is used to group inputs having the same value. + CallData(u32), + ReturnData, } impl std::fmt::Display for Visibility { @@ -398,7 +400,8 @@ impl std::fmt::Display for Visibility { match self { Self::Public => write!(f, "pub"), Self::Private => write!(f, "priv"), - Self::DataBus => write!(f, "databus"), + Self::CallData(id) => write!(f, "calldata{id}"), + Self::ReturnData => write!(f, "returndata"), } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs index b41efebc9058..8ce2e1a41c04 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -236,10 +236,11 @@ impl From for Expression { fn from(i: Ident) -> Expression { Expression { span: i.0.span(), - kind: ExpressionKind::Variable( - Path { span: i.span(), segments: vec![i], kind: PathKind::Plain }, - None, - ), + kind: ExpressionKind::Variable(Path { + span: i.span(), + segments: vec![PathSegment::from(i)], + kind: PathKind::Plain, + }), } } } @@ -362,18 +363,18 @@ impl UseTree { // it would most likely cause further errors during name resolution #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct Path { - pub segments: Vec, + pub segments: Vec, pub kind: PathKind, pub span: Span, } impl Path { - pub fn pop(&mut self) -> Ident { + pub fn pop(&mut self) -> PathSegment { self.segments.pop().unwrap() } fn join(mut self, ident: Ident) -> Path { - self.segments.push(ident); + self.segments.push(PathSegment::from(ident)); self } @@ -384,18 +385,37 @@ impl Path { } pub fn from_ident(name: Ident) -> Path { - Path { span: name.span(), segments: vec![name], kind: PathKind::Plain } + Path { span: name.span(), segments: vec![PathSegment::from(name)], kind: PathKind::Plain } } pub fn span(&self) -> Span { self.span } - pub fn last_segment(&self) -> Ident { + pub fn first_segment(&self) -> PathSegment { + assert!(!self.segments.is_empty()); + self.segments.first().unwrap().clone() + } + + pub fn last_segment(&self) -> PathSegment { assert!(!self.segments.is_empty()); self.segments.last().unwrap().clone() } + pub fn last_ident(&self) -> Ident { + self.last_segment().ident + } + + pub fn first_name(&self) -> &str { + assert!(!self.segments.is_empty()); + &self.segments.first().unwrap().ident.0.contents + } + + pub fn last_name(&self) -> &str { + assert!(!self.segments.is_empty()); + &self.segments.last().unwrap().ident.0.contents + } + pub fn is_ident(&self) -> bool { self.segments.len() == 1 && self.kind == PathKind::Plain } @@ -404,14 +424,14 @@ impl Path { if !self.is_ident() { return None; } - self.segments.first() + self.segments.first().map(|segment| &segment.ident) } pub fn to_ident(&self) -> Option { if !self.is_ident() { return None; } - self.segments.first().cloned() + self.segments.first().cloned().map(|segment| segment.ident) } pub fn as_string(&self) -> String { @@ -421,19 +441,58 @@ impl Path { match segments.next() { None => panic!("empty segment"), Some(seg) => { - string.push_str(&seg.0.contents); + string.push_str(&seg.ident.0.contents); } } for segment in segments { string.push_str("::"); - string.push_str(&segment.0.contents); + string.push_str(&segment.ident.0.contents); } string } } +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub struct PathSegment { + pub ident: Ident, + pub generics: Option>, + pub span: Span, +} + +impl PathSegment { + /// Returns the span where turbofish happen. For example: + /// + /// foo:: + /// ~^^^^ + /// + /// Returns an empty span at the end of `foo` if there's no turbofish. + pub fn turbofish_span(&self) -> Span { + Span::from(self.ident.span().end()..self.span.end()) + } +} + +impl From for PathSegment { + fn from(ident: Ident) -> PathSegment { + let span = ident.span(); + PathSegment { ident, generics: None, span } + } +} + +impl Display for PathSegment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.ident.fmt(f)?; + + if let Some(generics) = &self.generics { + let generics = vecmap(generics, ToString::to_string); + write!(f, "::<{}>", generics.join(", "))?; + } + + Ok(()) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct LetStatement { pub pattern: Pattern, @@ -517,7 +576,7 @@ impl Recoverable for Pattern { impl LValue { fn as_expression(&self) -> Expression { let kind = match self { - LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone()), None), + LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), LValue::MemberAccess { object, field_name, span: _ } => { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { lhs: object.as_expression(), @@ -606,11 +665,12 @@ impl ForRange { }; // array.len() - let segments = vec![array_ident]; - let array_ident = ExpressionKind::Variable( - Path { segments, kind: PathKind::Plain, span: array_span }, - None, - ); + let segments = vec![PathSegment::from(array_ident)]; + let array_ident = ExpressionKind::Variable(Path { + segments, + kind: PathKind::Plain, + span: array_span, + }); let end_range = ExpressionKind::MethodCall(Box::new(MethodCallExpression { object: Expression::new(array_ident.clone(), array_span), @@ -626,11 +686,12 @@ impl ForRange { let fresh_identifier = Ident::new(index_name.clone(), array_span); // array[i] - let segments = vec![Ident::new(index_name, array_span)]; - let index_ident = ExpressionKind::Variable( - Path { segments, kind: PathKind::Plain, span: array_span }, - None, - ); + let segments = vec![PathSegment::from(Ident::new(index_name, array_span))]; + let index_ident = ExpressionKind::Variable(Path { + segments, + kind: PathKind::Plain, + span: array_span, + }); let loop_element = ExpressionKind::Index(Box::new(IndexExpression { collection: Expression::new(array_ident, array_span), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs index 112747e09fba..732cbee92328 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -14,7 +14,6 @@ pub struct NoirStruct { pub generics: UnresolvedGenerics, pub fields: Vec<(Ident, UnresolvedType)>, pub span: Span, - pub is_comptime: bool, } impl Display for NoirStruct { 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 b23fbaede61e..f8f8ef667b42 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -53,7 +53,6 @@ pub struct TypeImpl { pub generics: UnresolvedGenerics, pub where_clause: Vec, pub methods: Vec<(NoirFunction, Span)>, - pub is_comptime: bool, } /// Ast node for an implementation of a trait for a particular type @@ -70,8 +69,6 @@ pub struct NoirTraitImpl { pub where_clause: Vec, pub items: Vec, - - pub is_comptime: bool, } /// Represents a simple trait constraint such as `where Foo: TraitY` diff --git a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs index 443267380b54..598ffed14335 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -1,3 +1,4 @@ +use crate::ast::PathSegment; use crate::parser::{parse_program, ParsedModule}; use crate::{ ast, @@ -171,14 +172,11 @@ impl DebugInstrumenter { let last_stmt = if has_ret_expr { ast::Statement { kind: ast::StatementKind::Expression(ast::Expression { - kind: ast::ExpressionKind::Variable( - ast::Path { - segments: vec![ident("__debug_expr", span)], - kind: PathKind::Plain, - span, - }, - None, - ), + kind: ast::ExpressionKind::Variable(ast::Path { + segments: vec![PathSegment::from(ident("__debug_expr", span))], + kind: PathKind::Plain, + span, + }), span, }), span, @@ -571,14 +569,11 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta let span = expr.span; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable( - ast::Path { - segments: vec![ident("__debug_var_assign", span)], - kind: PathKind::Plain, - span, - }, - None, - ), + kind: ast::ExpressionKind::Variable(ast::Path { + segments: vec![PathSegment::from(ident("__debug_var_assign", span))], + kind: PathKind::Plain, + span, + }), span, }), is_macro_call: false, @@ -590,14 +585,11 @@ fn build_assign_var_stmt(var_id: SourceVarId, expr: ast::Expression) -> ast::Sta fn build_drop_var_stmt(var_id: SourceVarId, span: Span) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable( - ast::Path { - segments: vec![ident("__debug_var_drop", span)], - kind: PathKind::Plain, - span, - }, - None, - ), + kind: ast::ExpressionKind::Variable(ast::Path { + segments: vec![PathSegment::from(ident("__debug_var_drop", span))], + kind: PathKind::Plain, + span, + }), span, }), is_macro_call: false, @@ -618,14 +610,14 @@ fn build_assign_member_stmt( let span = expr.span; let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable( - ast::Path { - segments: vec![ident(&format!["__debug_member_assign_{arity}"], span)], - kind: PathKind::Plain, + kind: ast::ExpressionKind::Variable(ast::Path { + segments: vec![PathSegment::from(ident( + &format!["__debug_member_assign_{arity}"], span, - }, - None, - ), + ))], + kind: PathKind::Plain, + span, + }), span, }), is_macro_call: false, @@ -642,14 +634,11 @@ fn build_assign_member_stmt( fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement { let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { func: Box::new(ast::Expression { - kind: ast::ExpressionKind::Variable( - ast::Path { - segments: vec![ident(&format!["__debug_fn_{fname}"], span)], - kind: PathKind::Plain, - span, - }, - None, - ), + kind: ast::ExpressionKind::Variable(ast::Path { + segments: vec![PathSegment::from(ident(&format!["__debug_fn_{fname}"], span))], + kind: PathKind::Plain, + span, + }), span, }), is_macro_call: false, @@ -712,10 +701,11 @@ fn ident(s: &str, span: Span) -> ast::Ident { fn id_expr(id: &ast::Ident) -> ast::Expression { ast::Expression { - kind: ast::ExpressionKind::Variable( - Path { segments: vec![id.clone()], kind: PathKind::Plain, span: id.span() }, - None, - ), + kind: ast::ExpressionKind::Variable(Path { + segments: vec![PathSegment::from(id.clone())], + kind: PathKind::Plain, + span: id.span(), + }), span: id.span(), } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs index 0cbd2db55dae..402ff31dafe1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -1,9 +1,6 @@ -use std::mem::replace; - use crate::{ hir_def::expr::HirIdent, - macros_api::Expression, - node_interner::{DependencyId, ExprId, FuncId}, + node_interner::{DependencyId, FuncId}, }; use super::{Elaborator, FunctionContext, ResolverMeta}; @@ -12,43 +9,41 @@ impl<'context> Elaborator<'context> { /// Elaborate an expression from the middle of a comptime scope. /// When this happens we require additional information to know /// what variables should be in scope. - pub fn elaborate_expression_from_comptime( - &mut self, - expr: Expression, - function: Option, - ) -> ExprId { - self.function_context.push(FunctionContext::default()); - let old_scope = self.scopes.end_function(); - self.scopes.start_function(); - let function_id = function.map(DependencyId::Function); - let old_item = replace(&mut self.current_item, function_id); + pub fn elaborate_item_from_comptime<'a, T>( + &'a mut self, + current_function: Option, + f: impl FnOnce(&mut Elaborator<'a>) -> T, + ) -> T { + // Create a fresh elaborator to ensure no state is changed from + // this elaborator + let mut elaborator = Elaborator::new( + self.interner, + self.def_maps, + self.crate_id, + self.debug_comptime_in_file, + ); - // Note: recover_generics isn't good enough here because any existing generics - // should not be in scope of this new function - let old_generics = std::mem::take(&mut self.generics); + elaborator.function_context.push(FunctionContext::default()); + elaborator.scopes.start_function(); - let old_crate_and_module = function.map(|function| { - let meta = self.interner.function_meta(&function); - let old_crate = replace(&mut self.crate_id, meta.source_crate); - let old_module = replace(&mut self.local_module, meta.source_module); - self.introduce_generics_into_scope(meta.all_generics.clone()); - (old_crate, old_module) - }); + if let Some(function) = current_function { + let meta = elaborator.interner.function_meta(&function); + elaborator.current_item = Some(DependencyId::Function(function)); + elaborator.crate_id = meta.source_crate; + elaborator.local_module = meta.source_module; + elaborator.file = meta.source_file; + elaborator.introduce_generics_into_scope(meta.all_generics.clone()); + } - self.populate_scope_from_comptime_scopes(); - let expr = self.elaborate_expression(expr).0; + elaborator.comptime_scopes = std::mem::take(&mut self.comptime_scopes); + elaborator.populate_scope_from_comptime_scopes(); - if let Some((old_crate, old_module)) = old_crate_and_module { - self.crate_id = old_crate; - self.local_module = old_module; - } + let result = f(&mut elaborator); + elaborator.check_and_pop_function_context(); - self.generics = old_generics; - self.current_item = old_item; - self.scopes.end_function(); - self.scopes.0.push(old_scope); - self.check_and_pop_function_context(); - expr + self.comptime_scopes = elaborator.comptime_scopes; + self.errors.append(&mut elaborator.errors); + result } fn populate_scope_from_comptime_scopes(&mut self) { 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 853098ce9316..6e2756f03016 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -35,11 +35,11 @@ use crate::{ use super::{Elaborator, LambdaContext}; impl<'context> Elaborator<'context> { - pub(super) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) { + pub(crate) fn elaborate_expression(&mut self, expr: Expression) -> (ExprId, Type) { 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) => return self.elaborate_prefix(*prefix), + ExpressionKind::Prefix(prefix) => return self.elaborate_prefix(*prefix, expr.span), 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), @@ -50,9 +50,7 @@ impl<'context> Elaborator<'context> { ExpressionKind::Cast(cast) => self.elaborate_cast(*cast, expr.span), ExpressionKind::Infix(infix) => return self.elaborate_infix(*infix, expr.span), ExpressionKind::If(if_) => self.elaborate_if(*if_), - ExpressionKind::Variable(variable, generics) => { - return self.elaborate_variable(variable, generics) - } + ExpressionKind::Variable(variable) => return self.elaborate_variable(variable), ExpressionKind::Tuple(tuple) => self.elaborate_tuple(tuple), ExpressionKind::Lambda(lambda) => self.elaborate_lambda(*lambda), ExpressionKind::Parenthesized(expr) => return self.elaborate_expression(*expr), @@ -227,8 +225,7 @@ impl<'context> Elaborator<'context> { (HirExpression::Literal(HirLiteral::FmtStr(str, fmt_str_idents)), typ) } - fn elaborate_prefix(&mut self, prefix: PrefixExpression) -> (ExprId, Type) { - let span = prefix.rhs.span; + fn elaborate_prefix(&mut self, prefix: PrefixExpression, span: Span) -> (ExprId, Type) { let (rhs, rhs_type) = self.elaborate_expression(prefix.rhs); let trait_id = self.interner.get_prefix_operator_trait_method(&prefix.operator); @@ -352,7 +349,7 @@ impl<'context> Elaborator<'context> { &mut object, ); - self.resolve_turbofish_generics(&func_id, method_call.generics, span) + self.resolve_function_turbofish_generics(&func_id, method_call.generics, span) } else { None }; @@ -410,9 +407,12 @@ impl<'context> Elaborator<'context> { &mut self, constructor: ConstructorExpression, ) -> (HirExpression, Type) { + let exclude_last_segment = true; + self.check_unsupported_turbofish_usage(&constructor.type_name, exclude_last_segment); + let span = constructor.type_name.span(); let last_segment = constructor.type_name.last_segment(); - let is_self_type = last_segment.is_self_type_name(); + let is_self_type = last_segment.ident.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); @@ -429,6 +429,15 @@ impl<'context> Elaborator<'context> { } }; + let turbofish_span = last_segment.turbofish_span(); + + let struct_generics = self.resolve_struct_turbofish_generics( + &r#type.borrow(), + struct_generics, + last_segment.generics, + turbofish_span, + ); + let struct_type = r#type.clone(); let generics = struct_generics.clone(); @@ -443,7 +452,7 @@ impl<'context> Elaborator<'context> { }); let struct_id = struct_type.borrow().id; - let reference_location = Location::new(last_segment.span(), self.file); + let reference_location = Location::new(last_segment.ident.span(), self.file); self.interner.add_struct_reference(struct_id, reference_location, is_self_type); (expr, Type::Struct(struct_type, generics)) @@ -544,7 +553,7 @@ impl<'context> Elaborator<'context> { fn elaborate_cast(&mut self, cast: CastExpression, span: Span) -> (HirExpression, Type) { let (lhs, lhs_type) = self.elaborate_expression(cast.lhs); let r#type = self.resolve_type(cast.r#type); - let result = self.check_cast(lhs_type, &r#type, span); + let result = self.check_cast(&lhs_type, &r#type, span); let expr = HirExpression::Cast(HirCastExpression { lhs, r#type }); (expr, result) } 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 e0affad1fbf9..490e1b9c3c4e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -11,10 +11,11 @@ use crate::{ def_collector::{ dc_crate::{ filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal, - UnresolvedStruct, UnresolvedTypeAlias, + UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias, }, dc_mod, }, + def_map::DefMaps, resolution::{errors::ResolverError, path_resolver::PathResolver}, scope::ScopeForest as GenericScopeForest, type_check::TypeCheckError, @@ -54,7 +55,7 @@ use crate::{ use crate::{ hir::{ def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, - def_map::{CrateDefMap, ModuleData}, + def_map::ModuleData, }, hir_def::traits::TraitImpl, macros_api::ItemVisibility, @@ -102,7 +103,7 @@ pub struct Elaborator<'context> { pub(crate) interner: &'context mut NodeInterner, - def_maps: &'context mut BTreeMap, + def_maps: &'context mut DefMaps, file: FileId, @@ -130,8 +131,6 @@ pub struct Elaborator<'context> { /// to the corresponding trait impl ID. current_trait_impl: Option, - trait_id: Option, - /// In-resolution names /// /// This needs to be a set because we can have multiple in-resolution @@ -195,22 +194,22 @@ struct FunctionContext { impl<'context> Elaborator<'context> { pub fn new( - context: &'context mut Context, + interner: &'context mut NodeInterner, + def_maps: &'context mut DefMaps, crate_id: CrateId, debug_comptime_in_file: Option, ) -> Self { Self { scopes: ScopeForest::default(), errors: Vec::new(), - interner: &mut context.def_interner, - def_maps: &mut context.def_maps, + interner, + def_maps, file: FileId::dummy(), nested_loops: 0, generics: Vec::new(), lambda_stack: Vec::new(), self_type: None, current_item: None, - trait_id: None, local_module: LocalModuleId::dummy_id(), crate_id, resolving_ids: BTreeSet::new(), @@ -223,6 +222,19 @@ impl<'context> Elaborator<'context> { } } + pub fn from_context( + context: &'context mut Context, + crate_id: CrateId, + debug_comptime_in_file: Option, + ) -> Self { + Self::new( + &mut context.def_interner, + &mut context.def_maps, + crate_id, + debug_comptime_in_file, + ) + } + pub fn elaborate( context: &'context mut Context, crate_id: CrateId, @@ -238,16 +250,9 @@ impl<'context> Elaborator<'context> { items: CollectedItems, debug_comptime_in_file: Option, ) -> Self { - 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 - // in the same crate, but can refer to any item in dependencies. Trying to - // run these at the same time as other items would lead to them seeing empty - // function bodies from functions that have yet to be elaborated. - let (comptime_items, runtime_items) = Self::filter_comptime_items(items); - this.elaborate_items(comptime_items); - this.elaborate_items(runtime_items); + let mut this = Self::from_context(context, crate_id, debug_comptime_in_file); + this.elaborate_items(items); + this.check_and_pop_function_context(); this } @@ -271,11 +276,11 @@ impl<'context> Elaborator<'context> { } // Must resolve structs before we resolve globals. - let mut generated_items = self.collect_struct_definitions(items.types); + 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, &mut generated_items); + self.collect_traits(&items.traits); // 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 @@ -299,7 +304,7 @@ impl<'context> Elaborator<'context> { // 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); + let generated_items = self.run_attributes(&items.traits, &items.types, &items.functions); // After everything is collected, we can elaborate our generated items. // It may be better to inline these within `items` entirely since elaborating them @@ -336,17 +341,12 @@ impl<'context> Elaborator<'context> { } fn elaborate_functions(&mut self, functions: UnresolvedFunctions) { - self.file = functions.file_id; - self.trait_id = functions.trait_id; // TODO: Resolve? - self.self_type = functions.self_type; - - for (local_module, id, _) in functions.functions { - self.local_module = local_module; - self.recover_generics(|this| this.elaborate_function(id)); + for (_, id, _) in functions.functions { + self.elaborate_function(id); } + self.generics.clear(); self.self_type = None; - self.trait_id = None; } fn introduce_generics_into_scope(&mut self, all_generics: Vec) { @@ -364,7 +364,7 @@ impl<'context> Elaborator<'context> { self.generics = all_generics; } - fn elaborate_function(&mut self, id: FuncId) { + pub(crate) fn elaborate_function(&mut self, id: FuncId) { let func_meta = self.interner.func_meta.get_mut(&id); let func_meta = func_meta.expect("FuncMetas should be declared before a function is elaborated"); @@ -377,11 +377,21 @@ impl<'context> Elaborator<'context> { FunctionBody::Resolving => return, }; + let func_meta = func_meta.clone(); + + assert_eq!( + self.crate_id, func_meta.source_crate, + "Functions in other crates should be already elaborated" + ); + + self.local_module = func_meta.source_module; + self.file = func_meta.source_file; + self.self_type = func_meta.self_type.clone(); + self.current_trait_impl = func_meta.trait_impl; + self.scopes.start_function(); let old_item = std::mem::replace(&mut self.current_item, Some(DependencyId::Function(id))); - let func_meta = func_meta.clone(); - self.trait_bounds = func_meta.trait_constraints.clone(); self.function_context.push(FunctionContext::default()); @@ -608,7 +618,11 @@ impl<'context> Elaborator<'context> { self.resolve_trait_bound(&constraint.trait_bound, typ) } - fn resolve_trait_bound(&mut self, bound: &TraitBound, typ: Type) -> Option { + pub fn resolve_trait_bound( + &mut self, + bound: &TraitBound, + typ: Type, + ) -> Option { let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?; let resolved_generics = &the_trait.generics.clone(); @@ -774,6 +788,8 @@ impl<'context> Elaborator<'context> { source_crate: self.crate_id, source_module: self.local_module, function_body: FunctionBody::Unresolved(func.kind, body, func.def.span), + self_type: self.self_type.clone(), + source_file: self.file, }; self.interner.push_fn_meta(meta, func_id); @@ -953,7 +969,16 @@ impl<'context> Elaborator<'context> { self.collect_trait_impl_methods(trait_id, trait_impl, &where_clause); - let span = trait_impl.object_type.span.expect("All trait self types should have spans"); + let span = trait_impl.object_type.span; + + let span = if let Some(span) = span { + span + } else if self.interner.is_in_lsp_mode() { + // A span might not be set if this was generated by a macro + Default::default() + } else { + span.expect("All trait self types should have spans") + }; self.declare_methods_on_struct(true, &mut trait_impl.methods, span); let methods = trait_impl.methods.function_ids(); @@ -964,7 +989,7 @@ impl<'context> Elaborator<'context> { let trait_generics = trait_impl.resolved_trait_generics.clone(); let resolved_trait_impl = Shared::new(TraitImpl { - ident: trait_impl.trait_path.last_segment().clone(), + ident: trait_impl.trait_path.last_ident(), typ: self_type.clone(), trait_id, trait_generics: trait_generics.clone(), @@ -1002,10 +1027,7 @@ impl<'context> Elaborator<'context> { self.self_type = None; } - fn get_module_mut( - def_maps: &mut BTreeMap, - module: ModuleId, - ) -> &mut ModuleData { + fn get_module_mut(def_maps: &mut DefMaps, module: ModuleId) -> &mut ModuleData { let message = "A crate should always be present for a given crate id"; &mut def_maps.get_mut(&module.krate).expect(message).modules[module.local_id.0] } @@ -1102,30 +1124,20 @@ impl<'context> Elaborator<'context> { self.generics.clear(); } - fn collect_struct_definitions( - &mut self, - structs: BTreeMap, - ) -> CollectedItems { + fn collect_struct_definitions(&mut self, structs: &BTreeMap) { // This is necessary to avoid cloning the entire struct map // when adding checks after each struct field is resolved. let struct_ids = structs.keys().copied().collect::>(); - // This will contain any additional top-level items that are generated at compile-time - // via macros. This often includes derived trait impls. - let mut generated_items = CollectedItems::default(); - // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. - for (type_id, mut typ) in structs { + for (type_id, typ) in structs { self.file = typ.file_id; self.local_module = typ.module_id; - let attributes = std::mem::take(&mut typ.struct_def.attributes); - let span = typ.struct_def.span; - - let fields = self.resolve_struct_fields(typ.struct_def, type_id); + let fields = self.resolve_struct_fields(&typ.struct_def, *type_id); let fields_len = fields.len(); - self.interner.update_struct(type_id, |struct_def| { + self.interner.update_struct(*type_id, |struct_def| { struct_def.set_fields(fields); // TODO(https://github.com/noir-lang/noir/issues/5156): Remove this with implicit numeric generics @@ -1152,12 +1164,11 @@ impl<'context> Elaborator<'context> { }); for field_index in 0..fields_len { - self.interner - .add_definition_location(ReferenceId::StructMember(type_id, field_index), None); + 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 @@ -1179,8 +1190,6 @@ impl<'context> Elaborator<'context> { } } } - - generated_items } fn run_comptime_attributes_on_item( @@ -1297,7 +1306,7 @@ impl<'context> Elaborator<'context> { pub fn resolve_struct_fields( &mut self, - unresolved: NoirStruct, + unresolved: &NoirStruct, struct_id: StructId, ) -> Vec<(Ident, Type)> { self.recover_generics(|this| { @@ -1308,7 +1317,9 @@ impl<'context> Elaborator<'context> { let struct_def = this.interner.get_struct(struct_id); this.add_existing_generics(&unresolved.generics, &struct_def.borrow().generics); - let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, this.resolve_type(typ))); + let fields = vecmap(&unresolved.fields, |(ident, typ)| { + (ident.clone(), this.resolve_type(typ.clone())) + }); this.resolving_ids.remove(&struct_id); @@ -1442,7 +1453,7 @@ impl<'context> Elaborator<'context> { self.generics.clear(); if let Some(trait_id) = trait_id { - let trait_name = trait_impl.trait_path.last_segment(); + let trait_name = trait_impl.trait_path.last_ident(); self.interner.add_trait_reference( trait_id, Location::new(trait_name.span(), trait_impl.file_id), @@ -1487,62 +1498,6 @@ impl<'context> Elaborator<'context> { }) } - /// Filters out comptime items from non-comptime items. - /// Returns a pair of (comptime items, non-comptime items) - fn filter_comptime_items(mut items: CollectedItems) -> (CollectedItems, CollectedItems) { - let mut function_sets = Vec::with_capacity(items.functions.len()); - let mut comptime_function_sets = Vec::new(); - - for function_set in items.functions { - let mut functions = Vec::with_capacity(function_set.functions.len()); - let mut comptime_functions = Vec::new(); - - for function in function_set.functions { - if function.2.def.is_comptime { - comptime_functions.push(function); - } else { - functions.push(function); - } - } - - let file_id = function_set.file_id; - let self_type = function_set.self_type; - let trait_id = function_set.trait_id; - - if !comptime_functions.is_empty() { - comptime_function_sets.push(UnresolvedFunctions { - functions: comptime_functions, - file_id, - trait_id, - self_type: self_type.clone(), - }); - } - - function_sets.push(UnresolvedFunctions { functions, file_id, trait_id, self_type }); - } - - let (comptime_trait_impls, trait_impls) = - items.trait_impls.into_iter().partition(|trait_impl| trait_impl.is_comptime); - - let (comptime_structs, structs) = - items.types.into_iter().partition(|typ| typ.1.struct_def.is_comptime); - - let comptime = CollectedItems { - functions: comptime_function_sets, - types: comptime_structs, - type_aliases: BTreeMap::new(), - traits: BTreeMap::new(), - trait_impls: comptime_trait_impls, - globals: Vec::new(), - impls: rustc_hash::FxHashMap::default(), - }; - - items.functions = function_sets; - items.trait_impls = trait_impls; - items.types = structs; - (comptime, items) - } - fn add_items( &mut self, items: Vec, @@ -1591,7 +1546,6 @@ impl<'context> Elaborator<'context> { methods, generics: trait_impl.impl_generics, where_clause: trait_impl.where_clause, - is_comptime: trait_impl.is_comptime, // These last fields are filled in later trait_id: None, @@ -1655,6 +1609,36 @@ impl<'context> Elaborator<'context> { } } + /// Run all the attributes on each item. The ordering is unspecified to users but currently + /// we run trait attributes first to (e.g.) register derive handlers before derive is + /// called on structs. + /// Returns any new items generated by attributes. + fn run_attributes( + &mut self, + traits: &BTreeMap, + types: &BTreeMap, + functions: &[UnresolvedFunctions], + ) -> CollectedItems { + let mut generated_items = CollectedItems::default(); + + for (trait_id, trait_) in traits { + let attributes = &trait_.trait_def.attributes; + let item = Value::TraitDefinition(*trait_id); + let span = trait_.trait_def.span; + self.run_comptime_attributes_on_item(attributes, item, span, &mut generated_items); + } + + for (struct_id, struct_def) in types { + let attributes = &struct_def.struct_def.attributes; + let item = Value::StructDefinition(*struct_id); + let span = struct_def.struct_def.span; + self.run_comptime_attributes_on_item(attributes, item, span, &mut generated_items); + } + + self.run_attributes_on_functions(functions, &mut generated_items); + generated_items + } + fn run_attributes_on_functions( &mut self, function_sets: &[UnresolvedFunctions], 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 e24b6a3a067a..ade5420bce43 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/patterns.rs @@ -15,7 +15,7 @@ use crate::{ }, macros_api::{HirExpression, Ident, Path, Pattern}, node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, GlobalId, TraitImplKind}, - Shared, StructType, Type, TypeBindings, + ResolvedGeneric, Shared, StructType, Type, TypeBindings, }; use super::{Elaborator, ResolverMeta}; @@ -157,8 +157,12 @@ impl<'context> Elaborator<'context> { mutable: Option, new_definitions: &mut Vec, ) -> HirPattern { - let name_span = name.last_segment().span(); - let is_self_type = name.last_segment().is_self_type_name(); + let exclude_last_segment = true; + self.check_unsupported_turbofish_usage(&name, exclude_last_segment); + + let last_segment = name.last_segment(); + let name_span = last_segment.ident.span(); + let is_self_type = last_segment.ident.is_self_type_name(); let error_identifier = |this: &mut Self| { // Must create a name here to return a HirPattern::Identifier. Allowing @@ -178,6 +182,15 @@ impl<'context> Elaborator<'context> { } }; + let turbofish_span = last_segment.turbofish_span(); + + let generics = self.resolve_struct_turbofish_generics( + &struct_type.borrow(), + generics, + last_segment.generics, + turbofish_span, + ); + let actual_type = Type::Struct(struct_type.clone(), generics); let location = Location::new(span, self.file); @@ -404,7 +417,7 @@ impl<'context> Elaborator<'context> { } /// Resolve generics using the expected kinds of the function we are calling - pub(super) fn resolve_turbofish_generics( + pub(super) fn resolve_function_turbofish_generics( &mut self, func_id: &FuncId, unresolved_turbofish: Option>, @@ -412,28 +425,61 @@ impl<'context> Elaborator<'context> { ) -> Option> { let direct_generics = self.interner.function_meta(func_id).direct_generics.clone(); - unresolved_turbofish.map(|option_inner| { - if option_inner.len() != direct_generics.len() { + unresolved_turbofish.map(|unresolved_turbofish| { + if unresolved_turbofish.len() != direct_generics.len() { let type_check_err = TypeCheckError::IncorrectTurbofishGenericCount { expected_count: direct_generics.len(), - actual_count: option_inner.len(), + actual_count: unresolved_turbofish.len(), span, }; self.push_err(type_check_err); } - let generics_with_types = direct_generics.iter().zip(option_inner); - vecmap(generics_with_types, |(generic, unresolved_type)| { - self.resolve_type_inner(unresolved_type, &generic.kind) - }) + self.resolve_turbofish_generics(&direct_generics, unresolved_turbofish) }) } - pub(super) fn elaborate_variable( + pub(super) fn resolve_struct_turbofish_generics( &mut self, - variable: Path, + struct_type: &StructType, + generics: Vec, unresolved_turbofish: Option>, - ) -> (ExprId, Type) { + span: Span, + ) -> Vec { + let Some(turbofish_generics) = unresolved_turbofish else { + return generics; + }; + + if turbofish_generics.len() != generics.len() { + self.push_err(TypeCheckError::GenericCountMismatch { + item: format!("struct {}", struct_type.name), + expected: generics.len(), + found: turbofish_generics.len(), + span, + }); + return generics; + } + + self.resolve_turbofish_generics(&struct_type.generics, turbofish_generics) + } + + pub(super) fn resolve_turbofish_generics( + &mut self, + generics: &[ResolvedGeneric], + turbofish_generics: Vec, + ) -> Vec { + let generics_with_types = generics.iter().zip(turbofish_generics); + vecmap(generics_with_types, |(generic, unresolved_type)| { + self.resolve_type_inner(unresolved_type, &generic.kind) + }) + } + + pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) { + let exclude_last_segment = true; + self.check_unsupported_turbofish_usage(&variable, exclude_last_segment); + + let unresolved_turbofish = variable.segments.last().unwrap().generics.clone(); + let span = variable.span; let expr = self.resolve_variable(variable); let definition_id = expr.id; @@ -445,7 +491,7 @@ impl<'context> Elaborator<'context> { // and if the turbofish operator was used. let generics = definition_kind.and_then(|definition_kind| match &definition_kind { DefinitionKind::Function(function) => { - self.resolve_turbofish_generics(function, unresolved_turbofish, span) + self.resolve_function_turbofish_generics(function, unresolved_turbofish, span) } _ => None, }); @@ -648,7 +694,7 @@ impl<'context> Elaborator<'context> { } pub fn get_ident_from_path(&mut self, path: Path) -> (HirIdent, usize) { - let location = Location::new(path.last_segment().span(), self.file); + let location = Location::new(path.last_ident().span(), self.file); let error = match path.as_ident().map(|ident| self.use_variable(ident)) { Some(Ok(found)) => return found, 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 23638b03cf54..ae9a2c75ab6c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/scope.rs @@ -1,6 +1,6 @@ use noirc_errors::{Location, Spanned}; -use crate::ast::ERROR_IDENT; +use crate::ast::{PathKind, ERROR_IDENT}; use crate::hir::def_map::{LocalModuleId, ModuleId}; use crate::hir::resolution::path_resolver::{PathResolver, StandardPathResolver}; use crate::hir::scope::{Scope as GenericScope, ScopeTree as GenericScopeTree}; @@ -43,11 +43,38 @@ impl<'context> Elaborator<'context> { } pub(super) fn resolve_path(&mut self, path: Path) -> Result { - let resolver = StandardPathResolver::new(self.module_id()); + let mut module_id = self.module_id(); + let mut path = path; + + if path.kind == PathKind::Plain && path.first_name() == SELF_TYPE_NAME { + if let Some(Type::Struct(struct_type, _)) = &self.self_type { + let struct_type = struct_type.borrow(); + if path.segments.len() == 1 { + return Ok(ModuleDefId::TypeId(struct_type.id)); + } + + module_id = struct_type.id.module_id(); + path = Path { + segments: path.segments[1..].to_vec(), + kind: PathKind::Plain, + span: path.span(), + }; + } + } + + self.resolve_path_in_module(path, module_id) + } + + fn resolve_path_in_module( + &mut self, + path: Path, + module_id: ModuleId, + ) -> Result { + let resolver = StandardPathResolver::new(module_id); let path_resolution; - if self.interner.track_references { - let last_segment = path.last_segment(); + if self.interner.lsp_mode { + let last_segment = path.last_ident(); let location = Location::new(last_segment.span(), self.file); let is_self_type_name = last_segment.is_self_type_name(); @@ -55,14 +82,14 @@ impl<'context> Elaborator<'context> { path_resolution = resolver.resolve(self.def_maps, path.clone(), &mut Some(&mut references))?; - for (referenced, ident) in references.iter().zip(path.segments) { + for (referenced, segment) in references.iter().zip(path.segments) { let Some(referenced) = referenced else { continue; }; self.interner.add_reference( *referenced, - Location::new(ident.span(), self.file), - ident.is_self_type_name(), + Location::new(segment.ident.span(), self.file), + segment.ident.is_self_type_name(), ); } 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 a00e770218ed..1e48fdd07e7c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -8,9 +8,7 @@ use crate::{ FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, }, hir::{ - def_collector::dc_crate::{ - CollectedItems, CompilationError, UnresolvedTrait, UnresolvedTraitImpl, - }, + def_collector::dc_crate::{CompilationError, UnresolvedTrait, UnresolvedTraitImpl}, type_check::TypeCheckError, }, hir_def::{ @@ -29,14 +27,10 @@ use crate::{ use super::Elaborator; impl<'context> Elaborator<'context> { - pub fn collect_traits( - &mut self, - traits: BTreeMap, - generated_items: &mut CollectedItems, - ) { + pub fn collect_traits(&mut self, traits: &BTreeMap) { for (trait_id, unresolved_trait) in traits { self.recover_generics(|this| { - let resolved_generics = this.interner.get_trait(trait_id).generics.clone(); + let resolved_generics = this.interner.get_trait(*trait_id).generics.clone(); this.add_existing_generics( &unresolved_trait.trait_def.generics, &resolved_generics, @@ -44,28 +38,23 @@ impl<'context> Elaborator<'context> { // Resolve order // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) - let _ = this.resolve_trait_types(&unresolved_trait); + let _ = this.resolve_trait_types(unresolved_trait); // 2. Trait Constants ( Trait's methods can use trait types & constants, therefore they should be after) - let _ = this.resolve_trait_constants(&unresolved_trait); + let _ = this.resolve_trait_constants(unresolved_trait); // 3. Trait Methods - let methods = this.resolve_trait_methods(trait_id, &unresolved_trait); + let methods = this.resolve_trait_methods(*trait_id, unresolved_trait); - this.interner.update_trait(trait_id, |trait_def| { + 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_infix_operator_trait(trait_id); - self.interner.try_add_prefix_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 d5dbb170843d..0973e592c1ef 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs @@ -41,7 +41,7 @@ pub const WILDCARD_TYPE: &str = "_"; impl<'context> Elaborator<'context> { /// Translates an UnresolvedType to a Type with a `TypeKind::Normal` - pub(super) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { + pub(crate) fn resolve_type(&mut self, typ: UnresolvedType) -> Type { let span = typ.span; let resolved_type = self.resolve_type_inner(typ, &Kind::Normal); if resolved_type.is_nested_slice() { @@ -61,8 +61,8 @@ impl<'context> Elaborator<'context> { 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(), + Some(named_path.last_ident().span()), + named_path.last_ident().is_self_type_name(), synthetic, ) } else { @@ -221,7 +221,7 @@ impl<'context> Elaborator<'context> { // Check if the path is a type variable first. We currently disallow generics on type // variables since we do not support higher-kinded types. if path.segments.len() == 1 { - let name = &path.last_segment().0.contents; + let name = path.last_name(); if name == SELF_TYPE_NAME { if let Some(self_type) = self.self_type.clone() { @@ -239,6 +239,7 @@ impl<'context> Elaborator<'context> { if let Some(type_alias) = self.lookup_type_alias(path.clone()) { let type_alias = type_alias.borrow(); + let actual_generic_count = args.len(); let expected_generic_count = type_alias.generics.len(); let type_alias_string = type_alias.to_string(); let id = type_alias.id; @@ -247,9 +248,13 @@ impl<'context> Elaborator<'context> { self.resolve_type_inner(arg, &generic.kind) }); - self.verify_generics_count(expected_generic_count, &mut args, span, || { - type_alias_string - }); + self.verify_generics_count( + expected_generic_count, + actual_generic_count, + &mut args, + span, + || type_alias_string, + ); if let Some(item) = self.current_item { self.interner.add_type_alias_dependency(item, id); @@ -279,6 +284,8 @@ impl<'context> Elaborator<'context> { } let expected_generic_count = struct_type.borrow().generics.len(); + let actual_generic_count = args.len(); + if !self.in_contract() && self .interner @@ -296,9 +303,13 @@ impl<'context> Elaborator<'context> { self.resolve_type_inner(arg, &generic.kind) }); - self.verify_generics_count(expected_generic_count, &mut args, span, || { - struct_type.borrow().to_string() - }); + self.verify_generics_count( + expected_generic_count, + actual_generic_count, + &mut args, + span, + || struct_type.borrow().to_string(), + ); if let Some(current_item) = self.current_item { let dependency_id = struct_type.borrow().id; @@ -333,15 +344,16 @@ impl<'context> Elaborator<'context> { fn verify_generics_count( &mut self, expected_count: usize, + actual_count: usize, args: &mut Vec, span: Span, type_name: impl FnOnce() -> String, ) { - if args.len() != expected_count { + if actual_count != expected_count { self.push_err(ResolverError::IncorrectGenericCount { span, item_name: type_name(), - actual: args.len(), + actual: actual_count, expected: expected_count, }); @@ -352,7 +364,7 @@ impl<'context> Elaborator<'context> { pub fn lookup_generic_or_global_type(&mut self, path: &Path) -> Option { if path.segments.len() == 1 { - let name = &path.last_segment().0.contents; + let name = path.last_name(); if let Some(generic) = self.find_generic(name) { let generic = generic.clone(); return Some(Type::NamedGeneric(generic.type_var, generic.name, generic.kind)); @@ -412,11 +424,12 @@ impl<'context> Elaborator<'context> { &mut self, path: &Path, ) -> Option<(TraitMethodId, TraitConstraint, bool)> { - let trait_id = self.trait_id?; + let trait_impl = self.current_trait_impl?; + let trait_id = self.interner.try_get_trait_implementation(trait_impl)?.borrow().trait_id; if path.kind == PathKind::Plain && path.segments.len() == 2 { - let name = &path.segments[0].0.contents; - let method = &path.segments[1]; + let name = &path.segments[0].ident.0.contents; + let method = &path.segments[1].ident; if name == SELF_TYPE_NAME { let the_trait = self.interner.get_trait(trait_id); @@ -449,7 +462,7 @@ impl<'context> Elaborator<'context> { let meta = self.interner.function_meta(&func_id); let trait_id = meta.trait_id?; let the_trait = self.interner.get_trait(trait_id); - let method = the_trait.find_method(&path.last_segment().0.contents)?; + let method = the_trait.find_method(path.last_name())?; let constraint = TraitConstraint { typ: Type::TypeVariable(the_trait.self_type_typevar.clone(), TypeVariableKind::Normal), trait_generics: Type::from_generics(&vecmap(&the_trait.generics, |generic| { @@ -477,14 +490,12 @@ impl<'context> Elaborator<'context> { for constraint in self.trait_bounds.clone() { if let Type::NamedGeneric(_, name, _) = &constraint.typ { // if `path` is `T::method_name`, we're looking for constraint of the form `T: SomeTrait` - if path.segments[0].0.contents != name.as_str() { + if path.segments[0].ident.0.contents != name.as_str() { continue; } let the_trait = self.interner.get_trait(constraint.trait_id); - if let Some(method) = - the_trait.find_method(path.segments.last().unwrap().0.contents.as_str()) - { + if let Some(method) = the_trait.find_method(path.last_name()) { return Some((method, constraint, true)); } } @@ -768,7 +779,7 @@ impl<'context> Elaborator<'context> { } } - pub(super) fn check_cast(&mut self, from: Type, to: &Type, span: Span) -> Type { + pub(super) fn check_cast(&mut self, from: &Type, to: &Type, span: Span) -> Type { match from.follow_bindings() { Type::Integer(..) | Type::FieldElement @@ -777,8 +788,13 @@ impl<'context> Elaborator<'context> { | Type::Bool => (), Type::TypeVariable(_, _) => { - self.push_err(TypeCheckError::TypeAnnotationsNeeded { span }); - return Type::Error; + // NOTE: in reality the expected type can also include bool, but for the compiler's simplicity + // we only allow integer types. If a bool is in `from` it will need an explicit type annotation. + let expected = Type::polymorphic_integer_or_field(self.interner); + self.unify(from, &expected, || TypeCheckError::InvalidCast { + from: from.clone(), + span, + }); } Type::Error => return Type::Error, from => { @@ -1211,37 +1227,7 @@ impl<'context> Elaborator<'context> { None } Type::NamedGeneric(_, _, _) => { - let func_id = match self.current_item { - Some(DependencyId::Function(id)) => id, - _ => panic!("unexpected method outside a function"), - }; - let func_meta = self.interner.function_meta(&func_id); - - for constraint in &func_meta.trait_constraints { - if *object_type == constraint.typ { - if let Some(the_trait) = self.interner.try_get_trait(constraint.trait_id) { - for (method_index, method) in the_trait.methods.iter().enumerate() { - if method.name.0.contents == method_name { - let trait_method = TraitMethodId { - trait_id: constraint.trait_id, - method_index, - }; - return Some(HirMethodReference::TraitMethodId( - trait_method, - constraint.trait_generics.clone(), - )); - } - } - } - } - } - - self.push_err(TypeCheckError::UnresolvedMethodCall { - method_name: method_name.to_string(), - object_type: object_type.clone(), - span, - }); - None + self.lookup_method_in_trait_constraints(object_type, method_name, span) } // Mutable references to another type should resolve to methods of their element type. // This may be a struct or a primitive type. @@ -1264,17 +1250,53 @@ impl<'context> Elaborator<'context> { other => match self.interner.lookup_primitive_method(&other, method_name) { Some(method_id) => Some(HirMethodReference::FuncId(method_id)), None => { - self.push_err(TypeCheckError::UnresolvedMethodCall { - method_name: method_name.to_string(), - object_type: object_type.clone(), - span, - }); - None + // It could be that this type is a composite type that is bound to a trait, + // for example `x: (T, U) ... where (T, U): SomeTrait` + // (so this case is a generalization of the NamedGeneric case) + self.lookup_method_in_trait_constraints(object_type, method_name, span) } }, } } + fn lookup_method_in_trait_constraints( + &mut self, + object_type: &Type, + method_name: &str, + span: Span, + ) -> Option { + let func_id = match self.current_item { + Some(DependencyId::Function(id)) => id, + _ => panic!("unexpected method outside a function"), + }; + let func_meta = self.interner.function_meta(&func_id); + + for constraint in &func_meta.trait_constraints { + if *object_type == constraint.typ { + if let Some(the_trait) = self.interner.try_get_trait(constraint.trait_id) { + for (method_index, method) in the_trait.methods.iter().enumerate() { + if method.name.0.contents == method_name { + let trait_method = + TraitMethodId { trait_id: constraint.trait_id, method_index }; + return Some(HirMethodReference::TraitMethodId( + trait_method, + constraint.trait_generics.clone(), + )); + } + } + } + } + } + + self.push_err(TypeCheckError::UnresolvedMethodCall { + method_name: method_name.to_string(), + object_type: object_type.clone(), + span, + }); + + None + } + pub(super) fn type_check_call( &mut self, call: &HirCallExpression, @@ -1610,6 +1632,19 @@ impl<'context> Elaborator<'context> { let context = context.expect("The function_context stack should always be non-empty"); context.trait_constraints.push((constraint, expr_id)); } + + pub fn check_unsupported_turbofish_usage(&mut self, path: &Path, exclude_last_segment: bool) { + for (index, segment) in path.segments.iter().enumerate() { + if exclude_last_segment && index == path.segments.len() - 1 { + continue; + } + + if segment.generics.is_some() { + let span = segment.turbofish_span(); + self.push_err(TypeCheckError::UnsupportedTurbofishUsage { span }); + } + } + } } /// Gives an error if a user tries to create a mutable reference diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs index ed12ba21398a..fd7e02df905f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/unquote.rs @@ -27,7 +27,7 @@ impl<'a> Elaborator<'a> { // Don't want the leading `$` anymore new_tokens.pop(); let path = Path::from_single(name, span); - let (expr_id, _) = self.elaborate_variable(path, None); + let (expr_id, _) = self.elaborate_variable(path); new_tokens.push(SpannedToken::new(Token::UnquoteMarker(expr_id), span)); } other_next => new_tokens.push(SpannedToken::new(other_next, span)), 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 b52201146dd5..137433b48efc 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 @@ -2,6 +2,7 @@ use std::fmt::Display; use std::rc::Rc; use crate::{ + ast::TraitBound, hir::{def_collector::dc_crate::CompilationError, type_check::NoMatchingImplFoundError}, parser::ParserError, token::Tokens, @@ -49,13 +50,14 @@ pub enum InterpreterError { 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 }, + ComptimeDependencyCycle { function: String, location: Location }, NoImpl { location: Location }, NoMatchingImplFound { error: NoMatchingImplFoundError, file: FileId }, ImplMethodTypeMismatch { expected: Type, actual: Type, location: Location }, BreakNotInLoop { location: Location }, ContinueNotInLoop { location: Location }, BlackBoxError(BlackBoxResolutionError, Location), + FailedToResolveTraitBound { trait_bound: TraitBound, location: Location }, Unimplemented { item: String, location: Location }, @@ -112,14 +114,15 @@ impl InterpreterError { | InterpreterError::CannotInlineMacro { location, .. } | InterpreterError::UnquoteFoundDuringEvaluation { location, .. } | InterpreterError::UnsupportedTopLevelItemUnquote { location, .. } - | InterpreterError::NonComptimeFnCallInSameCrate { location, .. } + | InterpreterError::ComptimeDependencyCycle { location, .. } | InterpreterError::Unimplemented { location, .. } | InterpreterError::NoImpl { location, .. } | InterpreterError::ImplMethodTypeMismatch { location, .. } | InterpreterError::DebugEvaluateComptime { location, .. } | InterpreterError::BlackBoxError(_, location) | InterpreterError::BreakNotInLoop { location, .. } - | InterpreterError::ContinueNotInLoop { location, .. } => *location, + | InterpreterError::ContinueNotInLoop { location, .. } + | InterpreterError::FailedToResolveTraitBound { location, .. } => *location, InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) @@ -207,7 +210,7 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { } InterpreterError::FailingConstraint { message, location } => { let (primary, secondary) = match message { - Some(msg) => (format!("{msg:?}"), "Assertion failed".into()), + Some(msg) => (format!("{msg}"), "Assertion failed".into()), None => ("Assertion failed".into(), String::new()), }; CustomDiagnostic::simple_error(primary, secondary, location.span) @@ -342,10 +345,10 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { error.add_note(format!("Unquoted item was:\n{item}")); error } - InterpreterError::NonComptimeFnCallInSameCrate { function, location } => { - let msg = format!("`{function}` cannot be called in a `comptime` context here"); + InterpreterError::ComptimeDependencyCycle { function, location } => { + let msg = format!("Comptime dependency cycle while resolving `{function}`"); let secondary = - "This function must be `comptime` or in a separate crate to be called".into(); + "This function uses comptime code internally which calls into itself".into(); CustomDiagnostic::simple_error(msg, secondary, location.span) } InterpreterError::Unimplemented { item, location } => { @@ -373,6 +376,10 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { InterpreterError::BlackBoxError(error, location) => { CustomDiagnostic::simple_error(error.to_string(), String::new(), location.span) } + InterpreterError::FailedToResolveTraitBound { trait_bound, location } => { + let msg = format!("Failed to resolve trait bound `{trait_bound}`"); + CustomDiagnostic::simple_error(msg, String::new(), location.span) + } InterpreterError::NoMatchingImplFound { error, .. } => error.into(), InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), 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 index 22763c9cb648..6328a164a2a6 100644 --- 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 @@ -5,8 +5,8 @@ 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, + MemberAccessExpression, MethodCallExpression, Path, PathSegment, Pattern, PrefixExpression, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, }; use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; use crate::hir_def::expr::{HirArrayLiteral, HirBlockExpression, HirExpression, HirIdent}; @@ -88,13 +88,19 @@ impl HirExpression { 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| { + let ident = ident.to_display_ast(interner); + let segment = PathSegment { + ident, + generics: generics.as_ref().map(|option| { option.iter().map(|generic| generic.to_display_ast()).collect() }), - ) + span, + }; + + let path = + Path { segments: vec![segment], kind: crate::ast::PathKind::Plain, span }; + + ExpressionKind::Variable(path) } HirExpression::Literal(HirLiteral::Array(array)) => { let array = array.to_display_ast(interner, span); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 2090310585ce..a3d37fd76fc6 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,3 +1,4 @@ +use std::collections::VecDeque; use std::{collections::hash_map::Entry, rc::Rc}; use acvm::{acir::AcirField, FieldElement}; @@ -10,12 +11,14 @@ use crate::ast::{BinaryOpKind, FunctionKind, IntegerBitSize, Signedness}; use crate::elaborator::Elaborator; use crate::graph::CrateId; use crate::hir_def::expr::ImplKind; +use crate::hir_def::function::FunctionBody; use crate::macros_api::UnaryOp; use crate::monomorphization::{ perform_impl_bindings, perform_instantiation_bindings, resolve_trait_method, undo_instantiation_bindings, }; use crate::token::Tokens; +use crate::TypeVariable; use crate::{ hir_def::{ expr::{ @@ -51,6 +54,12 @@ pub struct Interpreter<'local, 'interner> { in_loop: bool, current_function: Option, + + /// Maps each bound generic to each binding it has in the current callstack. + /// Since the interpreter monomorphizes as it interprets, we can bind over the same generic + /// multiple times. Without this map, when one of these inner functions exits we would + /// unbind the generic completely instead of resetting it to its previous binding. + bound_generics: Vec>, } #[allow(unused)] @@ -60,28 +69,41 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { crate_id: CrateId, current_function: Option, ) -> Self { - Self { elaborator, crate_id, current_function, in_loop: false } + let bound_generics = Vec::new(); + Self { elaborator, crate_id, current_function, bound_generics, in_loop: false } } pub(crate) fn call_function( &mut self, function: FuncId, arguments: Vec<(Value, Location)>, - instantiation_bindings: TypeBindings, + mut instantiation_bindings: TypeBindings, location: Location, ) -> IResult { let trait_method = self.elaborator.interner.get_trait_method_id(function); + // To match the monomorphizer, we need to call follow_bindings on each of + // the instantiation bindings before we unbind the generics from the previous function. + // This is because the instantiation bindings refer to variables from the call site. + for (_, binding) in instantiation_bindings.values_mut() { + *binding = binding.follow_bindings(); + } + + self.unbind_generics_from_previous_function(); perform_instantiation_bindings(&instantiation_bindings); - let impl_bindings = + let mut impl_bindings = perform_impl_bindings(self.elaborator.interner, trait_method, function, location)?; - let old_function = self.current_function.replace(function); + for (_, binding) in impl_bindings.values_mut() { + *binding = binding.follow_bindings(); + } + + self.remember_bindings(&instantiation_bindings, &impl_bindings); let result = self.call_function_inner(function, arguments, location); - self.current_function = old_function; undo_instantiation_bindings(impl_bindings); undo_instantiation_bindings(instantiation_bindings); + self.rebind_generics_from_previous_function(); result } @@ -100,19 +122,27 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { }); } - let is_comptime = self.elaborator.interner.function_modifiers(&function).is_comptime; - if !is_comptime && meta.source_crate == self.crate_id { - // Calling non-comptime functions from within the current crate is restricted - // as non-comptime items will have not been elaborated yet. - let function = self.elaborator.interner.function_name(&function).to_owned(); - return Err(InterpreterError::NonComptimeFnCallInSameCrate { function, location }); - } - if meta.kind != FunctionKind::Normal { let return_type = meta.return_type().follow_bindings(); - return self.call_builtin(function, arguments, return_type, location); + return self.call_special(function, arguments, return_type, location); } + // Wait until after call_special to set the current function so that builtin functions like + // `.as_type()` still call the resolver in the caller's scope. + let old_function = self.current_function.replace(function); + let result = self.call_user_defined_function(function, arguments, location); + self.current_function = old_function; + result + } + + /// Call a non-builtin function + fn call_user_defined_function( + &mut self, + function: FuncId, + arguments: Vec<(Value, Location)>, + location: Location, + ) -> IResult { + let meta = self.elaborator.interner.function_meta(&function); let parameters = meta.parameters.0.clone(); let previous_state = self.enter_function(); @@ -120,19 +150,36 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.define_pattern(parameter, typ, argument, arg_location)?; } - let function_body = - self.elaborator.interner.function(&function).try_as_expr().ok_or_else(|| { - let function = self.elaborator.interner.function_name(&function).to_owned(); - InterpreterError::NonComptimeFnCallInSameCrate { function, location } - })?; - + let function_body = self.get_function_body(function, location)?; let result = self.evaluate(function_body)?; - self.exit_function(previous_state); Ok(result) } - fn call_builtin( + /// Try to retrieve a function's body. + /// If the function has not yet been resolved this will attempt to lazily resolve it. + /// Afterwards, if the function's body is still not known or the function is still + /// in a Resolving state we issue an error. + fn get_function_body(&mut self, function: FuncId, location: Location) -> IResult { + let meta = self.elaborator.interner.function_meta(&function); + match self.elaborator.interner.function(&function).try_as_expr() { + Some(body) => Ok(body), + None => { + if matches!(&meta.function_body, FunctionBody::Unresolved(..)) { + self.elaborator.elaborate_item_from_comptime(None, |elaborator| { + elaborator.elaborate_function(function); + }); + + self.get_function_body(function, location) + } else { + let function = self.elaborator.interner.function_name(&function).to_owned(); + Err(InterpreterError::ComptimeDependencyCycle { function, location }) + } + } + } + } + + fn call_special( &mut self, function: FuncId, arguments: Vec<(Value, Location)>, @@ -145,19 +192,16 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if let Some(builtin) = func_attrs.builtin() { let builtin = builtin.clone(); - builtin::call_builtin( - self.elaborator.interner, - &builtin, - arguments, - return_type, - location, - ) + self.call_builtin(&builtin, arguments, return_type, location) } else if let Some(foreign) = func_attrs.foreign() { let foreign = foreign.clone(); foreign::call_foreign(self.elaborator.interner, &foreign, arguments, location) } else if let Some(oracle) = func_attrs.oracle() { if oracle == "print" { self.print_oracle(arguments) + // Ignore debugger functions + } else if oracle.starts_with("__debug") { + Ok(Value::Unit) } else { let item = format!("Comptime evaluation for oracle functions like {oracle}"); Err(InterpreterError::Unimplemented { item, location }) @@ -231,6 +275,42 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.elaborator.comptime_scopes.last_mut().unwrap() } + fn unbind_generics_from_previous_function(&mut self) { + if let Some(bindings) = self.bound_generics.last() { + for var in bindings.keys() { + var.unbind(var.id()); + } + } + // Push a new bindings list for the current function + self.bound_generics.push(HashMap::default()); + } + + fn rebind_generics_from_previous_function(&mut self) { + // Remove the currently bound generics first. + self.bound_generics.pop(); + + if let Some(bindings) = self.bound_generics.last() { + for (var, binding) in bindings { + var.force_bind(binding.clone()); + } + } + } + + fn remember_bindings(&mut self, main_bindings: &TypeBindings, impl_bindings: &TypeBindings) { + let bound_generics = self + .bound_generics + .last_mut() + .expect("remember_bindings called with no bound_generics on the stack"); + + for (var, binding) in main_bindings.values() { + bound_generics.insert(var.clone(), binding.follow_bindings()); + } + + for (var, binding) in impl_bindings.values() { + bound_generics.insert(var.clone(), binding.follow_bindings()); + } + } + pub(super) fn define_pattern( &mut self, pattern: &HirPattern, @@ -449,16 +529,50 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { self.evaluate_integer(value, is_negative, id) } HirLiteral::Str(string) => Ok(Value::String(Rc::new(string))), - HirLiteral::FmtStr(_, _) => { - let item = "format strings in a comptime context".into(); - let location = self.elaborator.interner.expr_location(&id); - Err(InterpreterError::Unimplemented { item, location }) + HirLiteral::FmtStr(string, captures) => { + self.evaluate_format_string(string, captures, id) } HirLiteral::Array(array) => self.evaluate_array(array, id), HirLiteral::Slice(array) => self.evaluate_slice(array, id), } } + fn evaluate_format_string( + &mut self, + string: String, + captures: Vec, + id: ExprId, + ) -> IResult { + let mut result = String::new(); + let mut escaped = false; + let mut consuming = false; + + let mut values: VecDeque<_> = + captures.into_iter().map(|capture| self.evaluate(capture)).collect::>()?; + + for character in string.chars() { + match character { + '\\' => escaped = true, + '{' if !escaped => consuming = true, + '}' if !escaped && consuming => { + consuming = false; + + if let Some(value) = values.pop_front() { + result.push_str(&value.to_string()); + } + } + other if !consuming => { + escaped = false; + result.push(other); + } + _ => (), + } + } + + let typ = self.elaborator.interner.id_type(id); + Ok(Value::FormatString(Rc::new(result), typ)) + } + fn evaluate_integer( &self, value: FieldElement, @@ -1124,7 +1238,9 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let expr = result.into_expression(self.elaborator.interner, location)?; let expr = self .elaborator - .elaborate_expression_from_comptime(expr, self.current_function); + .elaborate_item_from_comptime(self.current_function, |elab| { + elab.elaborate_expression(expr).0 + }); result = self.evaluate(expr)?; } Ok(result) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 02c45165ee36..1904eaa02b9b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -6,54 +6,62 @@ use std::{ use acvm::{AcirField, FieldElement}; use chumsky::Parser; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::{Location, Span}; +use noirc_errors::Location; use rustc_hash::FxHashMap as HashMap; use crate::{ - ast::{IntegerBitSize, TraitBound}, + ast::IntegerBitSize, hir::comptime::{errors::IResult, InterpreterError, Value}, - macros_api::{NodeInterner, Path, Signedness, UnresolvedTypeData}, + macros_api::{NodeInterner, Signedness}, node_interner::TraitId, parser, token::{SpannedToken, Token, Tokens}, QuotedType, Shared, Type, }; -pub(super) fn call_builtin( - interner: &mut NodeInterner, - name: &str, - arguments: Vec<(Value, Location)>, - return_type: Type, - location: Location, -) -> IResult { - match name { - "array_len" => array_len(interner, arguments, location), - "as_slice" => as_slice(interner, arguments, location), - "is_unconstrained" => Ok(Value::Bool(true)), - "modulus_be_bits" => modulus_be_bits(interner, arguments, location), - "modulus_be_bytes" => modulus_be_bytes(interner, arguments, location), - "modulus_le_bits" => modulus_le_bits(interner, arguments, location), - "modulus_le_bytes" => modulus_le_bytes(interner, arguments, location), - "modulus_num_bits" => modulus_num_bits(interner, arguments, location), - "slice_insert" => slice_insert(interner, arguments, location), - "slice_pop_back" => slice_pop_back(interner, arguments, location), - "slice_pop_front" => slice_pop_front(interner, arguments, location), - "slice_push_back" => slice_push_back(interner, arguments, location), - "slice_push_front" => slice_push_front(interner, arguments, location), - "slice_remove" => slice_remove(interner, arguments, location), - "struct_def_as_type" => struct_def_as_type(interner, arguments, location), - "struct_def_fields" => struct_def_fields(interner, arguments, location), - "struct_def_generics" => struct_def_generics(interner, arguments, location), - "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), - "trait_constraint_hash" => trait_constraint_hash(interner, arguments, location), - "trait_def_as_trait_constraint" => { - trait_def_as_trait_constraint(interner, arguments, location) - } - "quoted_as_trait_constraint" => quoted_as_trait_constraint(interner, arguments, location), - "zeroed" => zeroed(return_type, location), - _ => { - let item = format!("Comptime evaluation for builtin function {name}"); - Err(InterpreterError::Unimplemented { item, location }) +use super::Interpreter; + +impl<'local, 'context> Interpreter<'local, 'context> { + pub(super) fn call_builtin( + &mut self, + name: &str, + arguments: Vec<(Value, Location)>, + return_type: Type, + location: Location, + ) -> IResult { + let interner = &mut self.elaborator.interner; + match name { + "array_len" => array_len(interner, arguments, location), + "as_slice" => as_slice(interner, arguments, location), + "is_unconstrained" => Ok(Value::Bool(true)), + "modulus_be_bits" => modulus_be_bits(interner, arguments, location), + "modulus_be_bytes" => modulus_be_bytes(interner, arguments, location), + "modulus_le_bits" => modulus_le_bits(interner, arguments, location), + "modulus_le_bytes" => modulus_le_bytes(interner, arguments, location), + "modulus_num_bits" => modulus_num_bits(interner, arguments, location), + "slice_insert" => slice_insert(interner, arguments, location), + "slice_pop_back" => slice_pop_back(interner, arguments, location), + "slice_pop_front" => slice_pop_front(interner, arguments, location), + "slice_push_back" => slice_push_back(interner, arguments, location), + "slice_push_front" => slice_push_front(interner, arguments, location), + "slice_remove" => slice_remove(interner, arguments, location), + "struct_def_as_type" => struct_def_as_type(interner, arguments, location), + "struct_def_fields" => struct_def_fields(interner, arguments, location), + "struct_def_generics" => struct_def_generics(interner, arguments, location), + "trait_constraint_eq" => trait_constraint_eq(interner, arguments, location), + "trait_constraint_hash" => trait_constraint_hash(interner, arguments, location), + "trait_def_as_trait_constraint" => { + trait_def_as_trait_constraint(interner, arguments, location) + } + "quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location), + "quoted_as_type" => quoted_as_type(self, arguments, location), + "type_eq" => type_eq(arguments, location), + "type_of" => type_of(arguments, location), + "zeroed" => zeroed(return_type), + _ => { + let item = format!("Comptime evaluation for builtin function {name}"); + Err(InterpreterError::Unimplemented { item, location }) + } } } } @@ -125,9 +133,9 @@ pub(super) fn get_u32(value: Value, location: Location) -> IResult { } } -fn get_trait_constraint(value: Value, location: Location) -> IResult { +fn get_trait_constraint(value: Value, location: Location) -> IResult<(TraitId, Vec)> { match value { - Value::TraitConstraint(bound) => Ok(bound), + Value::TraitConstraint(trait_id, generics) => Ok((trait_id, generics)), value => { let expected = Type::Quoted(QuotedType::TraitConstraint); Err(InterpreterError::TypeMismatch { expected, value, location }) @@ -203,7 +211,7 @@ fn slice_push_back( Ok(Value::Slice(values, typ)) } -/// fn as_type(self) -> Quoted +/// fn as_type(self) -> Type fn struct_def_as_type( interner: &NodeInterner, mut arguments: Vec<(Value, Location)>, @@ -211,28 +219,23 @@ fn struct_def_as_type( ) -> IResult { check_argument_count(1, &arguments, location)?; - let (struct_def, span) = match arguments.pop().unwrap() { - (Value::StructDefinition(id), location) => (id, location.span), + let struct_def = match arguments.pop().unwrap().0 { + Value::StructDefinition(id) => id, value => { let expected = Type::Quoted(QuotedType::StructDefinition); - return Err(InterpreterError::TypeMismatch { expected, location, value: value.0 }); + return Err(InterpreterError::TypeMismatch { expected, location, value }); } }; - let struct_def = interner.get_struct(struct_def); - let struct_def = struct_def.borrow(); - let make_token = |name| SpannedToken::new(Token::Ident(name), span); + let struct_def_rc = interner.get_struct(struct_def); + let struct_def = struct_def_rc.borrow(); - let mut tokens = vec![make_token(struct_def.name.to_string())]; - - for (i, generic) in struct_def.generics.iter().enumerate() { - if i != 0 { - tokens.push(SpannedToken::new(Token::Comma, span)); - } - tokens.push(make_token(generic.type_var.borrow().to_string())); - } + let generics = vecmap(&struct_def.generics, |generic| { + Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), generic.kind.clone()) + }); - Ok(Value::Code(Rc::new(Tokens(tokens)))) + drop(struct_def); + Ok(Value::Type(Type::Struct(struct_def_rc, generics))) } /// fn generics(self) -> [Quoted] @@ -263,7 +266,7 @@ fn struct_def_generics( Ok(Value::Slice(generics.collect(), typ)) } -/// fn fields(self) -> [(Quoted, Quoted)] +/// fn fields(self) -> [(Quoted, Type)] /// Returns (name, type) pairs of each field of this StructDefinition fn struct_def_fields( interner: &mut NodeInterner, @@ -290,15 +293,13 @@ fn struct_def_fields( for (name, typ) in struct_def.get_fields_as_written() { let name = make_quoted(vec![make_token(name)]); - let id = interner.push_quoted_type(typ); - let typ = SpannedToken::new(Token::QuotedType(id), span); - let typ = Value::Code(Rc::new(Tokens(vec![typ]))); + let typ = Value::Type(typ); fields.push_back(Value::Tuple(vec![name, typ])); } let typ = Type::Slice(Box::new(Type::Tuple(vec![ Type::Quoted(QuotedType::Quoted), - Type::Quoted(QuotedType::Quoted), + Type::Quoted(QuotedType::Type), ]))); Ok(Value::Slice(fields, typ)) } @@ -386,7 +387,7 @@ fn slice_insert( // fn as_trait_constraint(quoted: Quoted) -> TraitConstraint fn quoted_as_trait_constraint( - _interner: &mut NodeInterner, + interpreter: &mut Interpreter, mut arguments: Vec<(Value, Location)>, location: Location, ) -> IResult { @@ -401,7 +402,54 @@ fn quoted_as_trait_constraint( InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } })?; - Ok(Value::TraitConstraint(trait_bound)) + let bound = interpreter + .elaborator + .elaborate_item_from_comptime(interpreter.current_function, |elaborator| { + elaborator.resolve_trait_bound(&trait_bound, Type::Unit) + }) + .ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?; + + Ok(Value::TraitConstraint(bound.trait_id, bound.trait_generics)) +} + +// fn as_type(quoted: Quoted) -> Type +fn quoted_as_type( + interpreter: &mut Interpreter, + mut arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + check_argument_count(1, &arguments, location)?; + + let tokens = get_quoted(arguments.pop().unwrap().0, location)?; + let quoted = tokens.as_ref().clone(); + + let typ = parser::parse_type().parse(quoted).map_err(|mut errors| { + let error = errors.swap_remove(0); + let rule = "a type"; + InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } + })?; + + let typ = interpreter + .elaborator + .elaborate_item_from_comptime(interpreter.current_function, |elab| elab.resolve_type(typ)); + + Ok(Value::Type(typ)) +} + +fn type_eq(mut arguments: Vec<(Value, Location)>, location: Location) -> IResult { + check_argument_count(2, &arguments, location)?; + + let value1 = arguments.pop().unwrap().0; + let value2 = arguments.pop().unwrap().0; + Ok(Value::Bool(value1 == value2)) +} + +fn type_of(mut arguments: Vec<(Value, Location)>, location: Location) -> IResult { + check_argument_count(1, &arguments, location)?; + + let value = arguments.pop().unwrap().0; + let typ = value.get_type().into_owned(); + Ok(Value::Type(typ)) } // fn constraint_hash(constraint: TraitConstraint) -> Field @@ -436,12 +484,12 @@ fn trait_constraint_eq( } // fn zeroed() -> T -fn zeroed(return_type: Type, location: Location) -> IResult { +fn zeroed(return_type: Type) -> IResult { match return_type { Type::FieldElement => Ok(Value::Field(0u128.into())), Type::Array(length_type, elem) => { if let Some(length) = length_type.evaluate_to_u32() { - let element = zeroed(elem.as_ref().clone(), location)?; + let element = zeroed(elem.as_ref().clone())?; let array = std::iter::repeat(element).take(length as usize).collect(); Ok(Value::Array(array, Type::Array(length_type, elem))) } else { @@ -471,40 +519,39 @@ fn zeroed(return_type: Type, location: Location) -> IResult { Ok(Value::Zeroed(Type::String(length_type))) } } - Type::FmtString(_, _) => { - let item = "format strings in a comptime context".into(); - Err(InterpreterError::Unimplemented { item, location }) + Type::FmtString(length_type, captures) => { + let length = length_type.evaluate_to_u32(); + let typ = Type::FmtString(length_type, captures); + if let Some(length) = length { + Ok(Value::FormatString(Rc::new("\0".repeat(length as usize)), typ)) + } else { + // Assume we can resolve the length later + Ok(Value::Zeroed(typ)) + } } Type::Unit => Ok(Value::Unit), - Type::Tuple(fields) => { - Ok(Value::Tuple(try_vecmap(fields, |field| zeroed(field, location))?)) - } + Type::Tuple(fields) => Ok(Value::Tuple(try_vecmap(fields, zeroed)?)), Type::Struct(struct_type, generics) => { let fields = struct_type.borrow().get_fields(&generics); let mut values = HashMap::default(); for (field_name, field_type) in fields { - let field_value = zeroed(field_type, location)?; + let field_value = zeroed(field_type)?; values.insert(Rc::new(field_name), field_value); } let typ = Type::Struct(struct_type, generics); Ok(Value::Struct(values, typ)) } - Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), location), + Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics)), typ @ Type::Function(..) => { // Using Value::Zeroed here is probably safer than using FuncId::dummy_id() or similar Ok(Value::Zeroed(typ)) } Type::MutableReference(element) => { - let element = zeroed(*element, location)?; + let element = zeroed(*element)?; Ok(Value::Pointer(Shared::new(element), false)) } - Type::Quoted(QuotedType::TraitConstraint) => Ok(Value::TraitConstraint(TraitBound { - trait_path: Path::from_single(String::new(), Span::default()), - trait_id: None, - trait_generics: Vec::new(), - })), // Optimistically assume we can resolve this type later or that the value is unused Type::TypeVariable(_, _) | Type::Forall(_, _) @@ -589,14 +636,9 @@ fn trait_def_as_trait_constraint( let trait_id = get_trait_def(arguments.pop().unwrap().0, location)?; let the_trait = interner.get_trait(trait_id); - - let trait_path = Path::from_ident(the_trait.name.clone()); - let trait_generics = vecmap(&the_trait.generics, |generic| { - let name = Path::from_single(generic.name.as_ref().clone(), generic.span); - UnresolvedTypeData::Named(name, Vec::new(), false).with_span(generic.span) + Type::NamedGeneric(generic.type_var.clone(), generic.name.clone(), generic.kind.clone()) }); - let trait_id = Some(trait_id); - Ok(Value::TraitConstraint(TraitBound { trait_path, trait_id, trait_generics })) + Ok(Value::TraitConstraint(trait_id, trait_generics)) } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs index 94a848b891d3..c80d39f8df86 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/unquote.rs @@ -1,8 +1,8 @@ use noirc_errors::Location; use crate::{ - hir::comptime::{errors::IResult, value::unwrap_rc, Value}, - token::{SpannedToken, Token, Tokens}, + hir::comptime::errors::IResult, + token::{Token, Tokens}, }; use super::Interpreter; @@ -19,20 +19,11 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { let mut new_tokens = Vec::with_capacity(tokens.0.len()); for token in tokens.0 { - let span = token.to_span(); match token.token() { Token::UnquoteMarker(id) => { - match self.evaluate(*id)? { - // If the value is already quoted we don't want to change the token stream by - // turning it into a Quoted block (which would add `quote`, `{`, and `}` tokens). - Value::Code(stream) => new_tokens.extend(unwrap_rc(stream).0), - value => { - let new_id = - value.into_hir_expression(self.elaborator.interner, location)?; - let new_token = Token::UnquoteMarker(new_id); - new_tokens.push(SpannedToken::new(new_token, span)); - } - } + let value = self.evaluate(*id)?; + let tokens = value.into_tokens(self.elaborator.interner, location)?; + new_tokens.extend(tokens.0); } _ => new_tokens.push(token), } 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 f29b67bfc4ec..0959e4c17ac9 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 @@ -7,7 +7,7 @@ use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; use crate::{ - ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness, TraitBound}, + ast::{ArrayLiteral, ConstructorExpression, Ident, IntegerBitSize, Signedness}, hir::def_map::ModuleId, hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{ @@ -38,6 +38,7 @@ pub enum Value { U32(u32), U64(u64), String(Rc), + FormatString(Rc, Type), Function(FuncId, Type, Rc), Closure(HirLambda, Vec, Type), Tuple(Vec), @@ -47,10 +48,11 @@ pub enum Value { Slice(Vector, Type), Code(Rc), StructDefinition(StructId), - TraitConstraint(TraitBound), + TraitConstraint(TraitId, /* trait generics */ Vec), TraitDefinition(TraitId), FunctionDefinition(FuncId), ModuleDefinition(ModuleId), + Type(Type), Zeroed(Type), } @@ -73,6 +75,7 @@ impl Value { let length = Type::Constant(value.len() as u32); Type::String(Box::new(length)) } + Value::FormatString(_, typ) => return Cow::Borrowed(typ), Value::Function(_, typ, _) => return Cow::Borrowed(typ), Value::Closure(_, _, typ) => return Cow::Borrowed(typ), Value::Tuple(fields) => { @@ -95,6 +98,7 @@ impl Value { Value::TraitDefinition(_) => Type::Quoted(QuotedType::TraitDefinition), Value::FunctionDefinition(_) => Type::Quoted(QuotedType::FunctionDefinition), Value::ModuleDefinition(_) => Type::Quoted(QuotedType::Module), + Value::Type(_) => Type::Quoted(QuotedType::Type), Value::Zeroed(typ) => return Cow::Borrowed(typ), }) } @@ -148,6 +152,10 @@ impl Value { ExpressionKind::Literal(Literal::Integer((value as u128).into(), false)) } Value::String(value) => ExpressionKind::Literal(Literal::Str(unwrap_rc(value))), + // Format strings are lowered as normal strings since they are already interpolated. + Value::FormatString(value, _) => { + ExpressionKind::Literal(Literal::Str(unwrap_rc(value))) + } Value::Function(id, typ, bindings) => { let id = interner.function_definition_id(id); let impl_kind = ImplKind::NotATraitMethod; @@ -214,10 +222,11 @@ impl Value { } Value::Pointer(..) | Value::StructDefinition(_) - | Value::TraitConstraint(_) + | Value::TraitConstraint(..) | Value::TraitDefinition(_) | Value::FunctionDefinition(_) | Value::Zeroed(_) + | Value::Type(_) | Value::ModuleDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } @@ -277,6 +286,10 @@ impl Value { HirExpression::Literal(HirLiteral::Integer((value as u128).into(), false)) } Value::String(value) => HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))), + // Format strings are lowered as normal strings since they are already interpolated. + Value::FormatString(value, _) => { + HirExpression::Literal(HirLiteral::Str(unwrap_rc(value))) + } Value::Function(id, typ, bindings) => { let id = interner.function_definition_id(id); let impl_kind = ImplKind::NotATraitMethod; @@ -329,10 +342,11 @@ impl Value { Value::Code(block) => HirExpression::Unquote(unwrap_rc(block)), Value::Pointer(..) | Value::StructDefinition(_) - | Value::TraitConstraint(_) + | Value::TraitConstraint(..) | Value::TraitDefinition(_) | Value::FunctionDefinition(_) | Value::Zeroed(_) + | Value::Type(_) | Value::ModuleDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } @@ -344,6 +358,19 @@ impl Value { Ok(id) } + pub(crate) fn into_tokens( + self, + interner: &mut NodeInterner, + location: Location, + ) -> IResult { + let token = match self { + Value::Code(tokens) => return Ok(unwrap_rc(tokens)), + Value::Type(typ) => Token::QuotedType(interner.push_quoted_type(typ)), + other => Token::UnquoteMarker(other.into_hir_expression(interner, location)?), + }; + Ok(Tokens(vec![SpannedToken::new(token, location.span)])) + } + /// Converts any unsigned `Value` into a `u128`. /// Returns `None` for negative integers. pub(crate) fn to_u128(&self) -> Option { @@ -407,6 +434,7 @@ impl Display for Value { Value::U32(value) => write!(f, "{value}"), Value::U64(value) => write!(f, "{value}"), Value::String(value) => write!(f, "{value}"), + Value::FormatString(value, _) => write!(f, "{value}"), Value::Function(..) => write!(f, "(function)"), Value::Closure(_, _, _) => write!(f, "(closure)"), Value::Tuple(fields) => { @@ -443,6 +471,7 @@ impl Display for Value { Value::FunctionDefinition(_) => write!(f, "(function definition)"), Value::ModuleDefinition(_) => write!(f, "(module)"), Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), + Value::Type(typ) => write!(f, "{}", typ), } } } 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 80186c19c769..864cbef56c1d 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 @@ -19,7 +19,8 @@ use crate::node_interner::{ use crate::ast::{ ExpressionKind, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTypeAlias, Path, PathKind, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, + NoirTypeAlias, Path, PathKind, PathSegment, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, }; use crate::parser::{ParserError, SortedModule}; @@ -79,7 +80,6 @@ pub struct UnresolvedTraitImpl { pub methods: UnresolvedFunctions, pub generics: UnresolvedGenerics, pub where_clause: Vec, - pub is_comptime: bool, // Every field after this line is filled in later in the elaborator pub trait_id: Option, @@ -306,7 +306,7 @@ impl DefCollector { // Resolve unresolved imports collected from the crate, one by one. 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 resolved_import = if context.def_interner.lsp_mode { let mut references: Vec> = Vec::new(); let resolved_import = resolve_import( crate_id, @@ -318,13 +318,14 @@ impl DefCollector { let current_def_map = context.def_maps.get(&crate_id).unwrap(); let file_id = current_def_map.file_id(module_id); - for (referenced, ident) in references.iter().zip(&collected_import.path.segments) { + for (referenced, segment) in references.iter().zip(&collected_import.path.segments) + { let Some(referenced) = referenced else { continue; }; context.def_interner.add_reference( *referenced, - Location::new(ident.span(), file_id), + Location::new(segment.ident.span(), file_id), false, ); } @@ -351,7 +352,7 @@ impl DefCollector { .import(name.clone(), ns, resolved_import.is_prelude); let file_id = current_def_map.file_id(module_id); - let last_segment = collected_import.path.last_segment(); + let last_segment = collected_import.path.last_ident(); add_import_reference(ns, &last_segment, &mut context.def_interner, file_id); if let Some(ref alias) = collected_import.alias { @@ -425,7 +426,12 @@ fn inject_prelude( if !crate_id.is_stdlib() { let segments: Vec<_> = "std::prelude" .split("::") - .map(|segment| crate::ast::Ident::new(segment.into(), Span::default())) + .map(|segment| { + crate::ast::PathSegment::from(crate::ast::Ident::new( + segment.into(), + Span::default(), + )) + }) .collect(); let path = Path { @@ -446,7 +452,7 @@ fn inject_prelude( for path in prelude { let mut segments = segments.clone(); - segments.push(Ident::new(path.to_string(), Span::default())); + segments.push(PathSegment::from(Ident::new(path.to_string(), Span::default()))); collected_imports.insert( 0, 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 e5893dc43d51..9eef1d7b77e0 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 @@ -188,7 +188,6 @@ impl<'a> ModCollector<'a> { generics: trait_impl.impl_generics, where_clause: trait_impl.where_clause, trait_generics: trait_impl.trait_generics, - is_comptime: trait_impl.is_comptime, // These last fields are filled later on trait_id: None, @@ -262,7 +261,8 @@ impl<'a> ModCollector<'a> { } /// Collect any struct definitions declared within the ast. - /// Returns a vector of errors if any structs were already defined. + /// Returns a vector of errors if any structs were already defined, + /// or if a struct has duplicate fields in it. fn collect_structs( &mut self, context: &mut Context, @@ -271,6 +271,8 @@ impl<'a> ModCollector<'a> { ) -> Vec<(CompilationError, FileId)> { let mut definition_errors = vec![]; for struct_definition in types { + self.check_duplicate_field_names(&struct_definition, &mut definition_errors); + let name = struct_definition.name.clone(); let unresolved = UnresolvedStruct { @@ -330,6 +332,29 @@ impl<'a> ModCollector<'a> { definition_errors } + fn check_duplicate_field_names( + &self, + struct_definition: &NoirStruct, + definition_errors: &mut Vec<(CompilationError, FileId)>, + ) { + let mut seen_field_names = std::collections::HashSet::new(); + for (field_name, _) in &struct_definition.fields { + if seen_field_names.insert(field_name) { + continue; + } + + let previous_field_name = *seen_field_names.get(field_name).unwrap(); + definition_errors.push(( + DefCollectorErrorKind::DuplicateField { + first_def: previous_field_name.clone(), + second_def: field_name.clone(), + } + .into(), + self.file_id, + )); + } + } + /// Collect any type aliases definitions declared within the ast. /// Returns a vector of errors if any type aliases were already defined. fn collect_type_aliases( diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs index 1ccf8dd47924..9e9471c0cb35 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -26,6 +26,8 @@ pub enum DuplicateType { pub enum DefCollectorErrorKind { #[error("duplicate {typ} found in namespace")] Duplicate { typ: DuplicateType, first_def: Ident, second_def: Ident }, + #[error("duplicate struct field {first_def}")] + DuplicateField { first_def: Ident, second_def: Ident }, #[error("unresolved import")] UnresolvedModuleDecl { mod_name: Ident, expected_path: String, alternative_path: String }, #[error("overlapping imports")] @@ -132,6 +134,23 @@ impl<'a> From<&'a DefCollectorErrorKind> for Diagnostic { diag } } + DefCollectorErrorKind::DuplicateField { first_def, second_def } => { + let primary_message = format!( + "Duplicate definitions of struct field with name {} found", + &first_def.0.contents + ); + { + let first_span = first_def.0.span(); + let second_span = second_def.0.span(); + let mut diag = Diagnostic::simple_error( + primary_message, + "First definition found here".to_string(), + first_span, + ); + diag.add_secondary("Second definition found here".to_string(), second_span); + diag + } + } DefCollectorErrorKind::UnresolvedModuleDecl { mod_name, expected_path, alternative_path } => { let span = mod_name.0.span(); let mod_name = &mod_name.0.contents; 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 9de96ab06e89..45f1f17940df 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 @@ -48,11 +48,13 @@ impl ModuleId { } impl ModuleId { - pub fn module(self, def_maps: &BTreeMap) -> &ModuleData { + pub fn module(self, def_maps: &DefMaps) -> &ModuleData { &def_maps[&self.krate].modules()[self.local_id.0] } } +pub type DefMaps = BTreeMap; + /// Map of all modules and scopes defined within a crate. /// /// The definitions of the crate are accessible indirectly via the scopes of each module. diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs index 87c4133d68ef..6e91f2fdb61c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/mod.rs @@ -291,8 +291,8 @@ impl Context<'_, '_> { }) } - // Enables reference tracking (useful for tools like LSP). - pub fn track_references(&mut self) { - self.def_interner.track_references = true; + /// Activates LSP mode, which will track references for all definitions. + pub fn activate_lsp_mode(&mut self) { + self.def_interner.lsp_mode = true; } } 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 10e18248dec6..4693d3826a80 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 @@ -6,7 +6,7 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::node_interner::ReferenceId; use std::collections::BTreeMap; -use crate::ast::{Ident, ItemVisibility, Path, PathKind}; +use crate::ast::{Ident, ItemVisibility, Path, PathKind, PathSegment}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId, PerNs}; use super::errors::ResolverError; @@ -163,7 +163,8 @@ fn resolve_path_to_ns( let current_mod_id = ModuleId { krate: crate_id, local_id: import_directive.module_id }; let current_mod = &def_map.modules[current_mod_id.local_id.0]; - let first_segment = import_path.first().expect("ice: could not fetch first segment"); + let first_segment = + &import_path.first().expect("ice: could not fetch first segment").ident; if current_mod.find_name(first_segment).is_none() { // Resolve externally when first segment is unresolved return resolve_external_dep( @@ -218,7 +219,7 @@ fn resolve_path_from_crate_root( crate_id: CrateId, importing_crate: CrateId, - import_path: &[Ident], + import_path: &[PathSegment], def_maps: &BTreeMap, path_references: &mut Option<&mut Vec>>, ) -> NamespaceResolutionResult { @@ -235,7 +236,7 @@ fn resolve_path_from_crate_root( fn resolve_name_in_module( krate: CrateId, importing_crate: CrateId, - import_path: &[Ident], + import_path: &[PathSegment], starting_mod: LocalModuleId, def_maps: &BTreeMap, path_references: &mut Option<&mut Vec>>, @@ -254,7 +255,7 @@ fn resolve_name_in_module( }); } - let first_segment = import_path.first().expect("ice: could not fetch first segment"); + let first_segment = &import_path.first().expect("ice: could not fetch first segment").ident; let mut current_ns = current_mod.find_name(first_segment); if current_ns.is_none() { return Err(PathResolutionError::Unresolved(first_segment.clone())); @@ -262,6 +263,9 @@ fn resolve_name_in_module( let mut warning: Option = None; for (last_segment, current_segment) in import_path.iter().zip(import_path.iter().skip(1)) { + let last_segment = &last_segment.ident; + let current_segment = ¤t_segment.ident; + let (typ, visibility) = match current_ns.types { None => return Err(PathResolutionError::Unresolved(last_segment.clone())), Some((typ, visibility, _)) => (typ, visibility), @@ -324,7 +328,7 @@ fn resolve_name_in_module( fn resolve_path_name(import_directive: &ImportDirective) -> Ident { match &import_directive.alias { - None => import_directive.path.segments.last().unwrap().clone(), + None => import_directive.path.last_ident(), Some(ident) => ident.clone(), } } @@ -340,7 +344,7 @@ fn resolve_external_dep( let path = &directive.path.segments; // Fetch the root module from the prelude - let crate_name = path.first().unwrap(); + let crate_name = &path.first().unwrap().ident; let dep_module = current_def_map .extern_prelude .get(&crate_name.0.contents) 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 af168a10df9a..8eba8215f84e 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 @@ -152,6 +152,8 @@ pub enum TypeCheckError { StringIndexAssign { span: Span }, #[error("Macro calls may only return `Quoted` values")] MacroReturningNonExpr { typ: Type, span: Span }, + #[error("turbofish (`::<_>`) usage at this position isn't supported yet")] + UnsupportedTurbofishUsage { span: Span }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -350,6 +352,10 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic { "Macro calls must return quoted values, otherwise there is no code to insert".into(), *span, ), + TypeCheckError::UnsupportedTurbofishUsage { span } => { + let msg = "turbofish (`::<_>`) usage at this position isn't supported yet"; + Diagnostic::simple_error(msg.to_string(), "".to_string(), *span) + }, } } } 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 b9f6af0c4c3b..6b66cf1ab4a3 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 @@ -1,3 +1,4 @@ +use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Location, Span}; @@ -156,6 +157,13 @@ pub struct FuncMeta { /// The module this function was defined in pub source_module: LocalModuleId, + + /// THe file this function was defined in + pub source_file: FileId, + + /// If this function is from an impl (trait or regular impl), this + /// is the object type of the impl. Otherwise this is None. + pub self_type: Option, } #[derive(Debug, Clone)] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs index 0ba74e22781f..c437676b605d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/locations.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/locations.rs @@ -147,7 +147,7 @@ impl NodeInterner { location: Location, is_self_type: bool, ) { - if !self.track_references { + if !self.lsp_mode { return; } @@ -166,7 +166,7 @@ impl NodeInterner { referenced: ReferenceId, module_id: Option, ) { - if !self.track_references { + if !self.lsp_mode { return; } 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 a46f32e30949..e6506a5fde63 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -383,8 +383,8 @@ impl<'interner> Monomorphizer<'interner> { self.parameter(field, &typ, new_params)?; } } - HirPattern::Struct(_, fields, _) => { - let struct_field_types = unwrap_struct_type(typ); + HirPattern::Struct(_, fields, location) => { + let struct_field_types = unwrap_struct_type(typ, *location)?; assert_eq!(struct_field_types.len(), fields.len()); let mut fields = @@ -663,8 +663,10 @@ impl<'interner> Monomorphizer<'interner> { constructor: HirConstructorExpression, id: node_interner::ExprId, ) -> Result { + let location = self.interner.expr_location(&id); + let typ = self.interner.id_type(id); - let field_types = unwrap_struct_type(&typ); + let field_types = unwrap_struct_type(&typ, location)?; let field_type_map = btree_map(&field_types, |x| x.clone()); @@ -740,8 +742,8 @@ impl<'interner> Monomorphizer<'interner> { let fields = unwrap_tuple_type(typ); self.unpack_tuple_pattern(value, patterns.into_iter().zip(fields)) } - HirPattern::Struct(_, patterns, _) => { - let fields = unwrap_struct_type(typ); + HirPattern::Struct(_, patterns, location) => { + let fields = unwrap_struct_type(typ, location)?; assert_eq!(patterns.len(), fields.len()); let mut patterns = @@ -975,12 +977,24 @@ impl<'interner> Monomorphizer<'interner> { } HirType::Struct(def, args) => { + // Not all generic arguments may be used in a struct's fields so we have to check + // the arguments as well as the fields in case any need to be defaulted or are unbound. + for arg in args { + Self::check_type(arg, location)?; + } + let fields = def.borrow().get_fields(args); let fields = try_vecmap(fields, |(_, field)| Self::convert_type(&field, location))?; ast::Type::Tuple(fields) } HirType::Alias(def, args) => { + // Similar to the struct case above: generics of an alias might not end up being + // used in the type that is aliased. + for arg in args { + Self::check_type(arg, location)?; + } + Self::convert_type(&def.borrow().get_type(args), location)? } @@ -1019,6 +1033,83 @@ impl<'interner> Monomorphizer<'interner> { }) } + // Similar to `convert_type` but returns an error if any type variable can't be defaulted. + fn check_type(typ: &HirType, location: Location) -> Result<(), MonomorphizationError> { + match typ { + HirType::FieldElement + | HirType::Integer(..) + | HirType::Bool + | HirType::String(..) + | HirType::Unit + | HirType::TraitAsType(..) + | HirType::Forall(_, _) + | HirType::Constant(_) + | HirType::Error + | HirType::Quoted(_) => Ok(()), + HirType::FmtString(_size, fields) => Self::check_type(fields.as_ref(), location), + HirType::Array(_length, element) => Self::check_type(element.as_ref(), location), + HirType::Slice(element) => Self::check_type(element.as_ref(), location), + HirType::NamedGeneric(binding, _, _) => { + if let TypeBinding::Bound(binding) = &*binding.borrow() { + return Self::check_type(binding, location); + } + + Ok(()) + } + + HirType::TypeVariable(binding, kind) => { + if let TypeBinding::Bound(binding) = &*binding.borrow() { + return Self::check_type(binding, location); + } + + // Default any remaining unbound type variables. + // This should only happen if the variable in question is unused + // and within a larger generic type. + let default = match kind.default_type() { + Some(typ) => typ, + None => return Err(MonomorphizationError::TypeAnnotationsNeeded { location }), + }; + + Self::check_type(&default, location) + } + + HirType::Struct(_def, args) => { + for arg in args { + Self::check_type(arg, location)?; + } + + Ok(()) + } + + HirType::Alias(_def, args) => { + for arg in args { + Self::check_type(arg, location)?; + } + + Ok(()) + } + + HirType::Tuple(fields) => { + for field in fields { + Self::check_type(field, location)?; + } + + Ok(()) + } + + HirType::Function(args, ret, env) => { + for arg in args { + Self::check_type(arg, location)?; + } + + Self::check_type(ret, location)?; + Self::check_type(env, location) + } + + HirType::MutableReference(element) => Self::check_type(element, location), + } + } + fn is_function_closure(&self, t: ast::Type) -> bool { if self.is_function_closure_type(&t) { true @@ -1595,7 +1686,7 @@ impl<'interner> Monomorphizer<'interner> { self.create_zeroed_function(parameter_types, ret_type, env, location) } ast::Type::Slice(element_type) => { - ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { + ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents: vec![], typ: ast::Type::Slice(element_type.clone()), })) @@ -1753,9 +1844,19 @@ fn unwrap_tuple_type(typ: &HirType) -> Vec { } } -fn unwrap_struct_type(typ: &HirType) -> Vec<(String, HirType)> { +fn unwrap_struct_type( + typ: &HirType, + location: Location, +) -> Result, MonomorphizationError> { match typ.follow_bindings() { - HirType::Struct(def, args) => def.borrow().get_fields(&args), + HirType::Struct(def, args) => { + // Some of args might not be mentioned in fields, so we need to check that they aren't unbound. + for arg in &args { + Monomorphizer::check_type(arg, location)?; + } + + Ok(def.borrow().get_fields(&args)) + } other => unreachable!("unwrap_struct_type: expected struct, found {:?}", other), } } 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 87ff45f8f1a4..d5996e6a1f37 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -200,8 +200,8 @@ pub struct NodeInterner { /// the actual type since types do not implement Send or Sync. quoted_types: noirc_arena::Arena, - /// Whether to track references. In regular compilations this is false, but tools set it to true. - pub(crate) track_references: bool, + /// Determins whether to run in LSP mode. In LSP mode references are tracked. + pub(crate) lsp_mode: bool, /// Store the location of the references in the graph. /// Edges are directed from reference nodes to referenced nodes. @@ -589,7 +589,7 @@ impl Default for NodeInterner { type_alias_ref: Vec::new(), type_ref_locations: Vec::new(), quoted_types: Default::default(), - track_references: false, + lsp_mode: false, location_indices: LocationIndices::default(), reference_graph: petgraph::graph::DiGraph::new(), reference_graph_indices: HashMap::new(), @@ -1969,6 +1969,10 @@ impl NodeInterner { let env = Box::new(Type::Unit); (Type::Function(args, Box::new(ret.clone()), env), ret) } + + pub fn is_in_lsp_mode(&self) -> bool { + self.lsp_mode + } } impl Methods { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop index 5bf48a764d63..1488a53183e1 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop +++ b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop @@ -4,7 +4,7 @@ use crate::lexer::token::BorrowedToken; use crate::lexer::token as noir_token; use crate::lexer::errors::LexerErrorKind; use crate::parser::TopLevelStatement; -use crate::ast::{Ident, Path, PathKind, UseTree, UseTreeKind}; +use crate::ast::{Ident, Path, PathKind, PathSegment, UseTree, UseTreeKind}; use lalrpop_util::ErrorRecovery; @@ -110,7 +110,7 @@ pub(crate) TopLevelStatement: TopLevelStatement = { UseTree: UseTree = { // path::to::ident as SomeAlias => { - let ident = prefix.pop(); + let ident = prefix.pop().ident; let kind = UseTreeKind::Path(ident, alias); UseTree { prefix, kind } }, @@ -129,7 +129,7 @@ pub(crate) Path: Path = { Path { segments, kind, span } }, - => { + => { segments.insert(0, id); let kind = PathKind::Plain; let span = Span::from(lo as u32..hi as u32); @@ -137,12 +137,20 @@ pub(crate) Path: Path = { }, } -PathSegments: Vec = { - )*> => { +PathSegments: Vec = { + )*> => { segments } } +PathSegment: PathSegment = { + => { + let token = noir_token::Token::Ident(i.to_string()); + let span = Span::from(lo as u32..hi as u32); + PathSegment::from(Ident::from_token(token, span)) + }, +} + Alias: Ident = { r"[\t\r\n ]+" "as" r"[\t\r\n ]+" => <>, } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs index c566489eb40e..80adb01dc9a4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -46,6 +46,8 @@ pub enum ParserErrorReason { Lexer(LexerErrorKind), #[error("The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`")] ForbiddenNumericGenericType, + #[error("Invalid call data identifier, must be a number. E.g `call_data(0)`")] + InvalidCallDataIdentifier, } /// Represents a parsing error, or a parsing error in the making. diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs index c62d66769ac2..677d741b5e0d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -22,7 +22,7 @@ use chumsky::primitive::Container; pub use errors::ParserError; pub use errors::ParserErrorReason; use noirc_errors::Span; -pub use parser::{expression, parse_program, top_level_items, trait_bound}; +pub use parser::{expression, parse_program, parse_type, top_level_items, trait_bound}; #[derive(Debug, Clone)] pub enum TopLevelStatement { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs index 7f3e0e68bbc7..9772814027fc 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -24,7 +24,8 @@ //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. use self::primitives::{keyword, macro_quote_marker, mutable_reference, variable}; -use self::types::{generic_type_args, maybe_comp_time, parse_type}; +use self::types::{generic_type_args, maybe_comp_time}; +pub use types::parse_type; use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, @@ -37,7 +38,7 @@ use crate::ast::{ BinaryOp, BinaryOpKind, BlockExpression, ForLoopStatement, ForRange, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, NoirTypeAlias, Param, Path, Pattern, Recoverable, Statement, TraitBound, TypeImpl, UnaryRhsMemberAccess, UnaryRhsMethodCall, - UnresolvedTraitConstraint, UseTree, UseTreeKind, Visibility, + UseTree, UseTreeKind, Visibility, }; use crate::ast::{ Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, @@ -45,6 +46,7 @@ use crate::ast::{ use crate::lexer::{lexer::from_spanned_token_result, Lexer}; use crate::parser::{force, ignore_then_commit, statement_recovery}; use crate::token::{Keyword, Token, TokenKind}; +use acvm::AcirField; use chumsky::prelude::*; use iter_extended::vecmap; @@ -69,8 +71,9 @@ lalrpop_mod!(pub noir_parser); mod test_helpers; use literals::literal; -use path::{maybe_empty_path, path}; +use path::{maybe_empty_path, path, path_no_turbofish}; use primitives::{dereference, ident, negation, not, nothing, right_shift_operator, token_kind}; +use traits::where_clause; /// Entry function for the parser - also handles lexing internally. /// @@ -215,9 +218,8 @@ fn top_level_statement<'a>( /// /// implementation: 'impl' generics type '{' function_definition ... '}' fn implementation() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Impl)) - .then(function::generics()) + keyword(Keyword::Impl) + .ignore_then(function::generics()) .then(parse_type().map_with_span(|typ, span| (typ, span))) .then(where_clause()) .then_ignore(just(Token::LeftBrace)) @@ -225,14 +227,13 @@ fn implementation() -> impl NoirParser { .then_ignore(just(Token::RightBrace)) .map(|args| { let ((other_args, where_clause), methods) = args; - let ((is_comptime, generics), (object_type, type_span)) = other_args; + let (generics, (object_type, type_span)) = other_args; TopLevelStatement::Impl(TypeImpl { generics, object_type, type_span, where_clause, methods, - is_comptime, }) }) } @@ -365,45 +366,8 @@ fn function_declaration_parameters() -> impl NoirParser impl NoirParser> { - struct MultiTraitConstraint { - typ: UnresolvedType, - trait_bounds: Vec, - } - - let constraints = parse_type() - .then_ignore(just(Token::Colon)) - .then(trait_bounds()) - .map(|(typ, trait_bounds)| MultiTraitConstraint { typ, trait_bounds }); - - keyword(Keyword::Where) - .ignore_then(constraints.separated_by(just(Token::Comma))) - .or_not() - .map(|option| option.unwrap_or_default()) - .map(|x: Vec| { - let mut result: Vec = Vec::new(); - for constraint in x { - for bound in constraint.trait_bounds { - result.push(UnresolvedTraitConstraint { - typ: constraint.typ.clone(), - trait_bound: bound, - }); - } - } - result - }) -} - -fn trait_bounds() -> impl NoirParser> { - trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing() -} - pub fn trait_bound() -> impl NoirParser { - path().then(generic_type_args(parse_type())).map(|(trait_path, trait_generics)| TraitBound { - trait_path, - trait_generics, - trait_id: None, - }) + traits::trait_bound() } fn block_expr<'a>( @@ -467,8 +431,8 @@ fn rename() -> impl NoirParser> { fn use_tree() -> impl NoirParser { recursive(|use_tree| { - let simple = path().then(rename()).map(|(mut prefix, alias)| { - let ident = prefix.pop(); + let simple = path_no_turbofish().then(rename()).map(|(mut prefix, alias)| { + let ident = prefix.pop().ident; UseTree { prefix, kind: UseTreeKind::Path(ident, alias) } }); @@ -502,6 +466,8 @@ where assertion::assertion_eq(expr_parser.clone()), declaration(expr_parser.clone()), assignment(expr_parser.clone()), + if_statement(expr_no_constructors.clone(), statement.clone()), + block_statement(statement.clone()), for_loop(expr_no_constructors.clone(), statement.clone()), break_statement(), continue_statement(), @@ -593,7 +559,7 @@ fn pattern() -> impl NoirParser { .separated_by(just(Token::Comma)) .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - let struct_pattern = path() + let struct_pattern = path(super::parse_type()) .then(struct_pattern_fields) .map_with_span(|(typename, fields), span| Pattern::Struct(typename, fields, span)); @@ -680,19 +646,28 @@ where }) } +fn call_data() -> impl NoirParser { + keyword(Keyword::CallData).then(parenthesized(literal())).validate(|token, span, emit| { + match token { + (_, ExpressionKind::Literal(Literal::Integer(x, _))) => { + let id = x.to_u128() as u32; + Visibility::CallData(id) + } + _ => { + emit(ParserError::with_reason(ParserErrorReason::InvalidCallDataIdentifier, span)); + Visibility::CallData(0) + } + } + }) +} + fn optional_visibility() -> impl NoirParser { keyword(Keyword::Pub) - .or(keyword(Keyword::CallData)) - .or(keyword(Keyword::ReturnData)) + .map(|_| Visibility::Public) + .or(call_data()) + .or(keyword(Keyword::ReturnData).map(|_| Visibility::ReturnData)) .or_not() - .map(|opt| match opt { - Some(Token::Keyword(Keyword::Pub)) => Visibility::Public, - Some(Token::Keyword(Keyword::CallData)) | Some(Token::Keyword(Keyword::ReturnData)) => { - Visibility::DataBus - } - None => Visibility::Private, - _ => unreachable!("unexpected token found"), - }) + .map(|opt| opt.unwrap_or(Visibility::Private)) } pub fn expression() -> impl ExprParser { @@ -959,6 +934,28 @@ where }) } +fn if_statement<'a, P, S>( + expr_no_constructors: P, + statement: S, +) -> impl NoirParser + 'a +where + P: ExprParser + 'a, + S: NoirParser + 'a, +{ + if_expr(expr_no_constructors, statement).map_with_span(|expression_kind, span| { + StatementKind::Expression(Expression::new(expression_kind, span)) + }) +} + +fn block_statement<'a, S>(statement: S) -> impl NoirParser + 'a +where + S: NoirParser + 'a, +{ + block(statement).map_with_span(|block, span| { + StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) + }) +} + fn for_loop<'a, P, S>(expr_no_constructors: P, statement: S) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -1155,7 +1152,7 @@ fn constructor(expr_parser: impl ExprParser) -> impl NoirParser .allow_trailing() .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); - path().then(args).map(ExpressionKind::constructor) + path(super::parse_type()).then(args).map(ExpressionKind::constructor) } fn constructor_field

(expr_parser: P) -> impl NoirParser<(Ident, Expression)> @@ -1325,20 +1322,6 @@ mod test { fn parse_block() { parse_with(block(fresh_statement()), "{ [0,1,2,3,4] }").unwrap(); - // Regression for #1310: this should be parsed as a block and not a function call - let res = - parse_with(block(fresh_statement()), "{ if true { 1 } else { 2 } (3, 4) }").unwrap(); - match unwrap_expr(&res.statements.last().unwrap().kind) { - // The `if` followed by a tuple is currently creates a block around both in case - // there was none to start with, so there is an extra block here. - ExpressionKind::Block(block) => { - assert_eq!(block.statements.len(), 2); - assert!(matches!(unwrap_expr(&block.statements[0].kind), ExpressionKind::If(_))); - assert!(matches!(unwrap_expr(&block.statements[1].kind), ExpressionKind::Tuple(_))); - } - _ => unreachable!(), - } - parse_all_failing( block(fresh_statement()), vec![ @@ -1352,14 +1335,6 @@ mod test { ); } - /// Extract an Statement::Expression from a statement or panic - fn unwrap_expr(stmt: &StatementKind) -> &ExpressionKind { - match stmt { - StatementKind::Expression(expr) => &expr.kind, - _ => unreachable!(), - } - } - #[test] fn parse_let() { // Why is it valid to specify a let declaration as having type u8? @@ -1656,4 +1631,40 @@ mod test { let failing = vec!["quote {}}", "quote a", "quote { { { } } } }"]; parse_all_failing(quote(), failing); } + + #[test] + fn test_parses_block_statement_not_infix_expression() { + let src = r#" + { + {} + -1 + }"#; + let (block_expr, _) = parse_recover(block(fresh_statement()), src); + let block_expr = block_expr.expect("Failed to parse module"); + assert_eq!(block_expr.statements.len(), 2); + } + + #[test] + fn test_parses_if_statement_not_infix_expression() { + let src = r#" + { + if 1 { 2 } else { 3 } + -1 + }"#; + let (block_expr, _) = parse_recover(block(fresh_statement()), src); + let block_expr = block_expr.expect("Failed to parse module"); + assert_eq!(block_expr.statements.len(), 2); + } + + #[test] + fn test_parses_if_statement_followed_by_tuple_as_two_separate_statements() { + // Regression for #1310: this should not be parsed as a function call + let src = r#" + { + if 1 { 2 } else { 3 } (1, 2) + }"#; + let (block_expr, _) = parse_recover(block(fresh_statement()), src); + let block_expr = block_expr.expect("Failed to parse module"); + assert_eq!(block_expr.statements.len(), 2); + } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs:28:9 b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs:28:9 new file mode 100644 index 000000000000..47dfb32b53b3 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs:28:9 @@ -0,0 +1,45 @@ +[?1049h[?1h[?2004h[?2026$p[?u[?12h[?25h[?25l(B[38:2:235:219:178m[48:2:168:153:132m [No Name]  (B[38:2:168:153:132m(B[38:2:235:219:178m (B[38:2:80:73:69m(B[38:2:168:153:132m[48:2:80:73:69m buffers +(B[38:2:124:111:100m 1 (B[38:2:235:219:178m +(B[38:2:80:73:69m~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +~ +(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:235:219:178m[48:2:168:153:132mNORMAL(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:168:153:132m[48:2:80:73:69m  jf/quoted-as-type (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m (B[38:2:60:56:54m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m  (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:80:73:69m(B[38:2:235:219:178m[48:2:168:153:132m 100% (B[38:2:235:219:178m[48:2:168:153:132m☰ 0/1 (B[38:2:235:219:178m[48:2:168:153:132m : 1 (B[38:2:254:128:25m[48:2:168:153:132m(B[38:2:235:219:178mNVIM v0.10.0Nvim is open source and freely distributablehttps://neovim.io/#chattype :help nvim(B[38:2:80:73:69m(B[38:2:235:219:178m if you are new! type :checkhealth(B[38:2:80:73:69m(B[38:2:235:219:178m to optimize Nvimtype :q(B[38:2:80:73:69m(B[38:2:235:219:178m to exit type :help(B[38:2:80:73:69m(B[38:2:235:219:178m for help type :help news(B[38:2:80:73:69m(B[38:2:235:219:178m to see changes in v0.10Help poor children in Uganda!type :help iccf(B[38:2:80:73:69m(B[38:2:235:219:178m for information ]112[2 q]112[2 q[?1002h[?1006h(B[38:2:235:219:178m[48:2:168:153:132m [No Name]  (B[38:2:168:153:132m(B[38:2:235:219:178m (B[38:2:80:73:69m(B[38:2:168:153:132m[48:2:80:73:69m buffers +(B[38:2:124:111:100m 1 (B[38:2:235:219:178m +(B[38:2:80:73:69m~ +~ +~ (B[38:2:235:219:178mNVIM v0.10.0(B[38:2:80:73:69m +~ +~ (B[38:2:235:219:178mNvim is open source and freely distributable(B[38:2:80:73:69m +~ (B[38:2:235:219:178mhttps://neovim.io/#chat(B[38:2:80:73:69m +~ +~ (B[38:2:235:219:178mtype :help nvim(B[38:2:80:73:69m(B[38:2:235:219:178m if you are new! (B[38:2:80:73:69m +~ (B[38:2:235:219:178mtype :checkhealth(B[38:2:80:73:69m(B[38:2:235:219:178m to optimize Nvim(B[38:2:80:73:69m +~ (B[38:2:235:219:178mtype :q(B[38:2:80:73:69m(B[38:2:235:219:178m to exit (B[38:2:80:73:69m +~ (B[38:2:235:219:178mtype :help(B[38:2:80:73:69m(B[38:2:235:219:178m for help (B[38:2:80:73:69m +~ +~ (B[38:2:235:219:178mtype :help news(B[38:2:80:73:69m(B[38:2:235:219:178m to see changes in v0.10(B[38:2:80:73:69m +~ +~ (B[38:2:235:219:178mHelp poor children in Uganda!(B[38:2:80:73:69m +~ (B[38:2:235:219:178mtype :help iccf(B[38:2:80:73:69m(B[38:2:235:219:178m for information (B[38:2:80:73:69m +~ +~ +~ +~ +(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:235:219:178m[48:2:168:153:132mNORMAL(B[38:2:235:219:178m[48:2:168:153:132m (B[38:2:168:153:132m[48:2:80:73:69m  jf/quoted-as-type (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m (B[38:2:60:56:54m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:60:56:54m  (B[38:2:80:73:69m[48:2:60:56:54m(B[38:2:168:153:132m[48:2:80:73:69m(B[38:2:235:219:178m[48:2:168:153:132m 100% (B[38:2:235:219:178m[48:2:168:153:132m☰ 0/1 (B[38:2:235:219:178m[48:2:168:153:132m : 1 (B[38:2:254:128:25m[48:2:168:153:132m(B[38:2:235:219:178m[?12h[?25h[?25l[?1004h[?12h[?25h \ No newline at end of file diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs index 8957fb7c40be..140650af1a23 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,24 +1,36 @@ -use crate::ast::{Path, PathKind}; +use crate::ast::{Path, PathKind, PathSegment, UnresolvedType}; use crate::parser::NoirParser; use crate::token::{Keyword, Token}; use chumsky::prelude::*; -use super::{ident, keyword}; +use super::keyword; +use super::primitives::{path_segment, path_segment_no_turbofish}; -pub(super) fn path() -> impl NoirParser { - let idents = || ident().separated_by(just(Token::DoubleColon)).at_least(1); +pub(super) fn path<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + path_inner(path_segment(type_parser)) +} + +pub(super) fn path_no_turbofish() -> impl NoirParser { + path_inner(path_segment_no_turbofish()) +} + +fn path_inner<'a>(segment: impl NoirParser + 'a) -> impl NoirParser + 'a { + let segments = segment.separated_by(just(Token::DoubleColon)).at_least(1); let make_path = |kind| move |segments, span| Path { segments, kind, span }; let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); - let path_kind = |key, kind| prefix(key).ignore_then(idents()).map_with_span(make_path(kind)); + let path_kind = + |key, kind| prefix(key).ignore_then(segments.clone()).map_with_span(make_path(kind)); choice(( path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep), path_kind(Keyword::Super, PathKind::Super), - idents().map_with_span(make_path(PathKind::Plain)), + segments.map_with_span(make_path(PathKind::Plain)), )) } @@ -30,13 +42,16 @@ fn empty_path() -> impl NoirParser { } pub(super) fn maybe_empty_path() -> impl NoirParser { - path().or(empty_path()) + path_no_turbofish().or(empty_path()) } #[cfg(test)] mod test { use super::*; - use crate::parser::parser::test_helpers::{parse_all_failing, parse_with}; + use crate::parser::{ + parse_type, + parser::test_helpers::{parse_all_failing, parse_with}, + }; #[test] fn parse_path() { @@ -45,18 +60,17 @@ mod test { ("std::hash", vec!["std", "hash"]), ("std::hash::collections", vec!["std", "hash", "collections"]), ("foo::bar", vec!["foo", "bar"]), - ("foo::bar", vec!["foo", "bar"]), ("crate::std::hash", vec!["std", "hash"]), ]; for (src, expected_segments) in cases { - let path: Path = parse_with(path(), src).unwrap(); + let path: Path = parse_with(path(parse_type()), src).unwrap(); for (segment, expected) in path.segments.into_iter().zip(expected_segments) { - assert_eq!(segment.0.contents, expected); + assert_eq!(segment.ident.0.contents, expected); } } - parse_all_failing(path(), vec!["std::", "::std", "std::hash::", "foo::1"]); + parse_all_failing(path(parse_type()), vec!["std::", "::std", "std::hash::", "foo::1"]); } #[test] @@ -69,12 +83,12 @@ mod test { ]; for (src, expected_path_kind) in cases { - let path = parse_with(path(), src).unwrap(); + let path = parse_with(path(parse_type()), src).unwrap(); assert_eq!(path.kind, expected_path_kind); } parse_all_failing( - path(), + path(parse_type()), vec!["crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], ); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs index 88f9e591aba5..25f693bf5040 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -1,13 +1,14 @@ use chumsky::prelude::*; -use crate::ast::{ExpressionKind, Ident, UnaryOp}; +use crate::ast::{ExpressionKind, Ident, PathSegment, UnaryOp}; use crate::macros_api::UnresolvedType; use crate::{ parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, token::{Keyword, Token, TokenKind}, }; -use super::path; +use super::path::{path, path_no_turbofish}; +use super::types::required_generic_type_args; /// This parser always parses no input and fails pub(super) fn nothing() -> impl NoirParser { @@ -32,6 +33,20 @@ pub(super) fn token_kind(token_kind: TokenKind) -> impl NoirParser { }) } +pub(super) fn path_segment<'a>( + type_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + ident().then(turbofish(type_parser)).map_with_span(|(ident, generics), span| PathSegment { + ident, + generics, + span, + }) +} + +pub(super) fn path_segment_no_turbofish() -> impl NoirParser { + ident().map(PathSegment::from) +} + pub(super) fn ident() -> impl NoirParser { token_kind(TokenKind::Ident).map_with_span(Ident::from_token) } @@ -81,17 +96,15 @@ where pub(super) fn turbofish<'a>( type_parser: impl NoirParser + 'a, ) -> impl NoirParser>> + 'a { - just(Token::DoubleColon).ignore_then(super::generic_type_args(type_parser)).or_not() + just(Token::DoubleColon).ignore_then(required_generic_type_args(type_parser)).or_not() } pub(super) fn variable() -> impl NoirParser { - path() - .then(turbofish(super::parse_type())) - .map(|(path, generics)| ExpressionKind::Variable(path, generics)) + path(super::parse_type()).map(ExpressionKind::Variable) } pub(super) fn variable_no_turbofish() -> impl NoirParser { - path().map(|path| ExpressionKind::Variable(path, None)) + path_no_turbofish().map(ExpressionKind::Variable) } pub(super) fn macro_quote_marker() -> impl NoirParser { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs index 9a3adf74d7f5..58bf1693eee4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -1,7 +1,6 @@ use chumsky::prelude::*; use crate::ast::{Ident, NoirStruct, UnresolvedType}; -use crate::parser::parser::types::maybe_comp_time; use crate::{ parser::{ parser::{ @@ -29,21 +28,13 @@ pub(super) fn struct_definition() -> impl NoirParser { .or(just(Semicolon).to(Vec::new())); attributes() - .then(maybe_comp_time()) .then_ignore(keyword(Struct)) .then(ident()) .then(function::generics()) .then(fields) - .validate(|((((attributes, is_comptime), name), generics), fields), span, emit| { + .validate(|(((attributes, name), generics), fields), span, emit| { let attributes = validate_secondary_attributes(attributes, span, emit); - TopLevelStatement::Struct(NoirStruct { - name, - attributes, - generics, - fields, - span, - is_comptime, - }) + TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) }) } 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 4e4c9d5c0db1..ffcf7e07629c 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 @@ -2,7 +2,7 @@ use chumsky::prelude::*; use super::attributes::{attributes, validate_secondary_attributes}; use super::function::function_return_type; -use super::types::maybe_comp_time; +use super::path::path_no_turbofish; use super::{block, expression, fresh_statement, function, function_declaration_parameters}; use crate::ast::{ @@ -17,7 +17,7 @@ use crate::{ token::{Keyword, Token}, }; -use super::{generic_type_args, parse_type, path, primitives::ident}; +use super::{generic_type_args, parse_type, primitives::ident}; pub(super) fn trait_definition() -> impl NoirParser { attributes() @@ -104,10 +104,9 @@ fn trait_type_declaration() -> impl NoirParser { /// /// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' pub(super) fn trait_implementation() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Impl)) - .then(function::generics()) - .then(path()) + keyword(Keyword::Impl) + .ignore_then(function::generics()) + .then(path_no_turbofish()) .then(generic_type_args(parse_type())) .then_ignore(keyword(Keyword::For)) .then(parse_type()) @@ -117,7 +116,7 @@ pub(super) fn trait_implementation() -> impl NoirParser { .then_ignore(just(Token::RightBrace)) .map(|args| { let (((other_args, object_type), where_clause), items) = args; - let (((is_comptime, impl_generics), trait_name), trait_generics) = other_args; + let ((impl_generics, trait_name), trait_generics) = other_args; TopLevelStatement::TraitImpl(NoirTraitImpl { impl_generics, @@ -126,7 +125,6 @@ pub(super) fn trait_implementation() -> impl NoirParser { object_type, items, where_clause, - is_comptime, }) }) } @@ -151,7 +149,7 @@ fn trait_implementation_body() -> impl NoirParser> { function.or(alias).repeated() } -fn where_clause() -> impl NoirParser> { +pub(super) fn where_clause() -> impl NoirParser> { struct MultiTraitConstraint { typ: UnresolvedType, trait_bounds: Vec, @@ -163,7 +161,7 @@ fn where_clause() -> impl NoirParser> { .map(|(typ, trait_bounds)| MultiTraitConstraint { typ, trait_bounds }); keyword(Keyword::Where) - .ignore_then(constraints.separated_by(just(Token::Comma))) + .ignore_then(constraints.separated_by(just(Token::Comma)).allow_trailing()) .or_not() .map(|option| option.unwrap_or_default()) .map(|x: Vec| { @@ -184,11 +182,9 @@ fn trait_bounds() -> impl NoirParser> { trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing() } -fn trait_bound() -> impl NoirParser { - path().then(generic_type_args(parse_type())).map(|(trait_path, trait_generics)| TraitBound { - trait_path, - trait_generics, - trait_id: None, +pub(super) fn trait_bound() -> impl NoirParser { + path_no_turbofish().then(generic_type_args(parse_type())).map(|(trait_path, trait_generics)| { + TraitBound { trait_path, trait_generics, trait_id: None } }) } @@ -215,6 +211,7 @@ mod test { "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", + "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait, { let Size: Field; fn zero() -> Self; }", ], ); 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 cecc1cbcd4cf..fca29bf1e7ab 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 @@ -1,6 +1,7 @@ +use super::path::path_no_turbofish; use super::primitives::token_kind; use super::{ - expression_with_precedence, keyword, nothing, parenthesized, path, NoirParser, ParserError, + expression_with_precedence, keyword, nothing, parenthesized, NoirParser, ParserError, ParserErrorReason, Precedence, }; use crate::ast::{ @@ -14,7 +15,7 @@ use crate::token::{Keyword, Token, TokenKind}; use chumsky::prelude::*; use noirc_errors::Span; -pub(super) fn parse_type<'a>() -> impl NoirParser + 'a { +pub fn parse_type<'a>() -> impl NoirParser + 'a { recursive(parse_type_inner) } @@ -180,7 +181,7 @@ pub(super) fn int_type() -> impl NoirParser { pub(super) fn named_type<'a>( type_parser: impl NoirParser + 'a, ) -> impl NoirParser + 'a { - path().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { + path_no_turbofish().then(generic_type_args(type_parser)).map_with_span(|(path, args), span| { UnresolvedTypeData::Named(path, args, false).with_span(span) }) } @@ -188,13 +189,22 @@ pub(super) fn named_type<'a>( pub(super) fn named_trait<'a>( type_parser: impl NoirParser + 'a, ) -> impl NoirParser + 'a { - keyword(Keyword::Impl).ignore_then(path()).then(generic_type_args(type_parser)).map_with_span( - |(path, args), span| UnresolvedTypeData::TraitAsType(path, args).with_span(span), - ) + keyword(Keyword::Impl) + .ignore_then(path_no_turbofish()) + .then(generic_type_args(type_parser)) + .map_with_span(|(path, args), span| { + UnresolvedTypeData::TraitAsType(path, args).with_span(span) + }) } pub(super) fn generic_type_args<'a>( type_parser: impl NoirParser + 'a, +) -> impl NoirParser> + 'a { + required_generic_type_args(type_parser).or_not().map(Option::unwrap_or_default) +} + +pub(super) fn required_generic_type_args<'a>( + type_parser: impl NoirParser + 'a, ) -> impl NoirParser> + 'a { type_parser .clone() @@ -208,8 +218,6 @@ pub(super) fn generic_type_args<'a>( .allow_trailing() .at_least(1) .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not() - .map(Option::unwrap_or_default) } pub(super) fn array_type<'a>( @@ -241,7 +249,7 @@ fn type_expression() -> impl NoirParser { /// This parser is the same as `type_expression()`, however, it continues parsing and /// emits a parser error in the case of an invalid type expression rather than halting the parser. -fn type_expression_validated() -> impl NoirParser { +pub(super) fn type_expression_validated() -> impl NoirParser { type_expression_inner().validate(|expr, span, emit| { let type_expr = UnresolvedTypeExpression::from_expr(expr, span); match type_expr { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index cbc15da20ff5..8ce430b6e489 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -2494,3 +2494,355 @@ fn bit_not_on_untyped_integer() { "#; assert_no_errors(src); } + +#[test] +fn duplicate_struct_field() { + let src = r#" + struct Foo { + x: i32, + x: i32, + } + + fn main() {} + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::DefinitionError(DefCollectorErrorKind::DuplicateField { + first_def, + second_def, + }) = &errors[0].0 + else { + panic!("Expected a duplicate field error, got {:?}", errors[0].0); + }; + + assert_eq!(first_def.to_string(), "x"); + assert_eq!(second_def.to_string(), "x"); + + assert_eq!(first_def.span().start(), 26); + assert_eq!(second_def.span().start(), 42); +} + +#[test] +fn trait_constraint_on_tuple_type() { + let src = r#" + trait Foo { + fn foo(self, x: A) -> bool; + } + + fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { + x.foo(y) + } + + fn main() {}"#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_constructor_generics_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let _ = Foo:: { x: 1 }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), + )); +} + +#[test] +fn turbofish_in_constructor() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let x: Field = 0; + let _ = Foo:: { x: x }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i32"); + assert_eq!(expr_typ, "Field"); +} + +#[test] +fn turbofish_in_middle_of_variable_unsupported_yet() { + let src = r#" + struct Foo { + x: T + } + + impl Foo { + fn new(x: T) -> Self { + Foo { x } + } + } + + fn main() { + let _ = Foo::::new(1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), + )); +} + +#[test] +fn turbofish_in_struct_pattern() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn turbofish_in_struct_pattern_generic_count_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "struct Foo"); + assert_eq!(*expected, 1); + assert_eq!(*found, 2); +} + +#[test] +fn incorrect_generic_count_on_struct_impl() { + let src = r#" + struct Foo {} + impl Foo {} + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::IncorrectGenericCount { + actual, + expected, + .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(actual, 1); + assert_eq!(expected, 0); +} + +#[test] +fn incorrect_generic_count_on_type_alias() { + let src = r#" + struct Foo {} + type Bar = Foo; + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::IncorrectGenericCount { + actual, + expected, + .. + }) = errors[0].0 + else { + panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(actual, 1); + assert_eq!(expected, 0); +} + +#[test] +fn uses_self_type_for_struct_function_call() { + let src = r#" + struct S { } + + impl S { + fn one() -> Field { + 1 + } + + fn two() -> Field { + Self::one() + Self::one() + } + } + + fn main() {} + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_inside_trait() { + let src = r#" + trait Foo { + fn foo() -> Self { + Self::bar() + } + + fn bar() -> Self; + } + + impl Foo for Field { + fn bar() -> Self { + 1 + } + } + + fn main() { + let _: Field = Foo::foo(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn uses_self_type_in_trait_where_clause() { + let src = r#" + trait Trait { + fn trait_func() -> bool; + } + + trait Foo where Self: Trait { + fn foo(self) -> bool { + self.trait_func() + } + } + + struct Bar { + + } + + impl Foo for Bar { + + } + + fn main() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = + &errors[0].0 + else { + panic!("Expected an unresolved method call error, got {:?}", errors[0].0); + }; + + assert_eq!(method_name, "trait_func"); +} + +#[test] +fn do_not_eagerly_error_on_cast_on_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) + } + + fn main() { + let x: u8 = 1; + let _: Field = foo(x, |x| x as Field); + } + "#; + assert_no_errors(src); +} + +#[test] +fn error_on_cast_over_type_variable() { + let src = r#" + pub fn foo(x: T, f: fn(T) -> U) -> U { + f(x) + } + + fn main() { + let x = "a"; + let _: Field = foo(x, |x| x as Field); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) + )); +} diff --git a/noir/noir-repo/cspell.json b/noir/noir-repo/cspell.json index 689b72435efc..b9199bea4bdd 100644 --- a/noir/noir-repo/cspell.json +++ b/noir/noir-repo/cspell.json @@ -126,6 +126,7 @@ "memset", "merkle", "metas", + "microcontroller", "minreq", "monomorphization", "monomorphize", @@ -135,6 +136,7 @@ "monomorphizing", "montcurve", "MSRV", + "multicall", "nand", "nargo", "neovim", diff --git a/noir/noir-repo/docs/docs/explainers/cspell.json b/noir/noir-repo/docs/docs/explainers/cspell.json new file mode 100644 index 000000000000..c60b0a597b10 --- /dev/null +++ b/noir/noir-repo/docs/docs/explainers/cspell.json @@ -0,0 +1,5 @@ +{ + "words": [ + "Cryptdoku" + ] +} diff --git a/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md new file mode 100644 index 000000000000..c8a42c379e66 --- /dev/null +++ b/noir/noir-repo/docs/docs/explainers/explainer-writing-noir.md @@ -0,0 +1,173 @@ +--- +title: Writing Performant Noir +description: Understand new considerations when writing Noir +keywords: [Noir, programming, rust] +tags: [Optimization] +sidebar_position: 0 +--- + + +This article intends to set you up with key concepts essential for writing more viable applications that use zero knowledge proofs, namely around efficient circuits. + +## Context - 'Efficient' is subjective + +When writing a web application for a performant computer with high-speed internet connection, writing efficient code sometimes is seen as an afterthought only if needed. Large multiplications running at the innermost of nested loops may not even be on a dev's radar. +When writing firmware for a battery-powered microcontroller, you think of cpu cycles as rations to keep within a product's power budget. + +> Code is written to create applications that perform specific tasks within specific constraints + +And these constraints differ depending on where the compiled code is execute. + +### The Ethereum Virtual Machine (EVM) + +In scenarios where extremely low gas costs are required for an Ethereum application to be viable/competitive, Ethereum smart contract developers get into what is colloquially known as: "*gas golfing*". Finding the lowest execution cost of their compiled code (EVM bytecode) to achieve a specific task. + +The equivalent optimization task when writing zk circuits is affectionately referred to as "*gate golfing*", finding the lowest gate representation of the compiled Noir code. + +### Coding for circuits - a paradigm shift + +In zero knowledge cryptography, code is compiled to "circuits" consisting of arithmetic gates, and gate count is the significant cost. Depending on the proving system this is linearly proportionate to proving time, and so from a product point this should be kept as low as possible. + +Whilst writing efficient code for web apps and Solidity has a few key differences, writing efficient circuits have a different set of considerations. It is a bit of a paradigm shift, like writing code for GPUs for the first time... + +For example, drawing a circle at (0, 0) of radius `r`: +- For a single CPU thread, +``` +for theta in 0..2*pi { + let x = r * cos(theta); + let y = r * sin(theta); + draw(x, y); +} // note: would do 0 - pi/2 and draw +ve/-ve x and y. +``` + +- For GPUs (simultaneous parallel calls with x, y across image), +``` +if (x^2 + y^2 = r^2) { + draw(x, y); +} +``` + +([Related](https://www.youtube.com/watch?v=-P28LKWTzrI)) + +Whilst this CPU -> GPU does not translate to circuits exactly, it is intended to exemplify the difference in intuition when coding for different machine capabilities/constraints. + +### Context Takeaway + +For those coming from a primarily web app background, this article will explain what you need to consider when writing circuits. Furthermore, for those experienced writing efficient machine code, prepare to shift what you think is efficient 😬 + +## Translating from Rust + +For some applications using Noir, existing code might be a convenient starting point to then proceed to optimize the gate count of. + +:::note +Many valuable functions and algorithms have been written in more established languages (C/C++), and converted to modern ones (like Rust). +::: + +Fortunately for Noir developers, when needing a particular function a Rust implementation can be readily compiled into Noir with some key changes. While the compiler does a decent amount of optimizations, it won't be able to change code that has been optimized for clock-cycles into code optimized for arithmetic gates. + +A few things to do when converting Rust code to Noir: +- `println!` is not a macro, use `println` function (same for `assert_eq`) +- No early `return` in function. Use constrain via assertion instead +- No passing by reference. Remove `&` operator to pass by value (copy) +- No boolean operators (`&&`, `||`). Use bitwise operators (`&`, `|`) with boolean values +- No type `usize`. Use types `u8`, `u32`, `u64`, ... +- `main` return must be public, `pub` +- No `const`, use `global` +- Noir's LSP is your friend, so error message should be informative enough to resolve syntax issues. + +## Writing efficient Noir for performant products + +The following points help refine our understanding over time. + +:::note +A Noir program makes a statement that can be verified. +::: + +It compiles to a structure that represents the calculation, and can assert results within the calculation at any stage (via the `constrain` keyword). + +A Noir program compiles to an Abstract Circuit Intermediate Representation which is: + - A tree structure + - Leaves (inputs) are the `Field` type + - Nodes contain arithmetic operations to combine them (gates) + - The root is the final result (return value) + +:::tip +The command `nargo info` shows the programs circuit size, and is useful to compare the value of changes made. +You can dig deeper and use the `--print-acir` param to take a closer look at individual ACIR opcodes, and the proving backend to see its gate count (eg for barretenberg, `bb gates -b ./target/program.json`). +::: + +### Use the `Field` type + +Since the native type of values in circuits are `Field`s, using them for variables in Noir means less gates converting them under the hood. + +:::tip +Where possible, use `Field` type for values. Using smaller value types, and bit-packing strategies, will result in MORE gates +::: + +**Note:** Need to remain mindful of overflow. Types with less bits may be used to limit the range of possible values prior to a calculation. + +### Use Arithmetic over non-arithmetic operations + +Since circuits are made of arithmetic gates, the cost of arithmetic operations tends to be one gate. Whereas for procedural code, they represent several clock cycles. + +Inversely, non-arithmetic operators are achieved with multiple gates, vs 1 clock cycle for procedural code. + +| (cost\op) | arithmetic
(`*`, `+`) | bit-wise ops
(eg `<`, `\|`, `>>`) | +| - | - | - | +| **cycles** | 10+ | 1 | +| **gates** | 1 | 10+ | + +Bit-wise operations (e.g. bit shifts `<<` and `>>`), albeit commonly used in general programming and especially for clock cycle optimizations, are on the contrary expensive in gates when performed within circuits. + +Translate away from bit shifts when writing constrained functions for the best performance. + +On the flip side, feel free to use bit shifts in unconstrained functions and tests if necessary, as they are executed outside of circuits and does not induce performance hits. + +### Use static over dynamic values + +Another general theme that manifests in different ways is that static reads are represented with less gates than dynamic ones. + +Reading from read-only memory (ROM) adds less gates than random-access memory (RAM), 2 vs ~3.25 due to the additional bounds checks. Arrays of fixed length (albeit used at a lower capacity), will generate less gates than dynamic storage. + +Related to this, if an index used to access an array is not known at compile time (ie unknown until run time), then ROM will be converted to RAM, expanding the gate count. + +:::tip +Use arrays and indices that are known at compile time where possible. +Using `assert_constant(i);` before an index, `i`, is used in an array will give a compile error if `i` is NOT known at compile time. +::: + +### Leverage unconstrained execution + +Constrained verification can leverage unconstrained execution, this is especially useful for operations that are represented by many gates. +Use an [unconstrained function](../noir/concepts/unconstrained.md) to perform gate-heavy calculations, then verify and constrain the result. + +Eg division generates more gates than multiplication, so calculating the quotient in an unconstrained function then constraining the product for the quotient and divisor (+ any remainder) equals the dividend will be more efficient. + +Use ` if is_unconstrained() { /`, to conditionally execute code if being called in an unconstrained vs constrained way. + +## Advanced + +Unless you're well into the depth of gate optimization, this advanced section can be ignored. + +### Combine arithmetic operations + +A Noir program can be honed further by combining arithmetic operators in a way that makes the most of each constraint of the backend proving system. This is in scenarios where the backend might not be doing this perfectly. + +Eg Barretenberg backend (current default for Noir) is a width-4 PLONKish constraint system +$ w_1*w_2*q_m + w_1*q_1 + w_2*q_2 + w_3*q_3 + w_4*q_4 + q_c = 0 $ + +Here we see there is one occurrence of witness 1 and 2 ($w_1$, $w_2$) being multiplied together, with addition to witnesses 1-4 ($w_1$ .. $w_4$) multiplied by 4 corresponding circuit constants ($q_1$ .. $q_4$) (plus a final circuit constant, $q_c$). + +Use `nargo info --print-acir`, to inspect the ACIR opcodes (and the proving system for gates), and it may present opportunities to amend the order of operations and reduce the number of constraints. + +#### Variable as witness vs expression + +If you've come this far and really know what you're doing at the equation level, a temporary lever (that will become unnecessary/useless over time) is: `std::as_witness`. This informs the compiler to save a variable as a witness not an expression. + +The compiler will mostly be correct and optimal, but this may help some near term edge cases that are yet to optimize. +Note: When used incorrectly it will create **less** efficient circuits (higher gate count). + +## References +- Guillaume's ["`Cryptdoku`" talk](https://www.youtube.com/watch?v=MrQyzuogxgg) (Jun'23) +- Tips from Tom, Jake and Zac. +- [Idiomatic Noir](https://www.vlayer.xyz/blog/idiomatic-noir-part-1-collections) blog post diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json b/noir/noir-repo/docs/docs/getting_started/backend/_category_.json similarity index 63% rename from noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json rename to noir/noir-repo/docs/docs/getting_started/backend/_category_.json index 27a8e89228de..b82e92beb0ca 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/_category_.json +++ b/noir/noir-repo/docs/docs/getting_started/backend/_category_.json @@ -1,6 +1,6 @@ { "position": 1, - "label": "Install Barretenberg", + "label": "Install Proving Backend", "collapsible": true, "collapsed": true } diff --git a/noir/noir-repo/docs/docs/getting_started/backend/index.md b/noir/noir-repo/docs/docs/getting_started/backend/index.md new file mode 100644 index 000000000000..7192d9548774 --- /dev/null +++ b/noir/noir-repo/docs/docs/getting_started/backend/index.md @@ -0,0 +1,31 @@ +--- +title: Proving Backend Installation +description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. +keywords: [ + Proving + Backend + Barretenberg + bb + bbup + Installation + Terminal + Command + CLI + Version +] +pagination_next: getting_started/hello_noir/index +--- + +Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. + +For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. + +You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). + +## Example: Installing `bb` + +`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. + +You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). + +Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/noir/noir-repo/docs/docs/getting_started/barretenberg/index.md b/noir/noir-repo/docs/docs/getting_started/barretenberg/index.md deleted file mode 100644 index 0102c86770b7..000000000000 --- a/noir/noir-repo/docs/docs/getting_started/barretenberg/index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Barretenberg Installation -description: bb is a command line tool for interacting with Aztec's proving backend Barretenberg. This page is a quick guide on how to install `bb` -keywords: [ - Barretenberg - bb - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches -] -pagination_next: getting_started/hello_noir/index ---- - -`bb` is the CLI tool for generating and verifying proofs for Noir programs using the Barretenberg proving library. It also allows generating solidity verifier contracts for which you can verify contracts which were constructed using `bb`. - -## Installing `bb` - -Open a terminal on your machine, and write: - -##### macOS (Apple Silicon) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.zshrc -bbup -v 0.41.0 -``` - -##### macOS (Intel) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.zshrc -bbup -v 0.41.0 -``` - -##### Linux (Bash) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.bashrc -bbup -v 0.41.0 -``` - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/noir-repo/docs/docs/getting_started/hello_noir/index.md b/noir/noir-repo/docs/docs/getting_started/hello_noir/index.md index 1ade3f09ae3d..3baae217eb30 100644 --- a/noir/noir-repo/docs/docs/getting_started/hello_noir/index.md +++ b/noir/noir-repo/docs/docs/getting_started/hello_noir/index.md @@ -17,22 +17,25 @@ sidebar_position: 1 --- -Now that we have installed Nargo, it is time to make our first hello world program! +Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! -## Create a Project Directory +### 1. Create a new project directory Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our Noir programs. +directory to house our first Noir program. -For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by -running: +Create the directory and change directory into it by running: ```sh mkdir ~/projects cd ~/projects ``` -## Create Our First Nargo Project +## Nargo + +Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. + +### 2. Create a new Noir project Now that we are in the projects directory, create a new Nargo project by running: @@ -40,18 +43,15 @@ Now that we are in the projects directory, create a new Nargo project by running nargo new hello_world ``` -> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for -> demonstration. -> -> In production, the common practice is to name the project folder as `circuits` for better -> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, -> `test`). +`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. + +In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and _Nargo.toml_ which contain the source code and environmental options of your Noir program respectively. -### Intro to Noir Syntax +#### Intro to Noir Syntax Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: @@ -81,7 +81,7 @@ The Noir syntax `assert` can be interpreted as something similar to constraints For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. -## Build In/Output Files +### 3. Build in/output files Change directory into _hello_world_ and build in/output files for your Noir program by running: @@ -92,7 +92,7 @@ nargo check A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. -## Execute Our Noir Program +### 4. Execute the Noir program Now that the project is set up, we can execute our Noir program. @@ -111,34 +111,41 @@ nargo execute witness-name The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. -## Prove Our Noir Program +The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. + +## Proving Backend -:::info +Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. -Nargo no longer handles communicating with backends in order to generate proofs. In order to prove/verify your Noir programs, you'll need an installation of [bb](../barretenberg/index.md). +Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. -::: +### 5. Prove an execution of the Noir program -Prove the valid execution of your Noir program using `bb`: +Using Barretenberg as an example, prove the valid execution of your Noir program running: ```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./proof +bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof ``` -A new file called `proof` will be generated in your project directory, containing the generated proof for your program. +The proof generated will then be written to the file `./target/proof`. -## Verify Our Noir Program +### 6. Verify the execution proof Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. -Verify your proof by running: +Using Barretenberg as an example, compute the verification key for the Noir program by running: ```sh bb write_vk -b ./target/hello_world.json -o ./target/vk -bb verify -k ./target/vk -p ./proof ``` -The verification will complete in silence if it is successful. If it fails, it will log the corresponding error instead. +And verify your proof by running: + +```sh +bb verify -k ./target/vk -p ./target/proof +``` + +If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. Congratulations, you have now created and verified a proof for your very first Noir program! diff --git a/noir/noir-repo/docs/docs/getting_started/hello_noir/project_breakdown.md b/noir/noir-repo/docs/docs/getting_started/hello_noir/project_breakdown.md index 525b8dabdd84..96e653f6c08d 100644 --- a/noir/noir-repo/docs/docs/getting_started/hello_noir/project_breakdown.md +++ b/noir/noir-repo/docs/docs/getting_started/hello_noir/project_breakdown.md @@ -8,8 +8,7 @@ keywords: sidebar_position: 2 --- -This section breaks down our hello world program from the previous section. We elaborate on the project -structure and what the `prove` and `verify` commands did. +This section breaks down our hello world program from the previous section. ## Anatomy of a Nargo Project diff --git a/noir/noir-repo/docs/docs/getting_started/installation/index.md b/noir/noir-repo/docs/docs/getting_started/installation/index.md index 4ef86aa59147..53ea9c7891ca 100644 --- a/noir/noir-repo/docs/docs/getting_started/installation/index.md +++ b/noir/noir-repo/docs/docs/getting_started/installation/index.md @@ -19,11 +19,9 @@ keywords: [ pagination_next: getting_started/hello_noir/index --- -`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. +`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. -With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. - -Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. +The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. ## Installing Noirup diff --git a/noir/noir-repo/docs/docs/getting_started/tooling/noir_codegen.md b/noir/noir-repo/docs/docs/reference/noir_codegen.md similarity index 97% rename from noir/noir-repo/docs/docs/getting_started/tooling/noir_codegen.md rename to noir/noir-repo/docs/docs/reference/noir_codegen.md index f7505bef7abe..db8f07dc22e6 100644 --- a/noir/noir-repo/docs/docs/getting_started/tooling/noir_codegen.md +++ b/noir/noir-repo/docs/docs/reference/noir_codegen.md @@ -33,7 +33,7 @@ yarn add @noir-lang/noir_codegen -D ``` ### Nargo library -Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md). +Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../getting_started/installation/index.md). If you're in a new project, make a `circuits` folder and create a new Noir library: diff --git a/noir/noir-repo/docs/docs/tutorials/noirjs_app.md b/noir/noir-repo/docs/docs/tutorials/noirjs_app.md index cbb1938a5c6f..8c23b639f123 100644 --- a/noir/noir-repo/docs/docs/tutorials/noirjs_app.md +++ b/noir/noir-repo/docs/docs/tutorials/noirjs_app.md @@ -14,13 +14,13 @@ You can find the complete app code for this guide [here](https://github.com/noir :::note -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.27.x matches `noir_js@0.27.x`, etc. +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.31.x matches `noir_js@0.31.x`, etc. -In this guide, we will be pinned to 0.27.0. +In this guide, we will be pinned to 0.31.0. ::: -Before we start, we want to make sure we have Node and Nargo installed. +Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). @@ -30,6 +30,9 @@ As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/ curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash ``` +Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. +Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. + Easy enough. Onwards! ## Our project @@ -42,13 +45,17 @@ In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! Run: -`nargo new circuit` +```bash +nargo new circuit +``` And... That's about it. Your program is ready to be compiled and run. To compile, let's `cd` into the `circuit` folder to enter our project, and call: -`nargo compile` +```bash +nargo compile +``` This compiles our circuit into `json` format and add it to a new `target` folder. @@ -92,30 +99,53 @@ Before we proceed with any coding, let's get our environment tailored for Noir. In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: ```javascript -import { defineConfig } from "vite"; -import copy from "rollup-plugin-copy"; - -export default defineConfig({ - esbuild: { - target: "esnext", - }, - optimizeDeps: { - esbuildOptions: { - target: "esnext", - }, +import { defineConfig } from 'vite'; +import copy from 'rollup-plugin-copy'; +import fs from 'fs'; +import path from 'path'; + +const wasmContentTypePlugin = { + name: 'wasm-content-type-plugin', + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + if (req.url.endsWith('.wasm')) { + res.setHeader('Content-Type', 'application/wasm'); + const newPath = req.url.replace('deps', 'dist'); + const targetPath = path.join(__dirname, newPath); + const wasmContent = fs.readFileSync(targetPath); + return res.end(wasmContent); + } + next(); + }); }, - plugins: [ - copy({ - targets: [ - { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, +}; + +export default defineConfig(({ command }) => { + if (command === 'serve') { + return { + build: { + target: 'esnext', + rollupOptions: { + external: ['@aztec/bb.js'] + } + }, + optimizeDeps: { + esbuildOptions: { + target: 'esnext' + } + }, + plugins: [ + copy({ + targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], + copySync: true, + hook: 'buildStart', + }), + command === 'serve' ? wasmContentTypePlugin : [], ], - copySync: true, - hook: "buildStart", - }), - ], - server: { - port: 3000, - }, + }; + } + + return {}; }); ``` @@ -124,7 +154,7 @@ export default defineConfig({ Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: ```bash -npm install && npm install @noir-lang/backend_barretenberg@0.27.0 @noir-lang/noir_js@0.27.0 +npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 npm install rollup-plugin-copy --save-dev ``` @@ -193,17 +223,6 @@ Our love for Noir needs undivided attention, so let's just open `main.js` and de Start by pasting in this boilerplate code: ```js -const setup = async () => { - await Promise.all([ - import('@noir-lang/noirc_abi').then((module) => - module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), - ), - import('@noir-lang/acvm_js').then((module) => - module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), - ), - ]); -}; - function display(container, msg) { const c = document.getElementById(container); const p = document.createElement('p'); @@ -222,8 +241,6 @@ document.getElementById('submitGuess').addEventListener('click', async () => { The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 -As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. - :::info At this point in the tutorial, your folder structure should look like this: @@ -310,9 +327,13 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verificationKey = await backend.getVerificationKey(); -const verifier = new Verifier(); -const isValid = await verifier.verifyProof(proof, verificationKey); +const isValid = await backend.verifyProof(proof); + +// or to cache and use the verification key: +// const verificationKey = await backend.getVerificationKey(); +// const verifier = new Verifier(); +// const isValid = await verifier.verifyProof(proof, verificationKey); + if (isValid) display('logs', 'Verifying proof... ✅'); ``` diff --git a/noir/noir-repo/docs/docusaurus.config.ts b/noir/noir-repo/docs/docusaurus.config.ts index f0c986f1c28c..29f612b01097 100644 --- a/noir/noir-repo/docs/docusaurus.config.ts +++ b/noir/noir-repo/docs/docusaurus.config.ts @@ -14,7 +14,6 @@ export default { favicon: 'img/favicon.ico', url: 'https://noir-lang.org', baseUrl: '/', - trailingSlash: true, onBrokenLinks: 'throw', onBrokenMarkdownLinks: 'throw', i18n: { diff --git a/noir/noir-repo/docs/src/components/Notes/_blackbox.mdx b/noir/noir-repo/docs/src/components/Notes/_blackbox.mdx index 226017072c88..514ca00a7e7c 100644 --- a/noir/noir-repo/docs/src/components/Notes/_blackbox.mdx +++ b/noir/noir-repo/docs/src/components/Notes/_blackbox.mdx @@ -1,5 +1,5 @@ :::info -This is a black box function. Read [this section](../../black_box_fns) to learn more about black box functions in Noir. +This is a black box function. Read [this section](/docs/noir/standard_library/black_box_fns) to learn more about black box functions in Noir. ::: diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md index 34f8cd96fcd8..0384ba4a0cda 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md @@ -84,7 +84,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../language_concepts/comments.md) chapter. +For more Noir syntax, check the [Language Concepts](../language_concepts/09_comments.md) chapter. ## Build In/Output Files diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md index d353606210a5..67baa00f930e 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/02_booleans.md @@ -26,5 +26,5 @@ fn main() { > `false` in _Verifier.toml_. The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow.md) and -[Assert Function](../assert.md) sections. +statements. More about conditionals is covered in the [Control Flow](../02_control_flow.md) and +[Assert Function](../04_assert.md) sections. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md index 1424ca2df14e..5b4a544cf379 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/language_concepts/data_types/04_arrays.md @@ -56,7 +56,7 @@ You can instantiate a new array of a fixed size with the same value repeated for let array: [Field; 32] = [0; 32]; ``` -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices.mdx), you can just call `as_slice` on your array: +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./05_slices.mdx), you can just call `as_slice` on your array: ```rust let array: [Field; 32] = [0; 32]; diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md index 87a09293ea87..753b60387033 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/modules_packages_crates/dependencies.md @@ -81,7 +81,7 @@ use dep::std::scalar_mul::fixed_base_embedded_curve; ``` Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives.md#examples), you +[elliptic curve example](../standard_library/cryptographic_primitives/04_ec_primitives.md#examples), you can import multiple items in the same line by enclosing them in curly braces: ```rust diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md index e2b0af522f43..914856a0f435 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/nargo/01_commands.md @@ -213,7 +213,7 @@ you run `nargo test`. To print `println` statements in tests, use the `--show-ou Takes an optional `--exact` flag which allows you to select tests based on an exact name. -See an example on the [testing page](./testing.md). +See an example on the [testing page](./02_testing.md). ### Options diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md index 985bb7c879dc..b115a450ed31 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.19.4/standard_library/black_box_fns.md @@ -26,19 +26,19 @@ fn sha256(_input : [u8; N]) -> [u8; 32] {} Here is a list of the current black box functions that are supported by UltraPlonk: - AES -- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) -- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) -- [HashToField128Security](./cryptographic_primitives/hashes.mdx#hash_to_field) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- [SHA256](./cryptographic_primitives/00_hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/02_schnorr.mdx) +- [Blake2s](./cryptographic_primitives/00_hashes.mdx#blake2s) +- [Pedersen Hash](./cryptographic_primitives/00_hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/00_hashes.mdx#pedersen_commitment) +- [HashToField128Security](./cryptographic_primitives/00_hashes.mdx#hash_to_field) +- [ECDSA signature verification](./cryptographic_primitives/03_ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/01_scalar.mdx) - [Compute merkle root](./merkle_trees.md#compute_merkle_root) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Keccak256](./cryptographic_primitives/00_hashes.mdx#keccak256) - [Recursive proof verification](./recursion.md) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/_category_.json similarity index 63% rename from noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/_category_.json rename to noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/_category_.json index 27a8e89228de..b82e92beb0ca 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/_category_.json +++ b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/_category_.json @@ -1,6 +1,6 @@ { "position": 1, - "label": "Install Barretenberg", + "label": "Install Proving Backend", "collapsible": true, "collapsed": true } diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/index.md new file mode 100644 index 000000000000..7192d9548774 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/backend/index.md @@ -0,0 +1,31 @@ +--- +title: Proving Backend Installation +description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. +keywords: [ + Proving + Backend + Barretenberg + bb + bbup + Installation + Terminal + Command + CLI + Version +] +pagination_next: getting_started/hello_noir/index +--- + +Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. + +For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. + +You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). + +## Example: Installing `bb` + +`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. + +You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). + +Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md deleted file mode 100644 index 0102c86770b7..000000000000 --- a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/barretenberg/index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Barretenberg Installation -description: bb is a command line tool for interacting with Aztec's proving backend Barretenberg. This page is a quick guide on how to install `bb` -keywords: [ - Barretenberg - bb - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches -] -pagination_next: getting_started/hello_noir/index ---- - -`bb` is the CLI tool for generating and verifying proofs for Noir programs using the Barretenberg proving library. It also allows generating solidity verifier contracts for which you can verify contracts which were constructed using `bb`. - -## Installing `bb` - -Open a terminal on your machine, and write: - -##### macOS (Apple Silicon) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.zshrc -bbup -v 0.41.0 -``` - -##### macOS (Intel) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.zshrc -bbup -v 0.41.0 -``` - -##### Linux (Bash) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.bashrc -bbup -v 0.41.0 -``` - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md index 1ade3f09ae3d..3baae217eb30 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/index.md @@ -17,22 +17,25 @@ sidebar_position: 1 --- -Now that we have installed Nargo, it is time to make our first hello world program! +Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! -## Create a Project Directory +### 1. Create a new project directory Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our Noir programs. +directory to house our first Noir program. -For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by -running: +Create the directory and change directory into it by running: ```sh mkdir ~/projects cd ~/projects ``` -## Create Our First Nargo Project +## Nargo + +Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. + +### 2. Create a new Noir project Now that we are in the projects directory, create a new Nargo project by running: @@ -40,18 +43,15 @@ Now that we are in the projects directory, create a new Nargo project by running nargo new hello_world ``` -> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for -> demonstration. -> -> In production, the common practice is to name the project folder as `circuits` for better -> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, -> `test`). +`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. + +In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and _Nargo.toml_ which contain the source code and environmental options of your Noir program respectively. -### Intro to Noir Syntax +#### Intro to Noir Syntax Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: @@ -81,7 +81,7 @@ The Noir syntax `assert` can be interpreted as something similar to constraints For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. -## Build In/Output Files +### 3. Build in/output files Change directory into _hello_world_ and build in/output files for your Noir program by running: @@ -92,7 +92,7 @@ nargo check A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. -## Execute Our Noir Program +### 4. Execute the Noir program Now that the project is set up, we can execute our Noir program. @@ -111,34 +111,41 @@ nargo execute witness-name The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. -## Prove Our Noir Program +The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. + +## Proving Backend -:::info +Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. -Nargo no longer handles communicating with backends in order to generate proofs. In order to prove/verify your Noir programs, you'll need an installation of [bb](../barretenberg/index.md). +Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. -::: +### 5. Prove an execution of the Noir program -Prove the valid execution of your Noir program using `bb`: +Using Barretenberg as an example, prove the valid execution of your Noir program running: ```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./proof +bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof ``` -A new file called `proof` will be generated in your project directory, containing the generated proof for your program. +The proof generated will then be written to the file `./target/proof`. -## Verify Our Noir Program +### 6. Verify the execution proof Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. -Verify your proof by running: +Using Barretenberg as an example, compute the verification key for the Noir program by running: ```sh bb write_vk -b ./target/hello_world.json -o ./target/vk -bb verify -k ./target/vk -p ./proof ``` -The verification will complete in silence if it is successful. If it fails, it will log the corresponding error instead. +And verify your proof by running: + +```sh +bb verify -k ./target/vk -p ./target/proof +``` + +If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. Congratulations, you have now created and verified a proof for your very first Noir program! diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md index 29688df148fe..96e653f6c08d 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/hello_noir/project_breakdown.md @@ -8,8 +8,7 @@ keywords: sidebar_position: 2 --- -This section breaks down our hello world program from the previous section. We elaborate on the project -structure and what the `prove` and `verify` commands did. +This section breaks down our hello world program from the previous section. ## Anatomy of a Nargo Project @@ -67,6 +66,7 @@ The package section defines a number of fields including: - `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) - `backend` (optional) - `license` (optional) +- `expression_width` (optional) - Sets the default backend expression width. This field will override the default backend expression width specified by the Noir compiler (currently set to width 4). #### Dependencies section diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md index 4ef86aa59147..53ea9c7891ca 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/getting_started/installation/index.md @@ -19,11 +19,9 @@ keywords: [ pagination_next: getting_started/hello_noir/index --- -`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. +`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. -With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. - -Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. +The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. ## Installing Noirup diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md index cbb1938a5c6f..8c23b639f123 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.31.0/tutorials/noirjs_app.md @@ -14,13 +14,13 @@ You can find the complete app code for this guide [here](https://github.com/noir :::note -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.27.x matches `noir_js@0.27.x`, etc. +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.31.x matches `noir_js@0.31.x`, etc. -In this guide, we will be pinned to 0.27.0. +In this guide, we will be pinned to 0.31.0. ::: -Before we start, we want to make sure we have Node and Nargo installed. +Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). @@ -30,6 +30,9 @@ As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/ curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash ``` +Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. +Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. + Easy enough. Onwards! ## Our project @@ -42,13 +45,17 @@ In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! Run: -`nargo new circuit` +```bash +nargo new circuit +``` And... That's about it. Your program is ready to be compiled and run. To compile, let's `cd` into the `circuit` folder to enter our project, and call: -`nargo compile` +```bash +nargo compile +``` This compiles our circuit into `json` format and add it to a new `target` folder. @@ -92,30 +99,53 @@ Before we proceed with any coding, let's get our environment tailored for Noir. In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: ```javascript -import { defineConfig } from "vite"; -import copy from "rollup-plugin-copy"; - -export default defineConfig({ - esbuild: { - target: "esnext", - }, - optimizeDeps: { - esbuildOptions: { - target: "esnext", - }, +import { defineConfig } from 'vite'; +import copy from 'rollup-plugin-copy'; +import fs from 'fs'; +import path from 'path'; + +const wasmContentTypePlugin = { + name: 'wasm-content-type-plugin', + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + if (req.url.endsWith('.wasm')) { + res.setHeader('Content-Type', 'application/wasm'); + const newPath = req.url.replace('deps', 'dist'); + const targetPath = path.join(__dirname, newPath); + const wasmContent = fs.readFileSync(targetPath); + return res.end(wasmContent); + } + next(); + }); }, - plugins: [ - copy({ - targets: [ - { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, +}; + +export default defineConfig(({ command }) => { + if (command === 'serve') { + return { + build: { + target: 'esnext', + rollupOptions: { + external: ['@aztec/bb.js'] + } + }, + optimizeDeps: { + esbuildOptions: { + target: 'esnext' + } + }, + plugins: [ + copy({ + targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], + copySync: true, + hook: 'buildStart', + }), + command === 'serve' ? wasmContentTypePlugin : [], ], - copySync: true, - hook: "buildStart", - }), - ], - server: { - port: 3000, - }, + }; + } + + return {}; }); ``` @@ -124,7 +154,7 @@ export default defineConfig({ Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: ```bash -npm install && npm install @noir-lang/backend_barretenberg@0.27.0 @noir-lang/noir_js@0.27.0 +npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 npm install rollup-plugin-copy --save-dev ``` @@ -193,17 +223,6 @@ Our love for Noir needs undivided attention, so let's just open `main.js` and de Start by pasting in this boilerplate code: ```js -const setup = async () => { - await Promise.all([ - import('@noir-lang/noirc_abi').then((module) => - module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), - ), - import('@noir-lang/acvm_js').then((module) => - module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), - ), - ]); -}; - function display(container, msg) { const c = document.getElementById(container); const p = document.createElement('p'); @@ -222,8 +241,6 @@ document.getElementById('submitGuess').addEventListener('click', async () => { The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 -As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. - :::info At this point in the tutorial, your folder structure should look like this: @@ -310,9 +327,13 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verificationKey = await backend.getVerificationKey(); -const verifier = new Verifier(); -const isValid = await verifier.verifyProof(proof, verificationKey); +const isValid = await backend.verifyProof(proof); + +// or to cache and use the verification key: +// const verificationKey = await backend.getVerificationKey(); +// const verifier = new Verifier(); +// const isValid = await verifier.verifyProof(proof, verificationKey); + if (isValid) display('logs', 'Verifying proof... ✅'); ``` diff --git a/noir/noir-repo/docs/docs/getting_started/barretenberg/_category_.json b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/_category_.json similarity index 63% rename from noir/noir-repo/docs/docs/getting_started/barretenberg/_category_.json rename to noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/_category_.json index 27a8e89228de..b82e92beb0ca 100644 --- a/noir/noir-repo/docs/docs/getting_started/barretenberg/_category_.json +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/_category_.json @@ -1,6 +1,6 @@ { "position": 1, - "label": "Install Barretenberg", + "label": "Install Proving Backend", "collapsible": true, "collapsed": true } diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md new file mode 100644 index 000000000000..7192d9548774 --- /dev/null +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/backend/index.md @@ -0,0 +1,31 @@ +--- +title: Proving Backend Installation +description: Proving backends offer command line tools for proving and verifying Noir programs. This page describes how to install `bb` as an example. +keywords: [ + Proving + Backend + Barretenberg + bb + bbup + Installation + Terminal + Command + CLI + Version +] +pagination_next: getting_started/hello_noir/index +--- + +Proving backends each provide their own tools for working with Noir programs, providing functionality like proof generation, proof verification, and verifier smart contract generation. + +For the latest information on tooling provided by each proving backend, installation instructions, Noir version compatibility... you may refer to the proving backends' own documentation. + +You can find the full list of proving backends compatible with Noir in [Awesome Noir](https://github.com/noir-lang/awesome-noir/?tab=readme-ov-file#proving-backends). + +## Example: Installing `bb` + +`bb` is the CLI tool provided by the [Barretenberg proving backend](https://github.com/AztecProtocol/barretenberg) developed by Aztec Labs. + +You can find the instructions for installation in [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation). + +Once installed, we are ready to start working on [our first Noir program](../hello_noir/index.md). diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/index.md deleted file mode 100644 index 0102c86770b7..000000000000 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/barretenberg/index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Barretenberg Installation -description: bb is a command line tool for interacting with Aztec's proving backend Barretenberg. This page is a quick guide on how to install `bb` -keywords: [ - Barretenberg - bb - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches -] -pagination_next: getting_started/hello_noir/index ---- - -`bb` is the CLI tool for generating and verifying proofs for Noir programs using the Barretenberg proving library. It also allows generating solidity verifier contracts for which you can verify contracts which were constructed using `bb`. - -## Installing `bb` - -Open a terminal on your machine, and write: - -##### macOS (Apple Silicon) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.zshrc -bbup -v 0.41.0 -``` - -##### macOS (Intel) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.zshrc -bbup -v 0.41.0 -``` - -##### Linux (Bash) - -```bash -curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash -source ~/.bashrc -bbup -v 0.41.0 -``` - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md index 1ade3f09ae3d..3baae217eb30 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/index.md @@ -17,22 +17,25 @@ sidebar_position: 1 --- -Now that we have installed Nargo, it is time to make our first hello world program! +Now that we have installed Nargo and a proving backend, it is time to make our first hello world program! -## Create a Project Directory +### 1. Create a new project directory Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our Noir programs. +directory to house our first Noir program. -For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by -running: +Create the directory and change directory into it by running: ```sh mkdir ~/projects cd ~/projects ``` -## Create Our First Nargo Project +## Nargo + +Nargo provides the ability to initiate and execute Noir projects. Read the [Nargo installation](../installation/index.md) section to learn more about Nargo and how to install it. + +### 2. Create a new Noir project Now that we are in the projects directory, create a new Nargo project by running: @@ -40,18 +43,15 @@ Now that we are in the projects directory, create a new Nargo project by running nargo new hello_world ``` -> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for -> demonstration. -> -> In production, the common practice is to name the project folder as `circuits` for better -> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, -> `test`). +`hello_world` can be any arbitrary project name, we are simply using `hello_world` for demonstration. + +In production, it is common practice to name the project folder, `circuits`, for clarity amongst other folders in the codebase (like: `contracts`, `scripts`, and `test`). A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and _Nargo.toml_ which contain the source code and environmental options of your Noir program respectively. -### Intro to Noir Syntax +#### Intro to Noir Syntax Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: @@ -81,7 +81,7 @@ The Noir syntax `assert` can be interpreted as something similar to constraints For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. -## Build In/Output Files +### 3. Build in/output files Change directory into _hello_world_ and build in/output files for your Noir program by running: @@ -92,7 +92,7 @@ nargo check A _Prover.toml_ file will be generated in your project directory, to allow specifying input values to the program. -## Execute Our Noir Program +### 4. Execute the Noir program Now that the project is set up, we can execute our Noir program. @@ -111,34 +111,41 @@ nargo execute witness-name The witness corresponding to this execution will then be written to the file `./target/witness-name.gz`. -## Prove Our Noir Program +The command also automatically compiles your Noir program if it was not already / was edited, which you may notice the compiled artifacts being written to the file `./target/hello_world.json`. + +## Proving Backend -:::info +Proving backends provide the ability to generate and verify proofs of executing Noir programs, following Noir's tooling that compiles and executes the programs. Read the [proving backend installation](../backend/index.md) section to learn more about proving backends and how to install them. -Nargo no longer handles communicating with backends in order to generate proofs. In order to prove/verify your Noir programs, you'll need an installation of [bb](../barretenberg/index.md). +Barretenberg is used as an example here to demonstrate how proving and verifying could be implemented and used. Read the [`bb` installation](../backend/index.md#example-installing-bb) section for how to install Barretenberg's CLI tool; refer to [`bb`'s documentation](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md) for full details about the tool. -::: +### 5. Prove an execution of the Noir program -Prove the valid execution of your Noir program using `bb`: +Using Barretenberg as an example, prove the valid execution of your Noir program running: ```sh -bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./proof +bb prove -b ./target/hello_world.json -w ./target/witness-name.gz -o ./target/proof ``` -A new file called `proof` will be generated in your project directory, containing the generated proof for your program. +The proof generated will then be written to the file `./target/proof`. -## Verify Our Noir Program +### 6. Verify the execution proof Once a proof is generated, we can verify correct execution of our Noir program by verifying the proof file. -Verify your proof by running: +Using Barretenberg as an example, compute the verification key for the Noir program by running: ```sh bb write_vk -b ./target/hello_world.json -o ./target/vk -bb verify -k ./target/vk -p ./proof ``` -The verification will complete in silence if it is successful. If it fails, it will log the corresponding error instead. +And verify your proof by running: + +```sh +bb verify -k ./target/vk -p ./target/proof +``` + +If successful, the verification will complete in silence; if unsuccessful, the command will trigger logging of the corresponding error. Congratulations, you have now created and verified a proof for your very first Noir program! diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md index 525b8dabdd84..96e653f6c08d 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/hello_noir/project_breakdown.md @@ -8,8 +8,7 @@ keywords: sidebar_position: 2 --- -This section breaks down our hello world program from the previous section. We elaborate on the project -structure and what the `prove` and `verify` commands did. +This section breaks down our hello world program from the previous section. ## Anatomy of a Nargo Project diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md index 4ef86aa59147..53ea9c7891ca 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/getting_started/installation/index.md @@ -19,11 +19,9 @@ keywords: [ pagination_next: getting_started/hello_noir/index --- -`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. +`nargo` is a tool for working with Noir programs on the CLI, providing you with the ability to start new projects, compile, execute and test Noir programs from the terminal. -With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. - -Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. +The name is inspired by Rust's package manager `cargo`; and similar to Rust's `rustup`, Noir also has an easy installation script `noirup`. ## Installing Noirup diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md index cbb1938a5c6f..8c23b639f123 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.32.0/tutorials/noirjs_app.md @@ -14,13 +14,13 @@ You can find the complete app code for this guide [here](https://github.com/noir :::note -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.27.x matches `noir_js@0.27.x`, etc. +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.31.x matches `noir_js@0.31.x`, etc. -In this guide, we will be pinned to 0.27.0. +In this guide, we will be pinned to 0.31.0. ::: -Before we start, we want to make sure we have Node and Nargo installed. +Before we start, we want to make sure we have Node, Nargo and the Barretenberg proving system (`bb`) installed. We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). @@ -30,6 +30,9 @@ As for `Nargo`, we can follow the [Nargo guide](../getting_started/installation/ curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash ``` +Follow the instructions on [this page](https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/cpp/src/barretenberg/bb#installation) to install `bb`. +Version 0.41.0 is compatible with `nargo` version 0.31.0, which you can install with `bbup -v 0.41.0` once `bbup` is installed. + Easy enough. Onwards! ## Our project @@ -42,13 +45,17 @@ In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! Run: -`nargo new circuit` +```bash +nargo new circuit +``` And... That's about it. Your program is ready to be compiled and run. To compile, let's `cd` into the `circuit` folder to enter our project, and call: -`nargo compile` +```bash +nargo compile +``` This compiles our circuit into `json` format and add it to a new `target` folder. @@ -92,30 +99,53 @@ Before we proceed with any coding, let's get our environment tailored for Noir. In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: ```javascript -import { defineConfig } from "vite"; -import copy from "rollup-plugin-copy"; - -export default defineConfig({ - esbuild: { - target: "esnext", - }, - optimizeDeps: { - esbuildOptions: { - target: "esnext", - }, +import { defineConfig } from 'vite'; +import copy from 'rollup-plugin-copy'; +import fs from 'fs'; +import path from 'path'; + +const wasmContentTypePlugin = { + name: 'wasm-content-type-plugin', + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + if (req.url.endsWith('.wasm')) { + res.setHeader('Content-Type', 'application/wasm'); + const newPath = req.url.replace('deps', 'dist'); + const targetPath = path.join(__dirname, newPath); + const wasmContent = fs.readFileSync(targetPath); + return res.end(wasmContent); + } + next(); + }); }, - plugins: [ - copy({ - targets: [ - { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, +}; + +export default defineConfig(({ command }) => { + if (command === 'serve') { + return { + build: { + target: 'esnext', + rollupOptions: { + external: ['@aztec/bb.js'] + } + }, + optimizeDeps: { + esbuildOptions: { + target: 'esnext' + } + }, + plugins: [ + copy({ + targets: [{ src: 'node_modules/**/*.wasm', dest: 'node_modules/.vite/dist' }], + copySync: true, + hook: 'buildStart', + }), + command === 'serve' ? wasmContentTypePlugin : [], ], - copySync: true, - hook: "buildStart", - }), - ], - server: { - port: 3000, - }, + }; + } + + return {}; }); ``` @@ -124,7 +154,7 @@ export default defineConfig({ Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: ```bash -npm install && npm install @noir-lang/backend_barretenberg@0.27.0 @noir-lang/noir_js@0.27.0 +npm install && npm install @noir-lang/backend_barretenberg@0.31.0 @noir-lang/noir_js@0.31.0 npm install rollup-plugin-copy --save-dev ``` @@ -193,17 +223,6 @@ Our love for Noir needs undivided attention, so let's just open `main.js` and de Start by pasting in this boilerplate code: ```js -const setup = async () => { - await Promise.all([ - import('@noir-lang/noirc_abi').then((module) => - module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), - ), - import('@noir-lang/acvm_js').then((module) => - module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), - ), - ]); -}; - function display(container, msg) { const c = document.getElementById(container); const p = document.createElement('p'); @@ -222,8 +241,6 @@ document.getElementById('submitGuess').addEventListener('click', async () => { The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 -As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. - :::info At this point in the tutorial, your folder structure should look like this: @@ -310,9 +327,13 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verificationKey = await backend.getVerificationKey(); -const verifier = new Verifier(); -const isValid = await verifier.verifyProof(proof, verificationKey); +const isValid = await backend.verifyProof(proof); + +// or to cache and use the verification key: +// const verificationKey = await backend.getVerificationKey(); +// const verifier = new Verifier(); +// const isValid = await verifier.verifyProof(proof, verificationKey); + if (isValid) display('logs', 'Verifying proof... ✅'); ``` diff --git a/noir/noir-repo/noir_stdlib/src/meta/mod.nr b/noir/noir-repo/noir_stdlib/src/meta/mod.nr index 395f09a453ec..351e128fa9a3 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/mod.nr @@ -1,5 +1,6 @@ mod trait_constraint; mod trait_def; +mod typ; mod type_def; mod quoted; @@ -9,3 +10,7 @@ mod quoted; pub comptime fn unquote(code: Quoted) -> Quoted { code } + +#[builtin(type_of)] +pub comptime fn type_of(x: T) -> Type {} + diff --git a/noir/noir-repo/noir_stdlib/src/meta/quoted.nr b/noir/noir-repo/noir_stdlib/src/meta/quoted.nr index 6273d64b10c5..8e96e8828f89 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/quoted.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/quoted.nr @@ -1,4 +1,7 @@ impl Quoted { #[builtin(quoted_as_trait_constraint)] fn as_trait_constraint(self) -> TraitConstraint {} + + #[builtin(quoted_as_type)] + fn as_type(self) -> Type {} } diff --git a/noir/noir-repo/noir_stdlib/src/meta/typ.nr b/noir/noir-repo/noir_stdlib/src/meta/typ.nr new file mode 100644 index 000000000000..197c41a482ca --- /dev/null +++ b/noir/noir-repo/noir_stdlib/src/meta/typ.nr @@ -0,0 +1,10 @@ +use crate::cmp::Eq; + +impl Eq for Type { + fn eq(self, other: Self) -> bool { + type_eq(self, other) + } +} + +#[builtin(type_eq)] +fn type_eq(_first: Type, _second: Type) -> bool {} diff --git a/noir/noir-repo/noir_stdlib/src/meta/type_def.nr b/noir/noir-repo/noir_stdlib/src/meta/type_def.nr index c01aab4b1413..7382aeb8e75d 100644 --- a/noir/noir-repo/noir_stdlib/src/meta/type_def.nr +++ b/noir/noir-repo/noir_stdlib/src/meta/type_def.nr @@ -2,7 +2,7 @@ impl StructDefinition { /// Return a syntactic version of this struct definition as a type. /// For example, `as_type(quote { type Foo { ... } })` would return `Foo` #[builtin(struct_def_as_type)] - fn as_type(self) -> Quoted {} + fn as_type(self) -> Type {} /// Return each generic on this struct. The names of these generics are unchanged /// so users may need to keep name collisions in mind if this is used directly in a macro. @@ -12,5 +12,5 @@ impl StructDefinition { /// Returns (name, type) pairs of each field in this struct. Each type is as-is /// with any generic arguments unchanged. #[builtin(struct_def_fields)] - fn fields(self) -> [(Quoted, Quoted)] {} + fn fields(self) -> [(Quoted, Type)] {} } diff --git a/noir/noir-repo/noir_stdlib/src/uint128.nr b/noir/noir-repo/noir_stdlib/src/uint128.nr index e99818bafa0d..7b75cf4cae4f 100644 --- a/noir/noir-repo/noir_stdlib/src/uint128.nr +++ b/noir/noir-repo/noir_stdlib/src/uint128.nr @@ -100,14 +100,14 @@ impl U128 { } fn decode_ascii(ascii: u8) -> Field { - if ascii < 58 { + (if ascii < 58 { ascii - 48 } else { let ascii = ascii + 32 * (U128::uconstrained_check_is_upper_ascii(ascii) as u8); assert(ascii >= 97); // enforce >= 'a' assert(ascii <= 102); // enforce <= 'f' ascii - 87 - } as Field + }) as Field } // TODO: Replace with a faster version. diff --git a/noir/noir-repo/scripts/install_bb.sh b/noir/noir-repo/scripts/install_bb.sh index 95dcfdda8800..65a449be5433 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.46.1" +VERSION="0.47.1" BBUP_PATH=~/.bb/bbup diff --git a/noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/Nargo.toml deleted file mode 100644 index 8bdefbbbd218..000000000000 --- a/noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "non_comptime_local_fn_call" -type = "bin" -authors = [""] -compiler_version = ">=0.23.0" - -[dependencies] diff --git a/noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/src/main.nr b/noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/src/main.nr deleted file mode 100644 index d75bb1a922af..000000000000 --- a/noir/noir-repo/test_programs/compile_failure/non_comptime_local_fn_call/src/main.nr +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - comptime { - let _a = id(3); - } -} - -fn id(x: Field) -> Field { - x -} diff --git a/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/Nargo.toml new file mode 100644 index 000000000000..ac7933fa250c --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "type_annotation_needed_on_struct_constructor" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/src/main.nr b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/src/main.nr new file mode 100644 index 000000000000..5207210dfbf3 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_constructor/src/main.nr @@ -0,0 +1,6 @@ +struct Foo { +} + +fn main() { + let foo = Foo {}; +} diff --git a/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/Nargo.toml b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/Nargo.toml new file mode 100644 index 000000000000..cb53d2924f42 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "type_annotation_needed_on_struct_new" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/src/main.nr b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/src/main.nr new file mode 100644 index 000000000000..f740dfa6d378 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_failure/type_annotation_needed_on_struct_new/src/main.nr @@ -0,0 +1,12 @@ +struct Foo { +} + +impl Foo { + fn new() -> Foo { + Foo {} + } +} + +fn main() { + let foo = Foo::new(); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/Nargo.toml new file mode 100644 index 000000000000..84162d3c0935 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_fmt_strings" +type = "bin" +authors = [""] +compiler_version = ">=0.32.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr new file mode 100644 index 000000000000..19572fd15a14 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_fmt_strings/src/main.nr @@ -0,0 +1,15 @@ +fn main() { + // format strings are lowered as normal strings + let (s1, s2): (str<39>, str<4>) = comptime { + let x = 4; + let y = 5; + + // Can't print these at compile-time here since printing to stdout while + // compiling breaks the test runner. + let s1 = f"x is {x}, fake interpolation: \{y}, y is {y}"; + let s2 = std::unsafe::zeroed::>(); + (s1, s2) + }; + assert_eq(s1, "x is 4, fake interpolation: {y}, y is 5"); + assert_eq(s2, "\0\0\0\0"); +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr index 5c99f8c587eb..2f2ca89cfb55 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_trait_constraint/src/main.nr @@ -24,16 +24,16 @@ fn main() { } } -comptime struct TestHasher { +struct TestHasher { result: Field, } -comptime impl Hasher for TestHasher { - comptime fn finish(self) -> Field { +impl Hasher for TestHasher { + fn finish(self) -> Field { self.result } - comptime fn write(&mut self, input: Field) { + fn write(&mut self, input: Field) { self.result += input; } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr index 8b1f81e65942..7d1e116dd0c2 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_traits/src/main.nr @@ -20,8 +20,8 @@ struct MyType { value: i32, } -comptime impl Neg for MyType { - comptime fn neg(self) -> Self { +impl Neg for MyType { + fn neg(self) -> Self { self } } diff --git a/noir/noir-repo/test_programs/compile_success_empty/comptime_type/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/Nargo.toml new file mode 100644 index 000000000000..c5b9ca892401 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "comptime_type" +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/comptime_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr new file mode 100644 index 000000000000..e74cc0a4d142 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/comptime_type/src/main.nr @@ -0,0 +1,16 @@ +use std::meta::type_of; + +fn main() { + comptime + { + // Check type_of works correctly (relies on Eq for Type) + let a_field = 0; + let another_field = 1; + let an_i32: i32 = 0; + let field_type_1 = type_of(a_field); + let field_type_2 = type_of(another_field); + let i32_type = type_of(an_i32); + assert(field_type_1 == field_type_2); + assert(field_type_1 != i32_type); + } +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr index 5463a61d9698..62b8a0421e63 100644 --- a/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr +++ b/noir/noir-repo/test_programs/compile_success_empty/derive_impl/src/main.nr @@ -27,7 +27,7 @@ struct Foo { #[derive_default] struct Bar {} -comptime fn make_field_exprs(fields: [(Quoted, Quoted)]) -> [Quoted] { +comptime fn make_field_exprs(fields: [(Quoted, Type)]) -> [Quoted] { let mut result = &[]; for my_field in fields { let name = my_field.0; diff --git a/noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/Nargo.toml new file mode 100644 index 000000000000..7d669ead363c --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "quoted_as_type" +type = "bin" +authors = [""] +compiler_version = ">=0.32.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/src/main.nr new file mode 100644 index 000000000000..e06294592ca4 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/quoted_as_type/src/main.nr @@ -0,0 +1,21 @@ +fn main() { + macro!().do_nothing(); +} + +comptime fn macro() -> Quoted { + let typ = quote { Foo }.as_type(); + quote { let foo: $typ = Foo {}; foo } +} + +struct Foo {} + +// Ensure we call the Foo impl +impl Foo { + fn do_nothing(_self: Self) { + assert(false); + } +} + +impl Foo { + fn do_nothing(_self: Self) {} +} diff --git a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/Nargo.toml similarity index 64% rename from noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml rename to noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/Nargo.toml index 8fce1bf44b6e..005fec5bf366 100644 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Nargo.toml +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/Nargo.toml @@ -1,5 +1,5 @@ [package] -name = "verify_honk_proof" +name = "trait_call_in_global" type = "bin" authors = [""] diff --git a/noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/src/main.nr new file mode 100644 index 000000000000..775cb5f3b7d6 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/trait_call_in_global/src/main.nr @@ -0,0 +1,5 @@ +global s: BoundedVec = From::from([0]); + +fn main() { + let _ = s; +} diff --git a/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/Nargo.toml b/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/Nargo.toml new file mode 100644 index 000000000000..650baead9e2b --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "zeroed_slice" +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/zeroed_slice/src/main.nr b/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr new file mode 100644 index 000000000000..44ccb2bd5959 --- /dev/null +++ b/noir/noir-repo/test_programs/compile_success_empty/zeroed_slice/src/main.nr @@ -0,0 +1,3 @@ +fn main() { + let _: [u8] = std::unsafe::zeroed(); +} diff --git a/noir/noir-repo/test_programs/execution_success/databus/src/main.nr b/noir/noir-repo/test_programs/execution_success/databus/src/main.nr index 7e5c23d508de..1e4aa141eead 100644 --- a/noir/noir-repo/test_programs/execution_success/databus/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/databus/src/main.nr @@ -1,4 +1,4 @@ -fn main(mut x: u32, y: call_data u32, z: call_data [u32; 4]) -> return_data u32 { +fn main(mut x: u32, y: call_data(0) u32, z: call_data(0) [u32; 4]) -> return_data u32 { let a = z[x]; a + foo(y) } diff --git a/noir/noir-repo/test_programs/execution_success/derive/Nargo.toml b/noir/noir-repo/test_programs/execution_success/derive/Nargo.toml new file mode 100644 index 000000000000..f38465943050 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/derive/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "derive" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/derive/src/main.nr b/noir/noir-repo/test_programs/execution_success/derive/src/main.nr new file mode 100644 index 000000000000..f226817fbaf8 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/derive/src/main.nr @@ -0,0 +1,51 @@ +use std::collections::umap::UHashMap; +use std::hash::BuildHasherDefault; +use std::hash::poseidon2::Poseidon2Hasher; + +#[my_derive(DoNothing)] +struct MyStruct { my_field: u32 } + +type DeriveFunction = fn(StructDefinition) -> Quoted; + +comptime mut global HANDLERS: UHashMap> = UHashMap::default(); + +comptime fn my_derive(s: StructDefinition, traits: [Quoted]) -> Quoted { + let mut result = quote {}; + + for trait_to_derive in traits { + let handler = HANDLERS.get(trait_to_derive.as_trait_constraint()); + assert(handler.is_some(), f"No derive function registered for `{trait_to_derive}`"); + + let trait_impl = handler.unwrap()(s); + result = quote { $result $trait_impl }; + } + + result +} + +unconstrained comptime fn my_derive_via(t: TraitDefinition, f: Quoted) { + HANDLERS.insert(t.as_trait_constraint(), std::meta::unquote!(f)); +} + +#[my_derive_via(derive_do_nothing)] +trait DoNothing { + fn do_nothing(self); +} + +comptime fn derive_do_nothing(s: StructDefinition) -> Quoted { + let typ = s.as_type(); + let generics = s.generics().join(quote {,}); + quote { + impl<$generics> DoNothing for $typ { + fn do_nothing(_self: Self) { + // Traits can't tell us what to do + println("something"); + } + } + } +} + +fn main() { + let s = MyStruct { my_field: 1 }; + s.do_nothing(); +} diff --git a/noir/noir-repo/test_programs/execution_success/regression_5615/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_5615/Nargo.toml new file mode 100644 index 000000000000..738d99391a22 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_5615/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_5615" +type = "bin" +authors = [""] +compiler_version = ">=0.32.0" + +[dependencies] diff --git a/noir/noir-repo/test_programs/execution_success/regression_5615/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_5615/src/main.nr new file mode 100644 index 000000000000..afb641e510de --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/regression_5615/src/main.nr @@ -0,0 +1,12 @@ +use std::collections::umap::UHashMap; +use std::hash::BuildHasherDefault; +use std::hash::poseidon2::Poseidon2Hasher; + +unconstrained fn main() { + comptime + { + let mut map: UHashMap> = UHashMap::default(); + + map.insert(1, 2); + } +} diff --git a/noir/noir-repo/test_programs/execution_success/slice_regex/Nargo.toml b/noir/noir-repo/test_programs/execution_success/slice_regex/Nargo.toml new file mode 100644 index 000000000000..ac95636c74a7 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/slice_regex/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "slice_regex" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr b/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr new file mode 100644 index 000000000000..43bd4433c694 --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/slice_regex/src/main.nr @@ -0,0 +1,811 @@ +struct Match { + succeeded: bool, + match_ends: u32, + leftover: [u8], +} + +impl Match { + fn empty(leftover: [u8]) -> Self { + Match { succeeded: true, match_ends: 0, leftover } + } +} + +impl Eq for Match { + fn eq(self, other: Self) -> bool { + (self.succeeded == other.succeeded) & + (self.match_ends == other.match_ends) + // (self.leftover == other.leftover) + } +} + +// TODO: load match into str and assert that it's the correct length +// impl From for str + +trait Regex { + fn match(self, input: [u8]) -> Match; +} + +// Empty +impl Regex for () { + fn match(_self: Self, input: [u8]) -> Match { + Match::empty(input) + } +} + +// Exact +impl Regex for str { + fn match(self, input: [u8]) -> Match { + let mut leftover = input; + let mut matches_input = true; + let self_as_bytes = self.as_bytes(); + for c in self_as_bytes { + if leftover.len() != 0 { + let (first_elem, popped_slice) = leftover.pop_front(); + leftover = popped_slice; + matches_input &= first_elem == c; + } else { + matches_input = false; + } + } + if matches_input { + Match { + succeeded: true, + match_ends: self_as_bytes.len(), + leftover, + } + } else { + Match { + succeeded: false, + match_ends: 0, + leftover: input, + } + } + } +} + +// And +impl Regex for (T, U) where T: Regex, U: Regex { + fn match(self, input: [u8]) -> Match { + let lhs_result = self.0.match(input); + if lhs_result.succeeded { + let rhs_result = self.1.match(lhs_result.leftover); + if rhs_result.succeeded { + Match { + succeeded: true, + match_ends: lhs_result.match_ends + rhs_result.match_ends, + leftover: rhs_result.leftover, + } + } else { + Match { + succeeded: false, + match_ends: 0, + leftover: input, + } + } + } else { + Match { + succeeded: false, + match_ends: 0, + leftover: input, + } + } + } +} + +// N T's: (T, (T, (T, T))) +struct Repeated { + inner: T, +} + +impl Regex for Repeated where T: Regex { + fn match(self, input: [u8]) -> Match { + let mut result = Match::empty(input); + for _ in 0..N { + if result.succeeded { + let next_result = self.inner.match(result.leftover); + result = Match { + succeeded: next_result.succeeded, + match_ends: result.match_ends + next_result.match_ends, + leftover: next_result.leftover, + }; + } + } + result + } +} + +struct Or { + lhs: T, + rhs: U, +} + +impl Regex for Or where T: Regex, U: Regex { + fn match(self, input: [u8]) -> Match { + let lhs_result = self.lhs.match(input); + if lhs_result.succeeded { + lhs_result + } else { + self.rhs.match(input) + } + } +} + +struct Question { + inner: T, +} + +impl Regex for Question where T: Regex { + fn match(self, input: [u8]) -> Match { + Or { + lhs: self.inner, + rhs: (), + }.match(input) + } +} + +// 0 <= num_matches <= N +struct Star { + inner: T, +} + +impl Regex for Star where T: Regex { + fn match(self, input: [u8]) -> Match { + let regex: Repeated<_, N> = Repeated { + inner: Question { inner: self.inner }, + }; + regex.match(input) + } +} + +// 0 < num_matches <= N +struct Plus { + inner: T, +} + +impl Regex for Plus where T: Regex { + fn match(self, input: [u8]) -> Match { + std::static_assert(N_PRED + 1 == N, "N - 1 != N_PRED"); + let star: Star = Star { inner: self.inner }; + ( + self.inner, + star + ).match(input) + } +} + +fn main() { + // gr(a|e)y + let graey_regex = ("gr", (Or { lhs: "a", rhs: "e" }, "y")); + + // NOTE: leftover ignored in Eq: Match + let result = graey_regex.match("gray".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 4, leftover: &[] }); + + // NOTE: leftover ignored in Eq: Match + let result = graey_regex.match("grey".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 4, leftover: &[] }); + + // colou?r + let colour_regex = ("colo", (Question { inner: "u" }, "r")); + + let result = colour_regex.match("color".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 5, leftover: &[] }); + + let result = colour_regex.match("colour".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 6, leftover: &[] }); + + // parse the empty string three times + // EMPTY{3} + let three_empties_regex: Repeated<(), 3> = Repeated { inner: () }; + + let result = three_empties_regex.match("111".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 0, leftover: &[] }); + + // 1{0} + let zero_ones_regex: Repeated, 0> = Repeated { inner: "1" }; + + let result = zero_ones_regex.match("111".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 0, leftover: &[] }); + + // 1{1} + let one_ones_regex: Repeated, 1> = Repeated { inner: "1" }; + + let result = one_ones_regex.match("111".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 1, leftover: &[] }); + + // 1{2} + let two_ones_regex: Repeated, 2> = Repeated { inner: "1" }; + + let result = two_ones_regex.match("111".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 2, leftover: &[] }); + + // 1{3} + let three_ones_regex: Repeated, 3> = Repeated { inner: "1" }; + + let result = three_ones_regex.match("1111".as_bytes().as_slice()); + println(result); + assert_eq(result, Match { succeeded: true, match_ends: 3, leftover: &[] }); + // TODO(https://github.com/noir-lang/noir/issues/5462): re-enable these cases and complete the test using array_regex below + // + // // 1* + // let ones_regex: Star, 5> = Star { inner: "1" }; + // + // let result = ones_regex.match("11000".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 2, leftover: &[] }); + // + // let result = ones_regex.match("11".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 2, leftover: &[] }); + // + // let result = ones_regex.match("111111".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 5, leftover: &[] }); + // + // + // // 1+ + // let nonempty_ones_regex: Plus, 5, 4> = Plus { inner: "1" }; + // + // let result = nonempty_ones_regex.match("111111".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 5, leftover: &[] }); + // + // // 2^n-1 in binary: 1+0 + // let pred_pow_two_regex = (nonempty_ones_regex, "0"); + // + // let result = pred_pow_two_regex.match("1110".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 3, leftover: &[] }); + // + // // (0|1)* + // let binary_regex: Star, str<1>>, 5> = Star { inner: Or { lhs: "0", rhs: "1" } }; + // + // let result = binary_regex.match("110100".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 5, leftover: &[] }); + // + // // even numbers in binary: 1(0|1)*0 + // let even_binary_regex = ("1", (binary_regex, "0")); + // + // let result = even_binary_regex.match("1111110".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { succeeded: true, match_ends: 6, leftover: &[] }); + // 2-letter capitalized words: [A-Z][a-z] + // numbers: \d+ + // [0-9]+ + // words: \w+ + // [a-Z]+ + // adapted URL parser: (https?:\/\/)?([\da-z.\-]+)\.([a-z.]+)([\/\w \.\-]*)*\/? + // // panics (at compile time) when input string is too short + // let foo_regex = ( + // "colo", + // ( + // Question { + // inner: "u", + // }, + // "r" + // ) + // ); + // + // let result = foo_regex.match("colo".as_bytes().as_slice()); + // println(result); + // assert_eq(result, Match { + // succeeded: true, + // match_ends: 4, + // leftover: &[], + // }); +} + +// array_regex: use to complete test once https://github.com/noir-lang/noir/issues/5462 is resolved +// +// // offset <= len <= N +// struct Bvec { +// inner: [T; N], +// +// // elements at indices < offset are zero +// offset: u32, +// +// // elements at indices >= len are zero +// len: u32, +// } +// +// impl Eq for Bvec where T: Eq { +// fn eq(self, other: Self) -> bool { +// (self.inner == other.inner) & +// (self.offset == other.offset) & +// (self.len == other.len) +// } +// } +// +// impl Bvec { +// fn empty() -> Self { +// Self { inner: [std::unsafe::zeroed(); N], offset: 0, len: 0 } +// } +// +// fn new(array: [T; N]) -> Self { +// let mut result = Bvec::empty(); +// for x in array { +// result = result.push(x); +// } +// result +// } +// +// // pushing when len == N is a no-op +// fn push(self, x: T) -> Self { +// let mut inner = self.inner; +// let mut len = self.len; +// if self.len < N { +// inner[self.len] = x; +// len += 1; +// } +// +// Self { inner, offset: self.offset, len } +// } +// +// fn pop_front(self) -> (T, Self) { +// assert(self.offset <= self.inner.len()); +// assert(self.len != 0); +// +// let first_elem = self.inner[self.offset]; +// let popped_slice = Self { inner: self.inner, offset: self.offset + 1, len: self.len - 1 }; +// +// (first_elem, popped_slice) +// } +// } +// +// struct Match { +// succeeded: bool, +// match_ends: u32, +// leftover: Bvec, +// } +// +// impl Match { +// fn empty(leftover: Bvec) -> Self { +// Match { succeeded: true, match_ends: 0, leftover } +// } +// +// fn failed(leftover: Bvec) -> Self { +// Match { succeeded: false, match_ends: 0, leftover } +// } +// } +// +// impl Eq for Match { +// fn eq(self, other: Self) -> bool { +// (self.succeeded == other.succeeded) & +// (self.match_ends == other.match_ends) & +// (self.leftover == other.leftover) +// } +// } +// +// // TODO: load match into str and assert that it's the correct length +// // impl From for str +// +// trait Regex { +// // Perform a match without backtracking +// fn match(self, input: Bvec) -> Match; +// } +// +// // Empty +// impl Regex for () { +// fn match(_self: Self, input: Bvec) -> Match { +// Match::empty(input) +// } +// } +// +// // Exact +// impl Regex for str { +// fn match(self, input: Bvec) -> Match { +// let mut leftover = input; +// let mut matches_input = true; +// let self_as_bytes = self.as_bytes(); +// for c in self_as_bytes { +// if leftover.len != 0 { +// let (first_elem, popped_slice) = leftover.pop_front(); +// leftover = popped_slice; +// matches_input &= first_elem == c; +// } else { +// matches_input = false; +// } +// } +// if matches_input { +// Match { +// succeeded: true, +// match_ends: self_as_bytes.len(), +// leftover, +// } +// } else { +// Match { +// succeeded: false, +// match_ends: 0, +// leftover: input, +// } +// } +// } +// } +// +// // And +// impl Regex for (T, U) where T: Regex, U: Regex { +// fn match(self, input: Bvec) -> Match { +// let lhs_result = self.0.match(input); +// if lhs_result.succeeded { +// let rhs_result = self.1.match(lhs_result.leftover); +// if rhs_result.succeeded { +// Match { +// succeeded: true, +// match_ends: lhs_result.match_ends + rhs_result.match_ends, +// leftover: rhs_result.leftover, +// } +// } else { +// Match { +// succeeded: false, +// match_ends: 0, +// leftover: input, +// } +// } +// } else { +// Match { +// succeeded: false, +// match_ends: 0, +// leftover: input, +// } +// } +// } +// } +// +// // N T's: (T, (T, (T, T))) +// struct Repeated { +// inner: T, +// } +// +// impl Regex for Repeated where T: Regex { +// fn match(self, input: Bvec) -> Match { +// let mut result = Match::empty(input); +// for _ in 0..M { +// if result.succeeded { +// let next_result = self.inner.match(result.leftover); +// result = Match { +// succeeded: next_result.succeeded, +// match_ends: result.match_ends + next_result.match_ends, +// leftover: next_result.leftover, +// }; +// } +// } +// result +// } +// } +// +// struct Or { +// lhs: T, +// rhs: U, +// } +// +// impl Regex for Or where T: Regex, U: Regex { +// fn match(self, input: Bvec) -> Match { +// let lhs_result = self.lhs.match(input); +// if lhs_result.succeeded { +// lhs_result +// } else { +// self.rhs.match(input) +// } +// } +// } +// +// struct Question { +// inner: T, +// } +// +// impl Regex for Question where T: Regex { +// fn match(self, input: Bvec) -> Match { +// Or { +// lhs: self.inner, +// rhs: (), +// }.match(input) +// } +// } +// +// // 0 <= num_matches <= N +// struct Star { +// inner: T, +// } +// +// impl Regex for Star where T: Regex { +// fn match(self, input: Bvec) -> Match { +// let regex: Repeated<_, M> = Repeated { +// inner: Question { inner: self.inner }, +// }; +// regex.match(input) +// } +// } +// +// // 0 < num_matches <= N +// struct Plus { +// inner: T, +// } +// +// impl Regex for Plus where T: Regex { +// fn match(self, input: Bvec) -> Match { +// std::static_assert(M_PRED + 1 == M, "M - 1 != M_PRED"); +// let star: Star = Star { inner: self.inner }; +// ( +// self.inner, +// star +// ).match(input) +// } +// } +// +// // Repeated is to (,) as AnyOf is to Or +// struct AnyOf { +// inner: [T; N], +// } +// +// impl Regex for AnyOf where T: Regex { +// fn match(self, input: Bvec) -> Match { +// let mut result = Match::failed(input); +// for i in 0..M { +// if !result.succeeded { +// result = self.inner[i].match(result.leftover); +// } +// } +// result +// } +// } +// +// fn reverse_array(input: [T; N]) -> [T; N] { +// let mut output = [std::unsafe::zeroed(); N]; +// for i in 0..N { +// output[i] = input[N - (i + 1)]; +// } +// output +// } +// +// fn main() { +// assert_eq(reverse_array([1, 2, 3, 4]), [4, 3, 2, 1]); +// +// let mut xs: Bvec = Bvec::empty(); +// +// xs = xs.push(0); +// assert_eq(xs, Bvec { inner: [0, 0, 0], offset: 0, len: 1 }); +// +// xs = xs.push(1); +// assert_eq(xs, Bvec { inner: [0, 1, 0], offset: 0, len: 2 }); +// +// xs = xs.push(2); +// assert_eq(xs, Bvec { inner: [0, 1, 2], offset: 0, len: 3 }); +// +// xs = xs.push(3); +// assert_eq(xs, Bvec { inner: [0, 1, 2], offset: 0, len: 3 }); +// +// let ys = Bvec::new([0, 1, 2]); +// assert_eq(xs, ys); +// +// // test that pop_front gives all contents, in order, +// // followed by std::unsafe::zeroed() +// println(xs); +// let (x, new_xs) = xs.pop_front(); +// assert_eq(x, 0); +// +// xs = new_xs; +// println(xs); +// let (x, new_xs) = xs.pop_front(); +// assert_eq(x, 1); +// +// xs = new_xs; +// println(xs); +// let (x, new_xs) = xs.pop_front(); +// assert_eq(x, 2); +// +// xs = new_xs; +// println(xs); +// if xs.len != 0 { +// let (x, _new_xs) = xs.pop_front(); +// assert_eq(x, std::unsafe::zeroed()); +// } +// +// assert_eq(new_xs, Bvec { inner: [0, 1, 2], offset: 3, len: 0 }); +// +// // gr(a|e)y +// let graey_regex = ("gr", (Or { lhs: "a", rhs: "e" }, "y")); +// +// let result = graey_regex.match(Bvec::new("gray".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 4); +// assert_eq(result.leftover.len, 0); +// +// let result = graey_regex.match(Bvec::new("grey".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 4); +// assert_eq(result.leftover.len, 0); +// +// // colou?r +// let colour_regex = ("colo", (Question { inner: "u" }, "r")); +// +// let result = colour_regex.match(Bvec::new("color".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 5); +// assert_eq(result.leftover.len, 0); +// +// let result = colour_regex.match(Bvec::new("colour".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 6); +// assert_eq(result.leftover.len, 0); +// +// // parse the empty string three times +// // EMPTY{3} +// let three_empties_regex: Repeated<(), 3> = Repeated { inner: () }; +// +// let result = three_empties_regex.match(Bvec::new("111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 0); +// assert_eq(result.leftover.len, 3); +// +// // 1{0} +// let zero_ones_regex: Repeated, 0> = Repeated { inner: "1" }; +// +// let result = zero_ones_regex.match(Bvec::new("111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 0); +// assert_eq(result.leftover.len, 3); +// +// // 1{1} +// let one_ones_regex: Repeated, 1> = Repeated { inner: "1" }; +// +// let result = one_ones_regex.match(Bvec::new("111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 1); +// assert_eq(result.leftover.len, 2); +// +// // 1{2} +// let two_ones_regex: Repeated, 2> = Repeated { inner: "1" }; +// +// let result = two_ones_regex.match(Bvec::new("111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 2); +// assert_eq(result.leftover.len, 1); +// +// // 1{3} +// let three_ones_regex: Repeated, 3> = Repeated { inner: "1" }; +// +// let result = three_ones_regex.match(Bvec::new("1111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 3); +// assert_eq(result.leftover.len, 1); +// +// // 1* +// let ones_regex: Star, 5> = Star { inner: "1" }; +// +// let result = ones_regex.match(Bvec::new("11000".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 2); +// assert_eq(result.leftover.len, 3); +// +// let result = ones_regex.match(Bvec::new("11".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 2); +// assert_eq(result.leftover.len, 0); +// +// let result = ones_regex.match(Bvec::new("111111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 5); +// assert_eq(result.leftover.len, 1); +// +// // 1+ +// let nonempty_ones_regex: Plus, 5, 4> = Plus { inner: "1" }; +// +// let result = nonempty_ones_regex.match(Bvec::new("111111".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 5); +// assert_eq(result.leftover.len, 1); +// +// // 2^n-1 in binary: 1+0 +// let pred_pow_two_regex = (nonempty_ones_regex, "0"); +// +// let result = pred_pow_two_regex.match(Bvec::new("1110".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 4); +// assert_eq(result.leftover.len, 0); +// +// // (0|1)* +// let binary_regex: Star, str<1>>, 5> = Star { inner: Or { lhs: "0", rhs: "1" } }; +// +// let result = binary_regex.match(Bvec::new("110100".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 5); +// assert_eq(result.leftover.len, 1); +// +// // even numbers in binary: 1(0|1)*0 +// let even_binary_regex = ("1", (binary_regex, "0")); +// +// let result = even_binary_regex.match(Bvec::new("1111110".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 7); +// assert_eq(result.leftover.len, 0); +// +// // digit: \d+ +// // [0-9] +// let digit_regex = AnyOf { +// inner: [ +// "0", +// "1", +// "2", +// "3", +// "4", +// "5", +// "6", +// "7", +// "8", +// "9" +// ] +// }; +// +// let result = digit_regex.match(Bvec::new("157196345823795".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 1); +// assert_eq(result.leftover.len, 14); +// +// let result = digit_regex.match(Bvec::new("hi".as_bytes())); +// println(result); +// assert(!result.succeeded); +// assert_eq(result.match_ends, 0); +// assert_eq(result.leftover.len, 2); +// +// // digits: \d+ +// // [0-9]+ +// let digits_regex: Plus, 10>, 32, 31> = Plus { inner: digit_regex }; +// +// let result = digits_regex.match(Bvec::new("123456789012345".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 15); +// assert_eq(result.leftover.len, 0); +// +// let result = digits_regex.match(Bvec::new("123456789012345 then words".as_bytes())); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 15); +// assert_eq(result.leftover.len, 11); +// +// // multiples of 10 +// // apply to a reversed input string (because there isn't backtracking) +// // 0\d+ +// let backwards_mult_of_10_regex = ("0", digits_regex); +// +// let result = backwards_mult_of_10_regex.match(Bvec::new(reverse_array("1230".as_bytes()))); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 4); +// assert_eq(result.leftover.len, 0); +// +// let ten_pow_16: str<17> = "10000000000000000"; +// let result = backwards_mult_of_10_regex.match(Bvec::new(reverse_array(ten_pow_16.as_bytes()))); +// println(result); +// assert(result.succeeded); +// assert_eq(result.match_ends, 17); +// assert_eq(result.leftover.len, 0); +// // adapted URL parser: (https?:\/\/)?([\da-c.\-]+)\.([a-c.]+)([\/\w \.\-]*)*\/? +// } + 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 fc5e6002dbfd..000000000000 --- a/noir/noir-repo/test_programs/execution_success/verify_honk_proof/Prover.toml +++ /dev/null @@ -1,4 +0,0 @@ -key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" -proof = ["0x0000000000000000000000000000000000000000000000000000000000000010","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000001","0x00000000000000000000000000000079ea57b3d7247e1b84fc1ab449de746345","0x000000000000000000000000000000000023fb17d477c91e0fb057233a66ef2a","0x000000000000000000000000000000146353d3faf24455819947aa0a25868174","0x00000000000000000000000000000000000093b1c637419c9f016bb0261cdfc6","0x000000000000000000000000000000325b128a84544d31fa1c577232c742b574","0x00000000000000000000000000000000002b3db93a2fca4c31308471d4f55fa2","0x00000000000000000000000000000054d9d87932eee6280c37d802ec8d47ca02","0x000000000000000000000000000000000000397167bb1e36d061487e93e4d97e","0x000000000000000000000000000000143b0960a1b9f19a44ad1cf2b7059832d6","0x0000000000000000000000000000000000158446576b2d43f78b48799ff7e760","0x000000000000000000000000000000cf640bad8ccc1890d738ab917d6caa957e","0x00000000000000000000000000000000001d6fd185d8771b864545438c6a1d68","0x000000000000000000000000000000a33cd928d0d4c7f244824b63b15f4c5423","0x00000000000000000000000000000000000433ccd872d2a302104048474e0bea","0x000000000000000000000000000000eaf7d13e5e9706e1b8a9343bd493a060af","0x00000000000000000000000000000000001a062842ba351b311ae52693f5114e","0x000000000000000000000000000000a33cd928d0d4c7f244824b63b15f4c5423","0x00000000000000000000000000000000000433ccd872d2a302104048474e0bea","0x000000000000000000000000000000eaf7d13e5e9706e1b8a9343bd493a060af","0x00000000000000000000000000000000001a062842ba351b311ae52693f5114e","0x000000000000000000000000000000160d90f214f524875c01cb9cf0f2d272b9","0x000000000000000000000000000000000015d5f906c4fe06017b0f9824434d09","0x0000000000000000000000000000007fc2db3cfe49b7666aeafd8cf6973c9fed","0x00000000000000000000000000000000000c7fc1e545a8ee19a7bc6ad6f2ea47","0x000000000000000000000000000000fc3c9df244afbba117cd897a4c929edb84","0x0000000000000000000000000000000000216f0c3a2e5e8683d9717ad40eadde","0x000000000000000000000000000000c381b45048aa5163e0129e4031e29058cb","0x00000000000000000000000000000000002f11022de88492201c28f87582684d","0x000000000000000000000000000000c98462e08c423124d92a41110c378db160","0x00000000000000000000000000000000000106dafb059575ec9b926aa90edfef","0x0000000000000000000000000000007d0cc0465628f6b0f3918aa9d7cf33ff38","0x00000000000000000000000000000000002cff01344fc7c6f81399b7ae660ad4","0x07eff01a06f356d255515e5f27cb51e8873277beb3f986c215181b475df4dd8e","0x28745e58da3e495762fee75759b60674a1017089c5bfe9cf2ec9da4c920b2273","0x1d5b7b751e509ac70caa253595be4523d1963cf7bd6751d2c146e2fc10d00196","0x26fe27f73b55be7d49b4c1c11f085f47f6a241ba5ea0d48b47964e7adf5e8e5a","0x239206c519de2576a554a70f387cdf5d525a599541be2ecd9260e52d572ae07c","0x04e35b29a57c31c89c72a6387bf89613b64c2827e0c2402b8dfb2c1cfea0c878","0x1e8398c5dd85d15154110c2480f2249030aecd7595242ae86bbdf7b2730ca070","0x2ba9986a038e85a4dd96badffb6a44950c37360fd6e8ec6c4b9647377bcb45f5","0x27ca7a06ceea23d329c52dac8c0715440238d37362ab0fb1e26544b18bb79a3b","0x23b768d51fa7922f8292309455adc5730b8964818c328a42dff60a57add32f50","0x24e8634d5381475abe5821450299d9d8d725a472610fe265e44c8360c4708c95","0x0cdbb73fe5c035427113e66a15b8c41e963ae215e491d855a3ce8c3ab200fb3b","0x0e8acd2ed6af85e4f71b96c51d2a57bceea5c50fb405b7888359f0635b415da7","0x2914cc0244acf5ac6d674d3c96d543ee2f3e95d0248ee66daf0cf2932107e300","0x00ff0384250d2c2e59cd1cf58cebd1d3b1ebab7989eb2eaa6b6bbce69f9e8ba0","0x253f7a5007d47d3d858fc0e172c971cb54f97cea5c63ca60efe61589913b2499","0x2d34704fc711dabe0f716dbebc5dfd0eaa5667006847d333dadc86e15bf672c0","0x0bdd67ff40c61242e46a234c0d438663a9ccae833d1e0b22833ffe41e2828bb4","0x04c7ba2edccfb340eba0c94a7a5d5d53b010939621053c7c0fd27f2ba4b08273","0x0c3f68e6de8042a10098596e80ea79882b37d22c6a6adaa64f5c668739932fa5","0x14bcb10845b45cb8fdcac13e41ad755f6d966756ee2f3d4ed8a5791d4b345ea8","0x0dd68c1e3d122d4d4b28a8ac7e6a592146afe70e3852906c27ccc7e345f745e2","0x06816aff04192007cb2b3ed2cee4b22e044ced0199b136942348ced61990c1a7","0x3013f13664687bc3cbe26314f17cf309486ef71ffb55ce2589075554fc31ee69","0x1941a602d47af0e52f06a272998b6a59313f316508c0778714a36d7bb4f5669b","0x268750f15f2ac995d1d59859b7d636ae814e751b84318218ac1ce75a14b00e18","0x2aaff14fd98aa13ffdf34e3f689e16b2e8cb7695db9a014dd270b021968e3bb2","0x090087ad0d688396823bbd90a8770c1101e5907efd1c4fbafff8a1e9f2f84d89","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x152deae3a77021b0201a74d98b30d842baea62c0d2531d69d5e866e59f48e052","0x084acb08dc53880864266b6dea02ec7a85ffab2ab590ba9a4adb32ad2c8ffe53","0x1b8ab1a2e47a839fdbf19d2cbea2abe79c57722270123cf96289a11e233cd175","0x03493f800f9abbe4e405f0f637f41f22dcc10e44e836a09115ed5821cd5856e6","0x24c358e686e47c512bbec4a1b9ac562c251e004ea142df44ea3b18cf214baa47","0x18296076ac89be1c4c24a04553be7bd07bba5a41d1c33de2bec14cfd1262ab9f","0x0e30341606dc2577a451251241394b3871e9db0e1758d250d36348bcbb8b6fdb","0x15f846978484540ac3c35eee38ccd980f01e8bda6050a645c4abca6f22b24619","0x2735dd2b603cde2937bf842002e8704ef1e3883d2d0a6e999dd7015496c10302","0x23c47d9891d04bdb88ca239119e423afdc6d2bd45fb92f5f19b8b0a9583fc863","0x1ce47f9088eecc7268d4558aa02a4902282bccaacbe882917cc57969af2236d0","0x2b5a6f937fcc921cced568de248e19fd3801e688505ee44af6499e14885c9879","0x2ae2f654890e7018bae8308b5a97230cdcd3b63b24334a05dc4fdc4107cff73d","0x06a87313997c2a5318a8ce0f75e26b9c4a2a83bd9c3578f10d1c1f3bfded8f29","0x0afe95fddb76f390d58e15b7e647e9ed083a66aa7829a18963125d865b64ef7f","0x1ff7ecaf04f4e8a9d57f79c85dd963099f6005f542df7c20505af69061473114","0x26ca489f39024294da78a601feda0a17c40d46e2c7d0787b47dc0afaf027a8c8","0x2da37034033c950b2f85c32be2b0f1102dae5ec01e13681ffc9a9a3033469a8d","0x22c35dc92f5bf1cb569ad756b45208ffa8a85d825ebacf8e7354e9162651d1fa","0x0e443f72c90fec92786098f7ec90cea01f6202db6998b34dbb1e7b0293f4bebd","0x049684508bb0af0f27bcaaf96aa53eac25a425e159eb33e031db157d63c22fb9","0x20d990716bfec57f52f603d50d0d81c4c851bfc231894eb573fa54f2ac70c9dd","0x1fd19e900621d01488be88d4a6d95c2583c19c6d1d49e8cd139bce76051b71bc","0x1679a31a104b20b301737b9214f12a0707727bd4510d5a53e5bec1321816cdfa","0x27b3d8000581372f35039477c28a268065b3717dbd9337c06a82162781e0d131","0x23b79b53bdb698ef8c7c01afaf3350deb78b5e841e09b13b6ef86fc68f97bcab","0x1d4abc42698589c40b05e187c12af268fffe64010756a8d08ea817105305a770","0x0f744ca06905efa1f604f387284979be483c00ee9298134e7337bd8bb4a88933","0x0be6790122704c6ed4c37fef0b524b413e63b88c2dadbe67a5ba039cf11cc628","0x19fa34479d41d734a17619048627633807d706b2b1035a326efada8f3e8eb183","0x1b208f5cc663a9560e8685c351cb17b8e5862eb16f1407cf654e8ffae331aa9b","0x1b140725b61fe2e1057d72525aecf1d319ecb509a392f68e4058d13cea209993","0x1b140725b61fe2e1057d72525aecf1d319ecb509a392f68e4058d13cea209993","0x0d1703eac9b276094d72a50322dd82033960a6f1f6176aa2b029e39a1375bb51","0x09ba2a48cfdcc27f6b6db2ca277c5016d4f5a177e65eec6f68e30a67d4b06c1b","0x0e243bf8b2d6b8e46ed75902fe60781b2b41cf45287f367df850ce50de7f86af","0x1be244289270e4c0dc8517edfe335954fa7b56c3bf6fe06bc2d268f7db7a68ee","0x116ef1bfcfbca0612c92872aa3d07d32cb0b9716b1ba735846888a56e03c6207","0x0de8a7471ceb058680d2e0afa73e3dd843b527db1c16ebfaf8612447ffbee858","0x16911fee4120f72d81b0dfb0eeeb7380611119ee990daec5669b711cb35e2756","0x1c278b26a16e1ee7e21a71b67a31cb0b9907dae80776aa1dc7094ea5b4e2c34e","0x0f5c67db668b1f1880c51f805ec3d40aa27d34b4c8833f755d4466c285264399","0x000000000000000000000000000000dc2546d68fbe5a4913dde8ed73f673bc5f","0x00000000000000000000000000000000001310657525d78319e5b15c92398dcf","0x0000000000000000000000000000000fde9a035776897ed560b4d9ae338b5f85","0x00000000000000000000000000000000000f84fecfb3ea28426f114d9de93cb3","0x000000000000000000000000000000d3ea685110f3ff69bf91cc32cc5170b62e","0x0000000000000000000000000000000000179205f5ebaf3eaf5d50be462f830d","0x00000000000000000000000000000024a7284c15d725d62b8f5c1090b08b58b7","0x00000000000000000000000000000000002b6fdb2139f7b9443cbd82e6423486","0x00000000000000000000000000000006489f49eed3370ee31c80590eed2d0c3a","0x000000000000000000000000000000000010c11c3a122e00a12e0cf7a58d81ae","0x000000000000000000000000000000eb2d1eef7e7c7c0c054859600d264176e9","0x000000000000000000000000000000000028ac3239a0917c7c3761e11fbf9541","0x0000000000000000000000000000006ecbe6a2ccf0c9e1b743a84e1540796b81","0x0000000000000000000000000000000000098a99a81cbc111660301a03f77d96","0x000000000000000000000000000000c4f256019891f39b00b1b00428b3a154a5","0x00000000000000000000000000000000001bc2f83790ff1d3086273e4560135c","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","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x000000000000000000000000000000868795ebcbf38bffa96f455a314c7b9310","0x00000000000000000000000000000000002e43e0a550d7cce874e869ed0ef545","0x0000000000000000000000000000001e5a780edfd01526758b69bfaf25803f67","0x00000000000000000000000000000000000f0991f4b5dc348354f019ecc66502","0x000000000000000000000000000000cb917b7819afd60fc86ea477594ffca008","0x000000000000000000000000000000000002beaa7c144fc6620870e72ee8064c","0x000000000000000000000000000000b7f4dfed23506dadd1726a896e226d7a34","0x00000000000000000000000000000000001bb28f2fcfb40843aa5f5e38d689e1"] -public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] -verification_key = ["0x0000000000000000000000000000000000000000000000000000000000000010","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000008c068dccb0e55d4b56c7c32ebfafeb5b02","0x0000000000000000000000000000000000266c985199590a284abf77ba01c36e","0x00000000000000000000000000000044fb25551548bb4059099673aed8646b35","0x000000000000000000000000000000000023ab8c745db114ee56b97a8aa27934","0x000000000000000000000000000000a56563b42599f0e4ad5455a8fd988b9ff3","0x00000000000000000000000000000000000a94e3640063f3f4758fcfe457d298","0x0000000000000000000000000000008a51861ca043ceae044d6ab4f136331514","0x00000000000000000000000000000000001812e50744ac8ed3cc4a9594a701cc","0x000000000000000000000000000000b911c8cf179747b410c1cb9fd8a8bde095","0x00000000000000000000000000000000001826edb9faf624498fe82f5a61008d","0x000000000000000000000000000000ed158ea534a9c72ec9e614906fd7adff9a","0x000000000000000000000000000000000017cb9637e464dc2647b9b8688c5fa0","0x0000000000000000000000000000004b5064dd55e5ec8cd9bdd01c0e22eb7122","0x00000000000000000000000000000000002c7cff0caa8ba3fec7523dcbc934a8","0x000000000000000000000000000000f268df76bf0d78739ded43daba9c339499","0x00000000000000000000000000000000002e11974b75c78b276ae16219b99dc9","0x000000000000000000000000000000cfc293980c0ecf813f4f1436ff140740c3","0x000000000000000000000000000000000016ff2972a7eedf8ff27f494904fa47","0x00000000000000000000000000000085a92cc2b6efec726ea10710b20776ee70","0x0000000000000000000000000000000000278709e98b64a3553dc3e6e514e7ff","0x0000000000000000000000000000004391d81714b7d7ad40642b9308d02258b4","0x0000000000000000000000000000000000207710f769c857fbe624a2333097b2","0x0000000000000000000000000000002f767ee4790206ca5c193b742aa672d6d8","0x00000000000000000000000000000000001044cdbbd63806d10426ca4cb77cbc","0x000000000000000000000000000000314be7aecd2a710b8966befe7c0b08f574","0x00000000000000000000000000000000000558190b4fa7d726895b6d7d9c0bef","0x000000000000000000000000000000d64f3a11faf61b8776b0e778ab7a16c09c","0x00000000000000000000000000000000000d1c3d5e8fe0193b17834424ce605d","0x000000000000000000000000000000d8019ded441b9e454eb4045069cefee487","0x00000000000000000000000000000000002c066d46d386975a57df073e19403b","0x0000000000000000000000000000006bf779063abc501d4102fbfc99d4227c16","0x00000000000000000000000000000000001bbf8b9e8c4b2184984b994c744d21","0x0000000000000000000000000000003896ea793e6b3f6a14218d476534109610","0x00000000000000000000000000000000000e84090add56f2500ab518c655cae6","0x00000000000000000000000000000065df446fdddba972f3c4414ad3c901f4f9","0x00000000000000000000000000000000002b78a584bd6ae88cf4ec7c65c90e0b","0x00000000000000000000000000000094e611b5d59a27773f744710b476fbd30f","0x00000000000000000000000000000000001bd6129f9646aa21af0d77e7b1cc97","0x000000000000000000000000000000139a9d1593d56e65e710b2f344756b721e","0x00000000000000000000000000000000002f8d492d76a22b6834f0b88e2d4096","0x00000000000000000000000000000026c814cd7c5e1ba2094969bb1d74f1c66b","0x000000000000000000000000000000000013129f0714c3307644809495e01504","0x0000000000000000000000000000007d4549a4df958fe4825e7cb590563154ab","0x00000000000000000000000000000000000e7d5873232b1bdd0ce181513b47d1","0x000000000000000000000000000000a54541a8f32c0d9f8645edf17aac8fa230","0x00000000000000000000000000000000001e0677756494ded8010e8ef02518b2","0x0000000000000000000000000000008b101700e2d4f9116b01bfaaf3c458a423","0x0000000000000000000000000000000000021e43a3c385eba62bcc47aad7b9ea","0x00000000000000000000000000000099559d1c1ed6758494d18b9890bb5e3f97","0x00000000000000000000000000000000002e68b3c679543d2933bf9f7f77d422","0x000000000000000000000000000000c842dceb89f5cf4c130810f4802014a67f","0x00000000000000000000000000000000000d647daa6d2a8ac14f2da194b3a27e","0x000000000000000000000000000000af641be24f11d735581ad2e14787470194","0x00000000000000000000000000000000001e90f381ece8401026212fdbb26199","0x000000000000000000000000000000f601a4b716e755b0cf516d07e403265e27","0x00000000000000000000000000000000002d49d628876caa6993afe9fc30a764","0x0000000000000000000000000000008e9de4c6ce2e85105ec90ab63303b61502","0x00000000000000000000000000000000001b063563a7858b064132573e0aca86","0x00000000000000000000000000000021c200c8468139aa32fcf13fd1d8570828","0x0000000000000000000000000000000000023a4e744c62548c3b32986b3bc73a","0x0000000000000000000000000000000af941f79a4d93c6e9aad19c6049e1fa53","0x000000000000000000000000000000000003db2201f4b1b9a4d3646331e1f8e1","0x00000000000000000000000000000005d91fe16bd2b8dd3ce8b7d70ce6222b4f","0x0000000000000000000000000000000000102db0f3fd668e06f49d133d1bf994","0x0000000000000000000000000000009459915944c39a12b978a433efb6517d0f","0x00000000000000000000000000000000000b1c9fa9f4ce17e53f3acd13be4078","0x0000000000000000000000000000007c8d45be92476f8867dca4078fb7b6b2f8","0x00000000000000000000000000000000001f21afb9b7ccd5c404f0115253d2a6","0x0000000000000000000000000000004d78a34b40208c31be4fb8b39d23f1d1de","0x00000000000000000000000000000000000f3090488b19df76c4358537728d9a","0x00000000000000000000000000000060b0272756debcae50a25a3ee7d7095ea9","0x00000000000000000000000000000000002e84bca0d93b098853cca06147ec94","0x000000000000000000000000000000a0875603e0a017ce12ff79764af43e7421","0x0000000000000000000000000000000000245798a7b19502ba14b46eb68dc771","0x00000000000000000000000000000089b25e854077925674d0645ed1e784c929","0x000000000000000000000000000000000008b8347d14433adba1d9e9406eb1db","0x000000000000000000000000000000d0d3258758dfa9bae9e415f6d48d990e16","0x0000000000000000000000000000000000224948ddbcddb1e360efa2ac511aac","0x000000000000000000000000000000f6a101330e9f928dc80a3d3b9afefb373a","0x00000000000000000000000000000000001011627c159ab9f3ff0a0416a01df6","0x0000000000000000000000000000002ec420ad50087360c152c131400547bcc6","0x000000000000000000000000000000000018dab63316305864682bfe7b586e91","0x0000000000000000000000000000004bd9f352c132c7ae6bed5ea997693e6300","0x00000000000000000000000000000000001edb4d30542aa0ac4fe8eb31fc2ce0","0x0000000000000000000000000000008bcf42c24591e90cf41fc687829fe0b0aa","0x000000000000000000000000000000000027a49cd522a4fbbdfc8846331514de","0x000000000000000000000000000000bdfbf1d964fcfb887c3631ef202797fc2f","0x00000000000000000000000000000000001432caafa62e791082fd900fcb34a1","0x0000000000000000000000000000006f99a40f79f14ed78a291d53d0425ddc9d","0x000000000000000000000000000000000007ea92c2de0345ded1d25b237f0845","0x000000000000000000000000000000bc1328fa2c343da93cb98486d414f0a40a","0x0000000000000000000000000000000000255aeaa6894472e3cb6b0a790cf290","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000008775499e69e8bd2c39af33bd5fa0b4079a","0x0000000000000000000000000000000000024236bda126650fb5228cf424a087","0x000000000000000000000000000000b0eb1a867b06854066589b967455259b32","0x0000000000000000000000000000000000233cda9292be02cfa2da9d0fc7b0ea"] \ No newline at end of file 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 a18403eba718..000000000000 --- 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 = 393; -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/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index c7b70339e1d9..04af28550841 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -236,38 +236,14 @@ fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( } } -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())); - } +pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result { + if let Some(toml_path) = find_file_manifest(file_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 @@ -313,7 +289,7 @@ pub(crate) fn prepare_package<'file_manager, 'parsed_files>( package: &Package, ) -> (Context<'file_manager, 'parsed_files>, CrateId) { let (mut context, crate_id) = nargo::prepare_package(file_manager, parsed_files, package); - context.track_references(); + context.activate_lsp_mode(); (context, crate_id) } @@ -334,7 +310,7 @@ fn prepare_source(source: String, state: &mut LspState) -> (Context<'static, 'st let parsed_files = parse_diff(&file_manager, state); let mut context = Context::new(file_manager, parsed_files); - context.track_references(); + context.activate_lsp_mode(); let root_crate_id = prepare_crate(&mut context, file_name); diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index 24409e85db80..b8ff4fb371f2 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -37,15 +37,9 @@ pub(super) fn on_did_open_text_document( state.input_files.insert(params.text_document.uri.to_string(), params.text_document.text); let document_uri = params.text_document.uri; - let only_process_document_uri_package = false; let output_diagnostics = true; - match process_workspace_for_noir_document( - state, - document_uri, - only_process_document_uri_package, - output_diagnostics, - ) { + match process_workspace_for_noir_document(state, document_uri, output_diagnostics) { Ok(_) => { state.open_documents_count += 1; ControlFlow::Continue(()) @@ -62,15 +56,9 @@ pub(super) fn on_did_change_text_document( state.input_files.insert(params.text_document.uri.to_string(), text.clone()); let document_uri = params.text_document.uri; - let only_process_document_uri_package = true; - let output_diagnotics = false; + let output_diagnostics = false; - match process_workspace_for_noir_document( - state, - document_uri, - only_process_document_uri_package, - output_diagnotics, - ) { + match process_workspace_for_noir_document(state, document_uri, output_diagnostics) { Ok(_) => ControlFlow::Continue(()), Err(err) => ControlFlow::Break(Err(err)), } @@ -90,15 +78,9 @@ pub(super) fn on_did_close_text_document( } let document_uri = params.text_document.uri; - let only_process_document_uri_package = true; - let output_diagnotics = false; + let output_diagnostics = false; - match process_workspace_for_noir_document( - state, - document_uri, - only_process_document_uri_package, - output_diagnotics, - ) { + match process_workspace_for_noir_document(state, document_uri, output_diagnostics) { Ok(_) => ControlFlow::Continue(()), Err(err) => ControlFlow::Break(Err(err)), } @@ -109,15 +91,9 @@ pub(super) fn on_did_save_text_document( params: DidSaveTextDocumentParams, ) -> ControlFlow> { let document_uri = params.text_document.uri; - let only_process_document_uri_package = false; - let output_diagnotics = true; + let output_diagnostics = true; - match process_workspace_for_noir_document( - state, - document_uri, - only_process_document_uri_package, - output_diagnotics, - ) { + match process_workspace_for_noir_document(state, document_uri, output_diagnostics) { Ok(_) => ControlFlow::Continue(()), Err(err) => ControlFlow::Break(Err(err)), } @@ -129,17 +105,15 @@ pub(super) fn on_did_save_text_document( pub(crate) fn process_workspace_for_noir_document( state: &mut LspState, document_uri: lsp_types::Url, - only_process_document_uri_package: bool, output_diagnostics: bool, ) -> Result<(), async_lsp::Error> { let file_path = 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, &state.root_path).map_err(|lsp_error| { - ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) - })?; + let workspace = resolve_workspace_for_source_path(&file_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( @@ -155,10 +129,6 @@ pub(crate) fn process_workspace_for_noir_document( .flat_map(|package| -> Vec { let package_root_dir: String = package.root_dir.as_os_str().to_string_lossy().into(); - if only_process_document_uri_package && !file_path.starts_with(&package.root_dir) { - return vec![]; - } - let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); 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 51336a324da4..325392e150c8 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 @@ -63,8 +63,7 @@ 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(), &state.root_path).unwrap(); + let workspace = resolve_workspace_for_source_path(file_path.as_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") diff --git a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs index 67e2505d8fd2..20fdfb6ece72 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -427,7 +427,7 @@ impl<'a> DocumentSymbolCollector<'a> { return; }; - let name = name_path.last_segment(); + let name = name_path.last_ident(); let Some(name_location) = self.to_lsp_location(name.span()) else { return; diff --git a/noir/noir-repo/tooling/lsp/src/requests/hover.rs b/noir/noir-repo/tooling/lsp/src/requests/hover.rs index 161fd20f5559..40d4bb924487 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/hover.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/hover.rs @@ -23,39 +23,45 @@ pub(crate) fn on_hover_request( 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| { + args.interner.reference_at_location(args.location).and_then(|reference| { let location = args.interner.reference_location(reference); let lsp_location = to_lsp_location(args.files, location.file, location.span); - Hover { + format_reference(reference, &args).map(|formatted| Hover { range: lsp_location.map(|location| location.range), contents: HoverContents::Markup(MarkupContent { kind: MarkupKind::Markdown, - value: format_reference(reference, &args), + value: formatted, }), - } + }) }) }); future::ready(result) } -fn format_reference(reference: ReferenceId, args: &ProcessRequestCallbackArgs) -> String { +fn format_reference(reference: ReferenceId, args: &ProcessRequestCallbackArgs) -> Option { 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::Struct(id) => Some(format_struct(id, args)), + ReferenceId::StructMember(id, field_index) => { + Some(format_struct_member(id, field_index, args)) + } + ReferenceId::Trait(id) => Some(format_trait(id, args)), + ReferenceId::Global(id) => Some(format_global(id, args)), + ReferenceId::Function(id) => Some(format_function(id, args)), + ReferenceId::Alias(id) => Some(format_alias(id, args)), + ReferenceId::Local(id) => Some(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); +fn format_module(id: ModuleId, args: &ProcessRequestCallbackArgs) -> Option { + // Note: it's not clear why `try_module_attributes` might return None here, but it happens. + // This is a workaround to avoid panicking in that case (which brings the LSP server down). + // Cases where this happens are related to generated code, so once that stops happening + // this won't be an issue anymore. + let module_attributes = args.interner.try_module_attributes(&id)?; let mut string = String::new(); if format_parent_module_from_module_id( @@ -68,7 +74,7 @@ fn format_module(id: ModuleId, args: &ProcessRequestCallbackArgs) -> String { string.push_str(" "); string.push_str("mod "); string.push_str(&module_attributes.name); - string + Some(string) } fn format_struct(id: StructId, args: &ProcessRequestCallbackArgs) -> String { diff --git a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs index 2afa5fa44fdb..35ee36e11fac 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/inlay_hint.rs @@ -4,8 +4,8 @@ use std::future::{self, Future}; use async_lsp::ResponseError; use fm::{FileId, FileMap, PathString}; use lsp_types::{ - InlayHint, InlayHintKind, InlayHintLabel, InlayHintLabelPart, InlayHintParams, Position, - TextDocumentPositionParams, + InlayHint, InlayHintKind, InlayHintLabel, InlayHintLabelPart, InlayHintParams, Position, Range, + TextDocumentPositionParams, TextEdit, }; use noirc_errors::{Location, Span}; use noirc_frontend::{ @@ -173,7 +173,7 @@ impl<'a> InlayHintCollector<'a> { self.collect_in_expression(&assign_statement.expression); } StatementKind::For(for_loop_statement) => { - self.collect_in_ident(&for_loop_statement.identifier); + self.collect_in_ident(&for_loop_statement.identifier, false); self.collect_in_expression(&for_loop_statement.block); } StatementKind::Comptime(statement) => self.collect_in_statement(statement), @@ -251,7 +251,15 @@ impl<'a> InlayHintCollector<'a> { self.collect_in_expression(expression); } } - ExpressionKind::Lambda(lambda) => self.collect_in_expression(&lambda.body), + ExpressionKind::Lambda(lambda) => { + for (pattern, typ) in &lambda.parameters { + if matches!(typ.typ, UnresolvedTypeData::Unspecified) { + self.collect_in_pattern(pattern); + } + } + + self.collect_in_expression(&lambda.body); + } ExpressionKind::Parenthesized(parenthesized) => { self.collect_in_expression(parenthesized); } @@ -276,7 +284,7 @@ impl<'a> InlayHintCollector<'a> { match pattern { Pattern::Identifier(ident) => { - self.collect_in_ident(ident); + self.collect_in_ident(ident, true); } Pattern::Mutable(pattern, _span, _is_synthesized) => { self.collect_in_pattern(pattern); @@ -294,7 +302,7 @@ impl<'a> InlayHintCollector<'a> { } } - fn collect_in_ident(&mut self, ident: &Ident) { + fn collect_in_ident(&mut self, ident: &Ident, editable: bool) { if !self.options.type_hints.enabled { return; } @@ -308,17 +316,17 @@ impl<'a> InlayHintCollector<'a> { let global_info = self.interner.get_global(global_id); let definition_id = global_info.definition_id; let typ = self.interner.definition_type(definition_id); - self.push_type_hint(lsp_location, &typ); + self.push_type_hint(lsp_location, &typ, editable); } ReferenceId::Local(definition_id) => { let typ = self.interner.definition_type(definition_id); - self.push_type_hint(lsp_location, &typ); + self.push_type_hint(lsp_location, &typ, editable); } ReferenceId::StructMember(struct_id, field_index) => { let struct_type = self.interner.get_struct(struct_id); let struct_type = struct_type.borrow(); let (_field_name, field_type) = struct_type.field_at(field_index); - self.push_type_hint(lsp_location, field_type); + self.push_type_hint(lsp_location, field_type, false); } ReferenceId::Module(_) | ReferenceId::Struct(_) @@ -331,7 +339,7 @@ impl<'a> InlayHintCollector<'a> { } } - fn push_type_hint(&mut self, location: lsp_types::Location, typ: &Type) { + fn push_type_hint(&mut self, location: lsp_types::Location, typ: &Type, editable: bool) { let position = location.range.end; let mut parts = Vec::new(); @@ -342,7 +350,14 @@ impl<'a> InlayHintCollector<'a> { position, label: InlayHintLabel::LabelParts(parts), kind: Some(InlayHintKind::TYPE), - text_edits: None, + text_edits: if editable { + Some(vec![TextEdit { + range: Range { start: location.range.end, end: location.range.end }, + new_text: format!(": {}", typ), + }]) + } else { + None + }, tooltip: None, padding_left: None, padding_right: None, @@ -609,7 +624,7 @@ fn push_type_variable_parts( fn get_expression_name(expression: &Expression) -> Option { match &expression.kind { - ExpressionKind::Variable(path, _) => Some(path.last_segment().to_string()), + ExpressionKind::Variable(path) => Some(path.last_name().to_string()), ExpressionKind::Prefix(prefix) => get_expression_name(&prefix.rhs), ExpressionKind::MemberAccess(member_access) => Some(member_access.rhs.to_string()), ExpressionKind::Call(call) => get_expression_name(&call.func), @@ -756,8 +771,10 @@ mod inlay_hints_tests { let inlay_hints = get_inlay_hints(0, 3, type_hints()).await; assert_eq!(inlay_hints.len(), 1); + let position = Position { line: 1, character: 11 }; + let inlay_hint = &inlay_hints[0]; - assert_eq!(inlay_hint.position, Position { line: 1, character: 11 }); + assert_eq!(inlay_hint.position, position); if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label { assert_eq!(labels.len(), 2); @@ -770,6 +787,14 @@ mod inlay_hints_tests { } else { panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); } + + assert_eq!( + inlay_hint.text_edits, + Some(vec![TextEdit { + range: Range { start: position, end: position }, + new_text: ": Field".to_string(), + }]) + ); } #[test] @@ -777,8 +802,10 @@ mod inlay_hints_tests { let inlay_hints = get_inlay_hints(12, 15, type_hints()).await; assert_eq!(inlay_hints.len(), 1); + let position = Position { line: 13, character: 11 }; + let inlay_hint = &inlay_hints[0]; - assert_eq!(inlay_hint.position, Position { line: 13, character: 11 }); + assert_eq!(inlay_hint.position, position); if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label { assert_eq!(labels.len(), 2); @@ -798,6 +825,34 @@ mod inlay_hints_tests { } else { panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); } + + assert_eq!( + inlay_hint.text_edits, + Some(vec![TextEdit { + range: Range { start: position, end: position }, + new_text: ": Foo".to_string(), + }]) + ); + } + + #[test] + async fn test_type_inlay_hints_in_struct_member_pattern() { + let inlay_hints = get_inlay_hints(94, 96, type_hints()).await; + assert_eq!(inlay_hints.len(), 1); + + let inlay_hint = &inlay_hints[0]; + assert_eq!(inlay_hint.position, Position { line: 95, character: 24 }); + + if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label { + assert_eq!(labels.len(), 2); + assert_eq!(labels[0].value, ": "); + assert_eq!(labels[0].location, None); + assert_eq!(labels[1].value, "i32"); + } else { + panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); + } + + assert_eq!(inlay_hint.text_edits, None); } #[test] @@ -816,6 +871,8 @@ mod inlay_hints_tests { } else { panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); } + + assert_eq!(inlay_hint.text_edits, None); } #[test] @@ -823,8 +880,10 @@ mod inlay_hints_tests { let inlay_hints = get_inlay_hints(19, 21, type_hints()).await; assert_eq!(inlay_hints.len(), 1); + let position = Position { line: 20, character: 10 }; + let inlay_hint = &inlay_hints[0]; - assert_eq!(inlay_hint.position, Position { line: 20, character: 10 }); + assert_eq!(inlay_hint.position, position); if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label { assert_eq!(labels.len(), 2); @@ -834,6 +893,42 @@ mod inlay_hints_tests { } else { panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); } + + assert_eq!( + inlay_hint.text_edits, + Some(vec![TextEdit { + range: Range { start: position, end: position }, + new_text: ": Field".to_string(), + }]) + ); + } + + #[test] + async fn test_type_inlay_hints_in_lambda() { + let inlay_hints = get_inlay_hints(102, 105, type_hints()).await; + assert_eq!(inlay_hints.len(), 1); + + let position = Position { line: 104, character: 35 }; + + let inlay_hint = &inlay_hints[0]; + assert_eq!(inlay_hint.position, position); + + if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label { + assert_eq!(labels.len(), 2); + assert_eq!(labels[0].value, ": "); + assert_eq!(labels[0].location, None); + assert_eq!(labels[1].value, "i32"); + } else { + panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); + } + + assert_eq!( + inlay_hint.text_edits, + Some(vec![TextEdit { + range: Range { start: position, end: position }, + new_text: ": i32".to_string(), + }]) + ); } #[test] @@ -855,6 +950,7 @@ mod inlay_hints_tests { let inlay_hint = &inlay_hints[0]; assert_eq!(inlay_hint.position, Position { line: 25, character: 12 }); + assert_eq!(inlay_hint.text_edits, None); if let InlayHintLabel::String(label) = &inlay_hint.label { assert_eq!(label, "one: "); } else { @@ -863,6 +959,7 @@ mod inlay_hints_tests { let inlay_hint = &inlay_hints[1]; assert_eq!(inlay_hint.position, Position { line: 25, character: 15 }); + assert_eq!(inlay_hint.text_edits, None); if let InlayHintLabel::String(label) = &inlay_hint.label { assert_eq!(label, "two: "); } else { @@ -877,6 +974,7 @@ mod inlay_hints_tests { let inlay_hint = &inlay_hints[0]; assert_eq!(inlay_hint.position, Position { line: 38, character: 18 }); + assert_eq!(inlay_hint.text_edits, None); if let InlayHintLabel::String(label) = &inlay_hint.label { assert_eq!(label, "one: "); } else { diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 4d261c1b50a2..59ce91ea6815 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::{collections::HashMap, future::Future}; use crate::insert_all_files_for_workspace_into_file_manager; @@ -324,7 +325,12 @@ where let file_name = files.name(file_id).ok()?; let path = file_name.to_string(); - let uri = Url::from_file_path(path).ok()?; + + // `path` might be a relative path so we canonicalize it to get an absolute path + let path_buf = PathBuf::from(path); + let path_buf = path_buf.canonicalize().unwrap_or(path_buf); + + let uri = Url::from_file_path(path_buf.to_str()?).ok()?; Some(Location { uri, range }) } @@ -358,8 +364,7 @@ where ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") })?; - let workspace = - resolve_workspace_for_source_path(file_path.as_path(), &state.root_path).unwrap(); + let workspace = resolve_workspace_for_source_path(file_path.as_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") })?; diff --git a/noir/noir-repo/tooling/lsp/src/requests/references.rs b/noir/noir-repo/tooling/lsp/src/requests/references.rs index 375e0b69aedc..c720156659d0 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/references.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/references.rs @@ -94,6 +94,10 @@ mod references_tests { check_references_succeeds("rename_function", "another_function", 0, false).await; } + // Ignored because making this work slows down everything, so for now things will not work + // as ideally, but they'll be fast. + // See https://github.com/noir-lang/noir/issues/5460 + #[ignore] #[test] async fn test_on_references_request_works_accross_workspace_packages() { let (mut state, noir_text_document) = test_utils::init_lsp_server("workspace").await; @@ -108,13 +112,11 @@ mod references_tests { 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 - let only_process_document_uri_package = false; let output_diagnostics = true; notifications::process_workspace_for_noir_document( &mut state, one_lib.clone(), - only_process_document_uri_package, output_diagnostics, ) .unwrap(); diff --git a/noir/noir-repo/tooling/lsp/test_programs/inlay_hints/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/inlay_hints/src/main.nr index 2b53f8de339b..b2bbed2b1e5c 100644 --- a/noir/noir-repo/tooling/lsp/test_programs/inlay_hints/src/main.nr +++ b/noir/noir-repo/tooling/lsp/test_programs/inlay_hints/src/main.nr @@ -92,3 +92,15 @@ fn call_yet_another_function() { yet_another_function(some_name) // Should not show parameter names ("name" is a suffix of "some_name") } +fn struct_member_hint() { + let SomeStruct { one } = SomeStruct { one: 1 }; +} + +fn some_map(x: T, f: fn(T) -> U) -> U { + f(x) +} + +fn hint_on_lambda_parameter() { + let value: i32 = 1; + let _: i32 = some_map(value, |x| x + 1); +} diff --git a/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs b/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs index a2877ebdeac5..3e3560c91bfd 100644 --- a/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/noir/noir-repo/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -9,8 +9,8 @@ use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::file_manager_with_stdlib; use noirc_driver::NOIR_ARTIFACT_VERSION_STRING; +use noirc_driver::{file_manager_with_stdlib, DEFAULT_EXPRESSION_WIDTH}; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract}; use noirc_frontend::graph::CrateName; @@ -250,12 +250,6 @@ fn save_contract( } } -/// Default expression width used for Noir compilation. -/// The ACVM native type `ExpressionWidth` has its own default which should always be unbounded, -/// while we can sometimes expect the compilation target width to change. -/// Thus, we set it separately here rather than trying to alter the default derivation of the type. -const DEFAULT_EXPRESSION_WIDTH: ExpressionWidth = ExpressionWidth::Bounded { width: 4 }; - /// If a target width was not specified in the CLI we can safely override the default. pub(crate) fn get_target_width( package_default_width: Option, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs index 015644c15cb2..5673baf2893c 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs @@ -1,5 +1,6 @@ use noirc_frontend::ast::{ - ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, UnaryOp, UnresolvedType, + ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, Path, PathKind, UnaryOp, + UnresolvedType, }; use noirc_frontend::{macros_api::Span, token::Token}; @@ -161,12 +162,7 @@ pub(crate) fn rewrite( visitor.format_if(*if_expr) } - ExpressionKind::Variable(path, generics) => { - let path_string = visitor.slice(path.span); - - let turbofish = rewrite_turbofish(visitor, shape, generics); - format!("{path_string}{turbofish}") - } + ExpressionKind::Variable(path) => rewrite_path(visitor, shape, path), ExpressionKind::Lambda(_) => visitor.slice(span).to_string(), ExpressionKind::Quote(_) => visitor.slice(span).to_string(), ExpressionKind::Comptime(block, block_span) => { @@ -192,6 +188,25 @@ fn rewrite_block(visitor: &FmtVisitor, block: BlockExpression, span: Span) -> St visitor.finish() } +fn rewrite_path(visitor: &FmtVisitor, shape: Shape, path: Path) -> String { + let mut string = String::new(); + + if path.kind != PathKind::Plain { + string.push_str(&path.kind.to_string()); + string.push_str("::"); + } + + for (index, segment) in path.segments.iter().enumerate() { + if index > 0 { + string.push_str("::"); + } + string.push_str(&segment.ident.to_string()); + string.push_str(&rewrite_turbofish(visitor, shape, segment.generics.clone())); + } + + string +} + fn rewrite_turbofish( visitor: &FmtVisitor, shape: Shape, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/utils.rs b/noir/noir-repo/tooling/nargo_fmt/src/utils.rs index 020f411ae2fe..293351055e18 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/utils.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/utils.rs @@ -146,9 +146,10 @@ impl HasItem for Param { fn format(self, visitor: &FmtVisitor, shape: Shape) -> String { let pattern = visitor.slice(self.pattern.span()); let visibility = match self.visibility { - Visibility::Public => "pub", - Visibility::Private => "", - Visibility::DataBus => "call_data", + Visibility::Public => "pub".to_string(), + Visibility::Private => "".to_string(), + Visibility::CallData(x) => format!("call_data({x})"), + Visibility::ReturnData => "return_data".to_string(), }; if self.pattern.is_synthesized() || self.typ.is_synthesized() { diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs index 5aaaf20ff479..0c9f61a7d40b 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs @@ -120,8 +120,11 @@ impl super::FmtVisitor<'_> { let visibility = match func.def.return_visibility { Visibility::Public => "pub", - Visibility::DataBus => "return_data", + Visibility::ReturnData => "return_data", Visibility::Private => "", + Visibility::CallData(_) => { + unreachable!("call_data cannot be used for return value") + } }; result.push_str(&append_space_if_nonempty(visibility.into())); diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/expected/expr.nr b/noir/noir-repo/tooling/nargo_fmt/tests/expected/expr.nr index 03a26835ee3c..babaf5b356e5 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/expected/expr.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/expected/expr.nr @@ -129,9 +129,9 @@ fn return_if_expr() { } fn if_if() { - if cond { + (if cond { some(); } else { none(); - }.bar().baz(); + }).bar().baz(); } diff --git a/noir/noir-repo/tooling/nargo_fmt/tests/input/expr.nr b/noir/noir-repo/tooling/nargo_fmt/tests/input/expr.nr index b4edcbbed5f9..9ecefad7dfd8 100644 --- a/noir/noir-repo/tooling/nargo_fmt/tests/input/expr.nr +++ b/noir/noir-repo/tooling/nargo_fmt/tests/input/expr.nr @@ -147,7 +147,7 @@ fn return_if_expr() { } fn if_if() { -if cond { some(); } else { none(); } +(if cond { some(); } else { none(); }) .bar() .baz(); } \ No newline at end of file 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 7087052602ca..aeca5fe543fb 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.47.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 c07d2d8a4c1d..16fb26e55db1 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 f77e9f7e72ef..40d6ccc55e6e 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.47.1": + version: 0.47.1 + resolution: "@aztec/bb.js@npm:0.47.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: fa06d2ab58b2a23bacc578df7654f5c7eb90553229fc9730aaaf7479bc96b39f10f24a4f3a7eae8f73df3cdd8a3ffb07627cad61dff9896cabdb275ce5b6f09b languageName: node - linkType: soft + linkType: hard "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4160,7 +4161,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.47.1 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3